this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수로 this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.
단, this가 가리키는 값, 즉 this 바인딩은 함수 호출 방식에 의해 동적으로 결정된다.
바인딩이란?
- 식별자와 값을 연결하는 과정을 말한다.
- 변수선언은 변수 이름과 확보된 메모리 공간의 주소를 바인딩하는 것이다.
- this 바인딩은 this와 this가 가리킬 객체를 바인딩하는 것이다.
전역 실행 맥락에서 this는 엄격 모드 여부에 관계 없이 전역 객체를 참조한다.
// 웹 브라우저에서는 window 객체가 전역 객체
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b) // "MDN"
console.log(b) // "MDN"
함수 내부에서 this의 값은 함수를 호출한 방법에 의해 좌우된다.
기본값으로 브라우저에서는 window인 전역 객체를 참조한다.
function f1() {
return this;
}
// 브라우저
f1() === window; // true
반면에 엄격 모드에서 this 값은 실행 컨텍스트에 진입하며 설정되는 값을 유지하므로 this는 undefined로 남아있다.
function f2(){ "use strict"; // 엄격 모드 참고 return this; } f2() === undefined; // true
f.bind()를 호출하면 f와 같은 코드와 범위를 가졌지만 this는 원본 함수를 가진 새로운 함수를 생성한다. 새 함수의 this는 호출 방식과 상관없이 영구적으로 bind()의 첫 번째 매개변수로 고정된다.
function f() {
return this.a;
}
var g = f.bind({a: '바인드된값'});
console.log(g()); // 바인드된값
var h = g.bind({a: '바인드된값2'}); // bind는 한 번만 동작한다!
console.log(h()); // 바인드된값
var o = {a: 37, f: f, g: g, h: h};
console.log(o.a, o.f(), o.g(), o.h());
// 37, 37, 바인드된값, 바인드된값
화살표 함수 안에서의 this는 자신의 함수 상위에 있는 객체를 가리킨다. 전역 코드에서는 전역 객체를 가리킨다.
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
let user = {
name : "Oliver",
say(){
let arrow = () => {
console.log(this.name);
}
arrow();
}
};
user.say(); // Oliver
어떤 방법을 사용하든 foo의 this는 생성 시점의 것으로 설정된다. (위 예시에서는 global 객체) 다른 함수 내에서 생성된 화살표 함수에도 동일하게 적용된다. 즉, this는 싸여진 렉시컬 컨텍스트의 것으로 유지된다.
함수를 어떤 객체의 메서드로 호출하면 this의 값은 그 객체의 값을 가리킨다.
var myObj = {
prop: 37,
func: function() {
return this.prop;
}
};
console.log(myObj.func()); // 37
이 경우 함수가 정의된 방법이나 위치에 전혀 영향을 받지 않는다.
var myObj = {prop: 37};
function independent() {
return this.prop;
}
myObj.func = independent;
console.log(myObj.func()); // logs 37
myObj.b = {g: independent, prop: 42};
console.log(o.b.g()); // logs 42
이는 함수가 myObj의 메서드 func로 부터 호출 된 것만이 중요하다는 것을 보여준다.
마찬가지로, 객체 myObj.b의 메소드 g로서 호출할 때, 함수 내부의 this는 myObj.b를 나타낸다. 객체는 그 자신이 myObj의 프로퍼티 중 하나라는 사실은 중요하지 않다. 가장 즉각적인 참조가 중요한 것이다.
같은 개념으로 객체의 프로토타입 체인 어딘가에 정의한 메서드도 마찬가지다. 메서드가 어떤 객체의 프로토타입 체인 위에 존재하면, this의 값은 그 객체가 메서드를 가진 것 마냥 설정된다.
var obj = {
f:function() { return this.a + this.b; }
};
var p = Object.create(obj);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
이 예제에서, f 속성을 가지고 있지 않은 변수 p 가 할당된 객체는, 프로토타입으로 부터 상속받는다. 즉, f가 p 의 메소드로서 호출된 이후로 this 는 p 를 나타낸다.
다시 한 번 같은 개념으로, 함수를 접근자와 설정자에서 호출하더라도 동일하다.
접근자나 설정자로 사용하는 함수의 this는 접근하거나 설정하는 속성을 가진 객체로 묶인다.
function sum() {
return this.a + this.b + this.c;
}
var o = {
a: 1,
b: 2,
c: 3,
get average() {
return (this.a + this.b + this.c) / 3;
}
};
Object.defineProperty(o, 'sum', {
get: sum, enumerable: true, configurable: true});
console.log(o.average, o.sum); // 2, 6
함수를 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.target);
this.style.backgroundColor = '#A5D9F3';
}
for (var i = 0; i < 10; i++) {
elements[i].addEventListener('click', bluify);
}