[코어JS] 3. this


실행 컨텍스트가 활성화 될 때 디스바인딩을 함. 실행 컨텍스트는 함수가 호출될 때 생성되기 때문에, this는 함수가 호출될때 결정됨.

1. 전역공간에서

this 전역 객체를 가리킴. 브라우저에서는 window객체, 노드JS에서는 global객체.

전역 컨텍스트를 실행하는 주체가 전역객체이기 때문. 이 전역객체는 host객체 라고도 함. 런타임(자바스크립트 실행 환경)에 따라서 전역객체의 정보가 달라짐. window객체와 global객체는 ECMAScript에서 정의한 객체가 아니고, ECMAScript가 정의한 전역 객체에 맞게 런타임에서 제공하는 구현체임. 각 호스트 환경에서 정의한 바에 따라 전역객체의 구체적인 내용은 달라짐.

2. 함수호출시

함수를 호출했을 때 함수 내부에서도 this는 전역 객체를 가리킴.

function a() {
  console.log(this);
}

a(); // Window 객체 출력

다소 이상하지만, 함수를 호출하는 순간 함수를 실행하는 주체가 전역 객체기 때문에 이런 현상이 발생하는 것으로 이해할 수 있음.

function b() {
  function c() {
    console.log(this);
  }
  c(); // 여기에 집중(함수로써 호출)
}

b(); // Window 객체 출력

내부함수 안이더라도 호출할 당시의 주체를 가르키게 되어 전역 객체를 가리킴. 그냥 함수로써 호출했을 때 this는 전역객체임. ES6에서는 본 문제를 해결하기 위해 this바인딩을 하지 않는 arrow function이 탄생. arrow function은 바로 위 컨텍스트의 this를 그대로 가져와서 사용.

var d = {
  e: function() {
    function f() {
      console.log(this);
    }
    f(); // 여기에 집중(함수로써 호출)
  }
}

d.e(); // Window 객체 출력

메서드일지라도 함수로써 호출되면 this는 전역 객체임. 다음 챕터의 메서드로써 호출했을 때 this가 대상 객체를 가리키는 것과는 별개임.

3. 메서드호출시

메서드를 호출했을 때 this는 메서드를 호출한 주체를 가리킴. 메서드를 누가 호출했는지, 메서드명 앞의 점 앞의 내용이 this가 됨.

var a = {
  b: function() {
    console.log(this);
  }
}

a.b(); // a(객체) 출력
var a = {
  b: {
    c: function() {
      console.log(this);
    }
  }
}

a.b.c(); // a.b(c가 들어있는 객체) 출력


대괄호 표기법을 사용했을 때도 매서드 앞을 잘 보면 됨.

메서드 내부함수에서 우회법

2번에서 메서드 안에 내부함수가 있을 때 내부함수 내에서의 this가 전역 객체를 참조하는 현상을 우회하는 방법이 있음.

obj.b()에서 this는 obj를 뜻하고 this.a를 출력하면 20. 내부함수 c를 호출하면 this는 전역객체임. 그런데 this.a를 출력하면 window.a가 아닌 전역변수 a가 출력됨. JS의 특징. 전역 객체와 전역 변수가 별개의 개념이지 않음. 전역 변수가 전역 객체의 프로퍼티로 동작함.

그건 그렇고, 스코프체인을 사용한 우회법으로 내부함수 c의 this가 obj를 가르키도록 할 수 있음.

내부함수 상위에서 self라는 변수에 this를 저장하고 내부함수에서 self를 사용. 내부함수는 자신의 Lexical Environment에서 self를 찾고, 없으니 outerEnvironmentReference를 타고 외부(b함수의 LexicalEnvironment)에 있는 self를 찾아서 사용함. 이 self에는 this가 담겨있는데 이 때의 this는 obj임(obj.b호출할 때의 this). self나 _this, that과 같은 변수명이 사용되기도 함.

ES6에서는 this를 바인딩하지 않는 arrow function의 등장으로 본 우회법을 사용하지 않아도 됨. arrow function은 this를 바인딩 하지 않아 상위에 있는 this를 그대로 사용함(20출력).

ES5에서도 call이나 apply를 사용한 우회방법도 있으나, 첫 번째 우회방법을 사용한 소스들도 많이 보임.

4. callback 호출시

call, apply, bind(명시적인 this바인딩의 세가지 방법)


위 코드는 모두 {bb: "bbb"} 1 2 3을 출력함.

세 메서드 모두 첫 인자로 this바인딩을 함.

기본적으로 콜백함수 내부에서의 this는 전역객체임. 하지만 콜백 호출시에 this를 지정했다면 바뀔 수 있음.

콜백을 함수로 호출했기에 this는 전역객체임.

콜백함수를 넘겨받는 대상에서 this를 지정했다면 해당 값이 this가 됨. 위 코드에서는 obj.

setTimeout은 두번째 인자에 지정한 시간 뒤에 콜백함수(첫번쨰 인자)를 실행함. 위 코드의 경우 0.1초 뒤에 callback함수가 실행됨. setTimeout은 this를 별도로 지정하지 않는 함수임. 따라서 callback이 실행될 때 this는 전역객체임.

setTimeout이 콜백처리하는 방식을 임의로 바꿀 수 없기에, this를 지정하려면 bind매서드를 사용. bind가 없던 시절에는 함수를 한번 더 감싸서 self를 사용했었음.

여기까지의 개념으로만 보면 addEventListener메서드의 콜백함수도 내부에서 this가 window를 의미할 것 같지만, html dom 엘리먼트를 의미함. addEventListener라는 메서드가 콜백함수를 처리할 때 this는 이벤트가 발생한 그 타겟대상 엘리먼트로 하도록 정의되어있기 때문.

이러한 경우에도 bind메서드를 통해 this의 값을 지정해줄 수 있음.

콜백에서의 this 정리

  1. 기본적으로 함수의 this와 같음
  2. 콜백의 this를 지정해둔 함수도 있음
  3. 2의 경우에도 this바인딩을 하면 그에 따름

5. 생성자함수 호출시

new연산자를 사용한 경우. new연산자는 생성자 함수의 내용을 바탕으로 인스턴스 객체를 만드는 명령. 이때는 새로 만들 인스턴스 객체가 this가 됨.

위 코드의 경우, new연산자 없이 Person함수를 호출함. Person이 함수로서 호출됐기 때문에 this는 전역객체를 가리키게 됨. roy에는 아무 값도 담기지 않고, 전역객체의 프로퍼티에 값이 저장됨.

new를 사용하여 호출(생성사함수 호출)하게 되면 roy라는 변수 자체가 this가 됨(새로 생성될 Person의 인스턴스 객체). 객체가 새로 생성되며 name, age프로퍼티가 생성되고 값이 저장됨.

정리

좋은 웹페이지 즐겨찾기