코딩쌀롱

[코어 자바스크립트] 3-1. 상황에 따라 달라지는 this 본문

[코어 자바스크립트] 3-1. 상황에 따라 달라지는 this

이브✱ 2020. 12. 21. 22:46

 

전역 공간에서의 this

전역 공간에서 this는 전역 객체를 가리킨다. 전역 컨텍스트를 생성하는 주체가 전역 객체이기 때문.

브라우저에서 전역 객체는 'window', Node.js에서는 'global'이다.

 

<전역 공간에서만 발생하는 특이한 성질>

전역 변수를 선언하면 자바스크립트 엔진은 전역 객체의 프로퍼티로 할당한다. 변수이면서 객체의 프로퍼티인 것.

let a = 1;
console.log(a); // 1
console.log(window.a); // 1
console.log(this.a); // 1

자바스크립트의 모든 변수는 특정 객체의 프로퍼티로서 동작한다.

[변수 선언] 실행컨텍스트: 변수 수집 → Lexical Environment 프로퍼티로 저장

[변수 호출] 실행컨텍스트: L.E 조회 → 프로퍼티 값 반환

 

전역 공간에서 var로 변수 선언 ≒ window 프로퍼티에 직접 할당 (대부분의 경우 같다)

전역 변수 선언과 전역 객체의 프로퍼티 할당 사이에 전혀 다른 경우도 있는데 '삭제' 명령에 대해 그렇다.

전역 변수로 선언하면 전역 객체의 프로퍼티로 할당하지만 삭제는 불가능하다.

전역 객체의 프로퍼티로 할당하면 삭제도 가능하다.

사용자가 의도치 않게 삭제하는 것을 방지하는 차원에서 생긴 방어 전략이라고 해석된다.

// ①전역변수 선언
var a = 1;
delete window.a;                  // false
console.log(a, window.a, this.a); // 1 1 1

var b = 2;
delete b;                         // false
console.log(b, window.b, this.b); // 2 2 2

// ②전역객체의 프로퍼티 할당
window.c = 3;
delete window.c;                  // true
console.log(c, window.c, this.c); // 3 3 3

window.d = 4;
delete d;                         // true
console.log(d, window.d, this.d); // 4 4 4

 

메서드로서 호출할 때 그 메서드 내부에서의 this

함수와 메서드의 차이는 독립성에 있다. 함수는 그 자체로 독립적 기능을 수행하고, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행한다. 어떤 함수를 객체의 프로퍼티에 할당한다고 해서 무조건 메서드인 것은 아니다. 객체의 메서드로서 호출할 경우에만 메서드로 동작하고, 그렇지 않으면 함수로 동작한다.

let func = function (x) {
   console.log(this, x);
};
func(1);       // Window {...} 1 ←함수로 동작

let obj = {
   method: func
}
obj.method(2); // { method: f } 2 ←메서드로 동작

위의 예시를 보면, 원래의 익명함수는 그대로인데, 변수에 담아 호출한 경우와 obj 객체의 프로퍼티에 할당해서 호출한 경우에 this가 달라진다. this에는 호출한 주체에 대한 정보가 담긴다. 메서드로서 호출하는 경우 호출 주체는 함수명 앞의 객체다.

 

함수로서 호출할 때 그 함수 내부에서의 this

어떤 함수를 함수로서 호출할 경우에는 this가 지정되지 않는다.  함수로서 호출하는 것은 호출 주체를 명시하지 않고 개발자가 실행한 것이기 때문에 호출 주체의 정보를 알 수 없기 때문이다. this가 지정되지 않은 경우 즉, 함수로 호출할 경우 this는 전역 객체를 가리킨다.

 

메서드의 내부함수에서의 this는 혼란을 느끼기 쉽다. 메서드의 내부함수에서 함수로서 호출했는지 메서드로서 호출했는지 파악하면 this의 값을 맞출 수 있다.

let obj = {
   outer: function() {
      console.log(this);  // (1) obj1
      
      let innerFunc = function () {
         console.log(this);
      }
      innerFunc();        // (2) Window
      
      let obj2 = {
         innerMethod: innerFunc
      }; 
      obj2.innerMethod(); // (3) obj2
   }
};
obj.outer();

(1) 마지막 줄에서 obj.outer()를 호출하면서 함수의 실행 컨텍스트가 생성된다. (호이스팅, 스코프 체인 정보 수집, this 바인딩 수행) 함수명 outer앞에 점이 있으므로 메서드로서 호출한 것. 따라서 점 앞의 객체인 obj가 바인딩된다.

(2) innerFunc()를 호출하면서 실행 컨텍스트가 생성된다. (호이스팅, 스코프 체인 정보 수집, this 바인딩 수행) 함수명 앞에 점이 없으므로 함수로서 호출한 것. 따라서 this가 지정되지 않았고, 스코프 체인상의 최상위 객체인 Window가 바인딩된다.

객체 내부에서 함수를 호출했음에도 this가 window로 바인딩되는 것을 '설계상의 오류'로 보기도 한다.(더글라스 크락포드)

(3) obj2.innerMethod()를 호출하면서 실행 컨텍스트 생성된다. 함수명 앞에 점이 있으므로 메서드로서 호출한 것. this에는 점 앞의 객체인 obj2가 바인딩된다.

 

this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지)은 중요하지 않다.

해당 함수를 호출하는 구문 앞에 점 또는 대괄호가 있는지 없는지가 관건이다!

 

<변수를 활용해 this 우회하기>

let obj = {
   outer: function () {
      console.log(this);             // (1) { outer: f }

      let innerFunc1 = function() {
         console.log(this);          // (2) Window
      };
      innerFunc1();
      
      let self = this;  // 상위 스코프의 this 저장, 내부함수에서 활용
      let innerFunc2 = function () {
         console.log(self);          // (3) { outer: f }
      }; 
      innerFunc2();
   }
};
obj.outer();

 

<this를 바인딩하지 않는 함수>

 → 화살표 함수(ES6)

화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 활용한다.

 

콜백 함수 호출 시 그 함수 내부에서의 this

콜백 함수의 제어권을 가지는 함수(메서드)가 콜백 함수에서의 this를 무엇으로 할지를 결정하며, 특별히 정의하지 않은 경우에는 기본적으로 함수와 마찬가지로 전역객체를 가리킨다.

setTimeout(function () { console.log(this); }, 300);      // (1) Window

[1, 2, 3, 4].forEach(function (x) {
   console.log(this, x);                                  // (2) Window
});

document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a')
   .addEventListener('click', function (e) {
      console.log(this, e);                               // (3) #a 요소
   });

(1) setTimeout 함수는 콜백 함수 호출 시 this 지정X → this는 전역객체 참조

(2) forEach 메서드는 콜백 함수 호출 시 this 지정X → this는 전역객체 참조

(3) addEventListener 메서드는 콜백 함수 호출 시 자신의 this를 상속한다. (메서드명의 점 앞부분이 this)

 

생성자 함수 내부에서의 this

어떤 함수가 생성자 함수로서 호출된 경우 this는 새로 만들 인스턴스 자신이 된다.

생성자 함수를 호출하면 우선 생성자의 prototype 프로퍼티를 참조하는 __proto__라는 프로퍼티가 있는 객체(인스턴스)를 만들고, 공통 속성 및 개성을 해당 객체(this)에 부여한다.

 

 

 


정재남 작가님의 〔 코어 자바스크립트 〕 책을 공부하며 정리했습니다.

Comments