2024년 12월 24일
this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이다.
this 바인딩은 this와 this가 가리킬 객체를 바인딩하는 것으로, 함수 호출 방식에 의해 동적으로 결정된다. (= this 바인딩은 함수 호출 시점에 결정된다)
함수 호출 방식에 따라 this 바인딩이 어떻게 결정되는지 알아보자.
✏️ 일반적으로 this에는 전역 객체가 바인딩된다.
function foo() {
console.log(this); // window(전역 객체)
}
foo();
✏️ 중첩 함수 또한 일반 함수로 호출하면 전역 객체가 바인딩된다.
function foo() {
console.log(this); // window
function bar() {
console.log(this); // window
}
bar();
}
foo();
✏️ var 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티이다. (const 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티가 아니다.)
✏️ 콜백 함수도 일반 함수로 호출되면 내부 this에 전역 객체가 바인딩된다.
-> 아래 예시에서 콜백함수 내부의 this.value는 전역 객체의 value 프로퍼티, 즉 1이 된다.
var value = 1;
// const value = 1;
const obj = {
value: 100,
foo() {
console.log(this); // {value: 100, foo: f}
console.log(this.value); // 100
setTimeout(function () {
console.log(this); // window
console.log(this.value); //1
}, 100);
},
};
obj.foo();
=> 💡 일반 함수로 호출된 모든 함수 내부의 this에는 전역 객체가 바인딩 된다 !
하지만, 메서드 내에서 정의한 중첩함수/콜백 함수의 this가 전역 객체를 바인딩하는 것은 문제가 있다. 다음은 메서드 내부의 중첩 함수나 콜백 함수의 this 바인딩을 메서드의 this 바인딩과 일치시키기 위한 방법이다.
a. this 바인딩을 다른 변수에 할당하여, 콜백 함수 내부에서 this 대신 해당 변수를 참조하도록 한다.
var value = 1;
const obj = {
value: 100,
foo() {
const that = this;
setTimeout(function () {
console.log(that.value); //100
}, 100);
},
};
obj.foo();
b. Function.prototype.apply/call/bind 메서드를 통해 명시적으로 this를 바인딩한다.
이 방법은 4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출에서 알아보자
c. 화살표 함수를 사용한다. 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
var value = 1;
const obj = {
value: 100,
foo() {
setTimeout(() => console.log(this.value), 100); // 100
},
};
obj.foo();
✏️ 메서드 내부의 this에는 메서드를 호출한 객체가 바인딩된다. (메서드를 소유한 객체가 아닌 메서드를 호출한 객체 !)
const person = {
name: 'Lee',
getName() {
return this.name;
},
};
console.log(person.getName()); //Lee
getName 프로퍼티가 가리키는 함수 객체는 person 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체이다. 따라서 getName 메서드는 다른 객체의 프로퍼티에 할당하는 것으로 다른 객체의 메서드가 될 수도 있고, 일반 변수에 할당하여 일반 함수로 호출될 수도 있다.
ex) getName 메서드를 anotherPerson 객체의 메서드로 할당
const anotherPerson = {
name: 'Kim',
};
anotherPerson.getName = person.getName;
console.log(anotherPerson.getName()); // Kim
ex) getName 메서드를 일반 함수로 호출
const getName = person.getName;
console.log(getName()); // '' -> window.name과 같음(브라우저 환경에서 window.name의 기본값은 '')
✏️ 프로토타입 메서드 내부에서 사용된 this도 해당 메서드를 호출한 객체에 바인딩된다.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function () {
return this.name;
};
const me = new Person('Lee');
console.log(me.getName()); // Lee
Person.prototype.name = 'Kim';
console.log(Person.prototype.getName()); // Kim
9번 줄에서 getName 메서드를 호출한 객체는 me이다. getName 내부 this는 me를 가리키고, 이때 this.name은 Lee이다.
13번 줄에서 getName 메서드를 호출한 객체는 Person.prototype이다. getName 내부 this는 Person.prototype을 가리키고, 이때 this.name은 Kim이다.
✏️ 생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩된다.
생성자 함수는 인스턴스를 생성하는 함수로, 함수 정의 후 new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작한다. new 연산자와 함께 호출하지 않으면 일반 함수로 동작한다.
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
const circle1 = new Circle(5);
const circle2 = new Circle(10);
const circle3 = Circle(15);
console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20
console.log(circle3); // 반환문 없으므로 undefined
console.log(radius); // 15
Function.prototype.apply와 Function.prototype.call 메서드는 this로 사용할 객체와 인수 리스트를 인수로 전달받아 함수를 호출한다.
두 메서드는 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩한다.
function getThisBinding() {
return this;
}
const thisArg = { a: 1 };
console.log(getThisBinding()); // window
console.log(getThisBinding.apply(thisArg)); // { a: 1 }
console.log(getThisBinding.call(thisArg)); // { a: 1 }
두 메서드는 호출할 함수에 인수를 전달하는 방식만 다를 뿐 동일하게 동작한다.
getThisBinding.apply(thisArg, [1, 2, 3]);
getThisBinding.call(thisArg, 1, 2, 3);
Function.prototype.bind 메서드는 함수를 호출하지 않는다. 첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.
function getThisBinding() {
return this;
}
const thisArg = { a: 1 };
console.log(getThisBinding.bind(thisArg)); // getThisBinding
console.log(getThisBinding.bind(thisArg)()); // { a: 1 }
bind 메서드는 함수를 호출하지 않으므로 마지막처럼 명시적으로 호출해야 한다.
bind 메서드는 메서드의 this와 내부의 중첩/콜백 함수의 this가 불일치하는 문제를 해결할 수 있다.
위 1-b에서 살펴본 예시 상황에서, 콜백함수 내부의 this를 직접 전달하는 방식으로 this를 일치시킬 수 있다.
var value = 1;
const obj = {
value: 100,
foo() {
setTimeout(
function () {
console.log(this.value); //100
}.bind(this),
100
);
},
};
obj.foo();
참고자료
모던 자바스크립트 Deep Dive 22장 this 참조