객체의 메서드가 this를 잃어버리게 될 때가 있습니다.
예를 들어 setTimeout의 콜백 함수로 객체 메서드를 사용할 때가 그렇습니다.
const something = {
name = "john";
sayHi() {
alert(`Hello, ${this.name }!`);
}
}
setTimeout(something.sayHi(), 1000); // Hello, undefined!
이는 sayHi 함수를 호출하는 컨텍스트가 변경되었기 때문인데요, setTimeout의 콜백으로 들어간 sayHi의 this가 window(node 환경일 경우 timer 객체)로 변경되기 때문입니다.
외부 렉시컬 환경 참조고 뭐고 할 말이 많아질 수도 있지만, 오늘은 어떻게 잃어버린 this를 되찾을 수 있을지에 대해 알아보겠습니다.
하나의 방법은 래퍼 함수를 사용하는 것입니다.
setTimeout(() => something.sayHi(), 1000); // Hello, john!
래퍼 함수를 통해 객체의 메서드를 호출할 경우, 외부 렉시컬 환경이 기존 객체로 고정되어 사용되므로 위에서 발생했던 문제를 해결할 수 있게 됩니다.
그러나 이 방법의 단점이 존재하는데, 이벤트 루프에 의해 setTimeout의 콜백 함수가 실행되기 전에 somthing의 내용이 변경되면, 변경된 객체의 메서드를 호출하게 된다는 것입니다. something 객체를 참조하고 있기 때문이지요.
bind 함수를 통해 래퍼 함수 사용 시 발생했던 문제를 해결할 수 있습니다. bind 함수를 사용하면, 함수처럼 호출 가능한 ‘특수 객체(exotic object)’ 를 반환 받게 됩니다. 이 객체는 실행 당시의 this를 고정하므로, 바인딩한 객체가 변하더라도 영향을 받지 않습니다.
let sayHi = something.sayHi.bind(something);
setTimeout(sayHi, 1000);
상황에 따라 객체 메서드의 this가 변경되어 실행될 때가 있습니다.
간단하게는 래퍼 함수를 통해서 해결할 수 있지만, bind 함수를 이용하여 사이드 이펙트 없는 this 바인딩을 만들어내는 것이 적합해 보입니다.