this

JavaScript

this?

대부분의 객체지향 언어에서 this는 클래스로 생성한 인스턴스 객체를 의미한다. 클래스에서만 사용할 수 잇기 때문에 혼란의 여지가 없거나 많지 않다. 그러나 자바스크립트에서의 this는 어디서든 사용할 수 있다. 함수와 객체(메서드)의 구분이 느슨한 자바스크립트에서 this는 실질적으로 이 둘을 구분하는 거의 유일한 기능이다.

this는 상황에 따라 달라진다

자바스크립트에서 this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다. 바꿔 말하면 this는 함수를 호출할 때 결정된다고 할 수 있다. 함수를 어떤 방식으로 호출하느냐에 따라 값이 달라지는 것이다.

전역 공간에서의 this

전역 공간에서 this는 전역 객체를 가리킨다. 개념상 전역 컨텍스트를 생성하는 주체가 바로 전역 객체이기 때문이다. 전역 객체는 자바스크립트 런타임 환경에 따라 다른 이름과 정보를 가지고 있다.

console.log(this); // {alert: f(), atob: f(), blur: f(), btoa: f(), ...}
console.log(window); // {alert: f(), atob: f(), blur: f(), btoa: f(), ...}
console.log(this === window); // true

브라우저 환경에서의 this

console.log(this); // {process: {title: 'node', version: 'v10.13.0', ...}}
console.log(global); // {process: {title: 'node', version: 'v10.13.0', ...}}
console.log(this === global); // true

Node.js 환경에서의 this

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

함수 vs. 메서드?

어떤 함수를 실행하는 방법에는 여러 가지가 있는데, 가장 일반적인 방법 두 가지는 함수로서 호출하는 경우메서드로서 호출하는 경우이다. 이 둘을 구분하는 차이는 독립성에 있다.

  • 함수는 그 자체로 독립적인 기능을 수행하고,
  • 메서드는 자신을 호출한 대상 객체에 대한 동작을 수행한다.

어떤 함수를 객체의 프로퍼티에 할당한다고 해서 그 자체로 무조건 메서드가 되는 게 아니라 객체의 메서드로 호출할 경우에만 메서드로 동작하고, 그렇지 않으면 함수로 동작한다.

var func = function(x){
  console.log(this, x);
}
func(1); // Window {...} 1

var obj = {
  method : func
};
obj.method(2); // {method : f} 2

이처럼 원래의 익명함수는 그대로인데 이를 변수에 담아 호출한 경우와 obj 객체의 프로퍼티에 할당해서 호출한 경우에 this가 달라지게 된다.

함수 호출 / 메서드 호출의 구분?

메서드 호출은 객체를 명시한다.

  • 함수 앞에 .이 있다. (i.e. obj.method(1))
  • 대괄호 표기법을 사용한다. (i.e. obj['method'](1))

그 외의 경우는 함수로 호출한 것이다.

이제, 메서드 내부에서의 this호출을 알아보자.

var obj = {
  methodA : function() {console.log(this);},
  inner : {
    methodB : function() {console.log(this);}
  }
};

obj.methodA(); // {methodA: f, inner: {...}}
obj['methodA'](); // {methodA: f, inner: {...}}

obj.inner.methodB(); // {methodB: f} 
obj.inner['methodB'](); // {methodB: f} 
obj['inner']['methodB'](); // {methodB: f} 

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

함수 내부에서의 this

어떤 함수를 함수로서 호출할 경우에는 this가 지정되지 않는다. this에는 호출한 ‘주체’에 대한 정보가 담기는데, 함수로서 호출하는 것은 호출 주체(객체)를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이기 때문에 호출 주체의 정보를 알 수 없는 것이다.

메서드 내부함수에서의 this

내부함수 역시 이를 함수로서 호출했는지 메서드로서 호출했는지 파악하면 this의 값을 알 수 있다.

var obj1 = {
  outer: function(){
    console.log(this); // (1)
    var innerFunc = function(){
      console.log(this); // (2)(3)
    }
    innerFunc();

    var obj2 = {
      innerMethod : innerFunc
    };
    obj2.innerMethod();
  }
};

obj1.outer();

(1) : obj1, (2) : 전역객체(Window), (3) : obj2

7번째 줄에서 outer 메서드 내부에 있는 함수(innerFunc)를 함수로서 호출했다. 반면 12번째 줄에서는 같은 함수(innerFunc)를 메서드로서 호출했다. 같은 함수임에도 7번째, 12번째줄에 의해 바인딩되는 this의 대상이 서로 달라진 것이다.

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

함수 A의 제어권을 다른 함수(또는 메서드) B에게 넘겨주는 경우 함수 A를 콜백 함수라고 한다. 이때 함수 A는 함수 B의 내부 로직에 따라 실행되며, this역시 함수 B 내부 로직에서 정한 규칙에 따라 값이 결정된다.

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

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

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

(1) : 전역객체(Window), (2) : 전역객체(Window), (3) : document.body.querySelector(‘#a’)

(1)의 setTimeout 함수와 (2)의 forEach 메서드는 그 내부에서 콜백 함수를 호출할 때 대상이 되는 this를 지정하지 않는다. 따라서 콜백 함수 내부에서의 this는 전역객체를 참조한다. (3)의 addEventListener 메서드는 콜백 함수를 호출할 때 자신의 this를 상속하도록 정의되어 있으므로, 메소드명의 . 앞부분이 this가 된다.

생성자 함수 내부에서의 this

생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수다. 객체지향 언어에서는 생성자를 class, 클래스를 통해 만든 객체를 instance라고 한다.

자바스크립트는 함수에 생성자로서의 역할을 함께 부여했다. new 명령어와 함께 함수를 호출하면 해당 함수가 생성자로서 동작하게 된다. 그리고 어떤 함수가 생성자 함수로서 호출된 경우 내부에서의 this는 새로 만들 구체적인 인스턴스 자신이 된다.

var Cat = function(name, age){
  this.cry = 'meow';
  this.name = name;
  this.age = age;
};

var choco = new Cat('초코', 7);
var nabi = new Cat('나비', 3);
console.log(choco, nabi);

Cat {cry : ‘meow’, name : ‘초코’, age : 7}

Cat {cry : ‘meow’, name : ‘나비’, age : 3}

6번째 줄에서 실행한 생성자 함수 내부에서의 thischoco 인스턴스를, 7번째 줄에서의 thisnabi 인스턴스를 가리킨다.


출처
코어 자바스크립트 (정재남)