this는 객체(Object)를 참조하는 키워드 라고 할 수 있다. 대체 어떤 객체를 참조한다는 말일까? MDN에 따르면, 대부분의 경우 this가 참조하는 객체는 함수를 호출한 방법에 의해 결정된다. 상황에 따른 this의 값을 알아보자.
전역 실행 문맥(global execution context)에서, this는 strict 모드에 관계없이 전역 객체(window)를 반환한다.
console.log(this); // Window {...}
console.log(window); // Window {...}
a = 42;
console.log(window.a); // 42
this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"
console.log(this === window); // true
함수 내부에서 this는 strict 모드일 때는 undefined, strict 모드가 아닐 경우에는 window를 반환한다.
/*strict 모드*/
function strMode(){
'use strict';
console.log(this); // undefined
}
function noStrMode() {
console.log(this); // Window {...}
}
함수를 객체의 메소드로 호출하면 this의 값은 그 객체를 사용한다.
var o = {
prop: 37,
f: function() {
console.log(this); // {prop:37, f: f}
return this.prop;
}
};
console.log(o.f()); // 37
함수를 호출한 방법에 따라 this의 값이 결정된다는 것에 유의해야 한다. 예를 들어 아래 코드는 함수를 먼저 정의한 다음 o.f를 추가했지만, 똑같이 37을 출력한다.
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // 37
그럼 아래 코드는 어떨까?
function independent() {
return this.prop;
}
var o = {prop: 37};
o.b = {g: independent, prop: 42};
console.log(o.b.g());
g는 o.b의 메소드이므로, 이 경우 42가 출력되며, this는 o.b를 나타낸다.
화살표 함수에서 this는 호출되는 시점하고 무관하게 선언되는 시점에 결정되며, 언제나 상위 스코프의 this를 가리킨다. 이를 Lexical this라고 한다.(Lexical scoping과 유사하다)
아래 코드의 결과를 예상해 보자.
var obj = {
myName: '철수',
logName: function() {
console.log(this.myName);
}
};
obj.logName();
this는 obj를 반환할 테니, "철수" 가 출력될 것이라 어렵지 않게 알 수 있다. 그러면 아래는 어떨까?
var obj = {
myName: '철수',
logName: () => {
console.log(this.myName);
}
};
obj.logName();
이 경우 undefined가 출력된다. obj의 상위 스코프는 전역 스코프(window)이므로, window.myName을 출력하려 시도하기 때문이다.
아래의 코드는 어떨까?
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
var fn = obj.bar();
console.log(fn() === obj);
obj.bar에 할당된 함수(=함수 A)는 화살표 함수로 생성된 다른 함수(=함수 B)를 반환한다. 함수 B의 this는 함수 A의 this, 즉 obj로 설정되기에 "true" 가 출력된다.
함수를 new 키워드와 함께 생성자로 사용하면, this는 새로 생긴 객체가 된다.
function C() {
this.a = 37;
}
var o = new C();
console.log(o.a); // 37
function C2() {
this.a = 37;
return {a: 38};
}
o = new C2();
console.log(o.a); // 38
이벤트 핸들러로 사용된 함수 안의 this는 이벤트를 유발한 요소로 설정된다.
function bluify(e) {
console.log(this === e.currentTarget); // true
console.log(this === e.target); // e.currentTarget === target일시 true
this.style.backgroundColor = '#A5D9F3';
}
var elements = document.getElementsByTagName('*');
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', bluify, false);
}
함수 메소드인 call,apply,bind를 사용하면, 함수를 호출한 방법에 무관하게 this에 원하는 값을 지정할 수 있다.
call과 apply는 this로 사용할 객체와 함수 호출에 필요한 인수들을 받아, 함수가 주어진 객체의 메소드인 것처럼 사용할 수 있다.
call은 인수를 직접 받으며, apply는 인수를 배열 형태로 받는다.
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {a: 1, b: 3};
//o는 this로 사용할 객체를 말한다.
add.call(o, 5, 7); // 16
add.apply(o, [10, 20]); // 34
bind를 사용하면 원본 함수와 같은 코드, 같은 범위를 가졌지만 this는 다른 새로운 함수를 생성한다. 이 때, 새로운 함수의 this는 호출 방식과 상관없이 영구적으로 bind()의 첫 번째 매개변수로 고정된다. apply, call, 심지어는 bind를 다시 쓴다 해도 this는 변하지 않는다!
function f() {
return this.a;
}
console.log(f()); // undefined
var g = f.bind({a: 'azerty'});
console.log(g()); // azerty
var h = g.bind({a: 'yoo'}); // bind는 한 번만 동작함!
console.log(h()); // azerty
var o = {a: 37, f: f, g: g, h: h};
console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, azerty, azerty
참고: