this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수다.
this가 가리키는 값은 함수 호출 방식에 의해 동적으로 결정된다.
함수 호출은 다음과 같이 이루어질 수 있다.
- 일반함수 호출
- 메서드 호출
- 생성자 함수 호출
- apply/call/bind 메서드에 의한 호출
일반 함수로 호출된 모든 함수 내부의 this에는 전역객체가 바인딩 된다.
function f() {//함수
console.log(this);//window
}
function f2() {//중첩함수
function inner() {
console.log(this);//window
}
inner();
}
function f3() {//콜백함수
setTimeout(() => {
console.log(this);//window
}, 1000);
}
f();
f2();
f3();
일반 함수로 호출 시 this에 전역객체가 바인딩 되기 때문에 의도한 결과가 나오지 않을 수 있다.
const obj = {
value: 10,
func: function () {
setTimeout(function () {
console.log(this.value);//undefined
}, 1000);
},
};
obj.func();
10을 출력하고 싶었지만, this가 전역객체를 가리키고 있기 때문에 해당 값이 존재하지 않는다.
문제를 해결해보자.
const obj = {
value: 10,
func: function () {
const bind_this = this;
setTimeout(function () {
console.log(bind_this.value);//10
}, 1000);
},
};
obj.func();
뒤에서 다시 살펴보겠지만 func은 메서드이기 때문에 this에는 obj가 바인딩 된다. 이 값을 저장하고 콜백함수에서 사용한다.
우선 bind만 간략하게 살펴보자.
const obj = {
value: 10,
func: function () {
setTimeout(
function () {
console.log(this.value);//10
}.bind(this),
1000
);
},
};
obj.func();
위 예제를 이해했다면 이것도 쉽게 이해할 수 있다. bind 함수를 사용해서 func함수의 this를 콜백함수의 this와 연결한다.
가장 간단한 방법이다. function 키워드 대신 화살표 함수를 사용한다.
const obj = {
value: 10,
func: function () {
setTimeout(() => {
console.log(this.value);//10
}, 1000);
},
};
obj.func();
메서드 내부의 this에는 메서드를 호출한 객체가 바인딩 된다.
const obj = {
value: 10,
func: function () {
console.log(this); //{value: 10, func: ƒ}
},
};
obj.func();
생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩된다.
function Circle(radius) {
this.radius = radius;
this.getRadius = function () {
console.log(this.radius);//2
};
}
const c1 = new Circle(2);
c1.getRadius();
apply,call,bind 메서드는 Function.prototype의 메서드이다.
apply와 call 메서드의 본질적인 기능은 함수를 호출하는 것이다. apply와 call메서드는 함수를 호출하면서 첫 번째 인수로 전달한 객체를 호출한 함수의 this에 바인딩한다.
반면 bind는 메서드를 호출하지는 않는다.
function bindPrint() {
console.log(arguments);
console.log(this.value);
}
const applyObj = {
value: 1,
};
const callObj = {
value: 10,
};
const bindObj = {
value: 100,
}
bindPrint.apply(applyObj, [1, 2, 3]);
//[Arguments] { '0': 1, '1': 2, '2': 3 }
//1
bindPrint.call(callObj, [1, 2, 3]);
//[Arguments] { '0': [ 1, 2, 3 ] }
//10
bindPrint.bind(bindObj)();
//[Arguments] {}
//100