먼저 this 키워드의 정의에 대해 알아보고 시작하자. this의 정의는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-referencing variable)이다. 이 키워드를 통해 자시이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다. 그렇다면 이것이 왜 필요한지부터 생각해보자.
function Circle(radius) {
????.radius = radius;
}
Circle.prototype.getDiameter j= function () {
return 2 * ????.radius;
};
이 상황에서, 생성자 함수 내부에서는 자신이 생성할 인스턴스가 무엇일지 알 수 없다. 그렇기 때문에 그것을 가리킬 키워드가 필요한데 그것이 this인 셈이다. 이것은 자바스크립트 엔진에 의해 암묵적으로 생성되며, 코드의 어디서든 참조할 수 있다. 그리고 this가 가리키는 값은 함수 호출 방식에 의해 동적으로 결정된다. 함수 호출 시에 arguments 객체와 this가 암묵적으로 함수 내부에 전달되는 것이다. 그것을 이용해 위의 예제를 수정해보자.
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.getDiameter j= function () {
return 2 * this.radius;
};
//this는 생성자 함수가 생성할 인스턴스를 가리키게 된다.
여기서 참고해야 할 부분은 JS에서의 this 바인딩은 동적으로 결정된다는 점이다. 전역에선 window, 일반 함수 내부에서도 전역 객체 window, 메서드 내부에서는 그 메서드를 호출한 객체, 생성자 함수 내부에서는 생성할 인스턴스를 가리킨다. 그리고 렉시컬 스코프와 달리 this 바인딩은 함수가 호출되는 시점에 결정된다는 것도 중요하다.
여기서 조금 문제되는 부분이 생길 수 있다. 일반 함수로 호출된 모든 함수(중첩, 콜백함수 포함) 내부의 this에는 전역 객체가 바인딩된다. 예제로 살펴보자.
var value = 1; //전역 객체의 프로퍼티
const obj = {
value:100,
foo() {
console.log("foo's this : ",this); //{value:100,foo:f}
setTimeout(function (){
console.log("callback's this: ", this); //window
console.log("callback's this.value: ", this); //1
},100);
}
};
obj.foo();
이 예제에서 function이 obj 내부의 foo 함수 내부에 있지만 콜백함수로 호출되었기 때문에 전역 객체를 참조한다. 이는 의도와 다르게 작동될 수 있는 부분이므로 bind 메서드로 간접 호출을 하거나 화살표 함수를 사용하면 그 내부의 this는 상위 스코프의 this를 가리키게 된다.
bind 메서드도 예제로 같이 살펴보자. apply, call, bind는 모두 Function.prototype의 메서드인데, 먼저 apply와 call 메서드는 this로 사용할 객체와 인수 리스트를 인수로 전달받아 함수를 호출해주는 역할을 한다.
function getThisBinding() {
console.log(arguments);
return this;
}
const thisArg = {a:1};
console.log(getThisBinding.apply(thisArg,[1,2,3]));
console.log(getThisBinding.call(thisArg,1,2,3));
// 두 경우 모두 출력은
Arguments(3) [1,2,3, callee:f, Symbol(Symbol.iterator):f]
{a:1} < return되는 값
이 경우 두 메서드 공통으로 getThisBinding에 첫 번째 인수 thisArg를 this에 바인딩해서 호출한다. 여기서 두 번째 인수로 전달한 것을 getThisBinding의 인수로 전달해주는 형태이다.
apply은 배열로 묶어서 전달, call은 쉼표로 구분해서 전달하는 차이가 있다. 주로 arguments같은 유사 배열 객체에 배열 메서드를 사용하기 위해 사용한다. Array.prototype.slice.call(arguments); 처럼 객체를 배열로 변환하는 데에 유용하다.
bind 메서드는 함수를 호출하지는 않고 this 바인딩이 교체된 함수를 새롭게 생성해 반환해준다.
function getThisBinding() {
return this;
}
const thisArg = {a:1};
console.log(getThisBinding.bind(thisArg)); //getThisBinding
console.log(getThisBinding.bind(thisArg)()); //{a:1}
호출하지는 않으므로 다른 결과를 원한다면 명시적으로 호출 할 필요가 있다.
이 메서드는 주로 콜백 함수의 this 일치 문제를 해결 하는 데에 도움이 된다.
const person = {
name : 'Kim',
foo(callback) {
setTimeout(callback.bind(this),100);
}
};
person.foo(functino () {
console.log(`Hi! my name is ${this.name}.`); // Kim이 들어간다.
});