this
JavaScriptthis?
대부분의 객체지향 언어에서 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번째 줄에서 실행한 생성자 함수 내부에서의 this
는 choco
인스턴스를, 7번째 줄에서의 this
는 nabi
인스턴스를 가리킨다.