22장 this
메서드가 자신이 속한 객체의 프로퍼티를 참조하려면 자신이 속한 객체를 가리킬 수 있어야함.
this
는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이다.
this 는 JS 엔진이 암묵적으로 생성하며 함수를 호출하면 arguments
객체와 this
가 암묵적으로 함수 내부에 전달됨.
이때 this 바인딩은 함수 호출 방식에 의해 동적으로 결정
this 바인딩
: this 와 this가 가르킬 객체를 묶는 일.
// this는 어디서든지 참조 가능하다.
// 전역에서 this는 전역 객체 window를 가리킨다.
console.log(this); // window
function square(number) {
// 일반 함수 내부에서 this는 전역 객체 window를 가리킨다.
console.log(this); // window
return number * number;
}
square(2);
const person = {
name: 'Lee',
getName() {
// 메서드 내부에서 this는 메서드를 호출한 객체를 가리킨다.
console.log(this); // {name: "Lee", getName: ƒ}
return this.name;
}
};
console.log(person.getName()); // Lee
function Person(name) {
this.name = name;
// 생성자 함수 내부에서 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
console.log(this); // Person {name: "Lee"}
}
const me = new Person('Lee');
렉시컬 스코프는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 스코프를 결정하지만, this 바인딩은 함수 호출시점에 결정된다.(함수가 호출되야만 결정된다는 것)
함수 호출 방식과 this 바인딩
함수 호출 방식
- 일반 함수 호출
- 메서드 호출
- 생성자 함수 호출
- Function.prototype.apply/call/bind 간접 호출
일반 함수 호출
this 에 전역객체가 바인딩
중첩함수나 콜백함수도 일반함수로 호출되면 전역객체가 바인딩 된다.
🚨 중첩함수와 콜백함수가 외부함수와 this 가 일치하지 않으면 그들을 헬퍼 함수로 동작하기 힘들게함
- 문제상황
const person = { name: 'Lee', foo(callback) { // ① setTimeout(callback, 100); } }; person.foo(function () { console.log(`Hi! my name is ${this.name}.`); // ② Hi! my name is . // 일반 함수로 호출된 콜백 함수 내부의 this.name은 브라우저 환경에서 window.name과 같다. // 브라우저 환경에서 window.name은 브라우저 창의 이름을 나타내는 빌트인 프로퍼티이며 기본값은 ''이다. // Node.js 환경에서 this.name은 undefined다. });
해결방법
- this 바인딩을 that 변수에 할당해서 사용
var value = 1;
const obj = {
value: 100,
foo() {
// this 바인딩(obj)을 변수 that에 할당한다.
const that = this;
// 콜백 함수 내부에서 this 대신 that을 참조한다.
setTimeout(function () {
console.log(that.value); // 100
}, 100);
}
};
obj.foo();
- this를 명시적으로 바인딩함
var value = 1;
const obj = {
value: 100,
foo() {
// 콜백 함수에 명시적으로 this를 바인딩한다.
setTimeout(function () {
console.log(this.value); // 100
}.bind(this), 100);
}
};
obj.foo();
- 화살표 함수 이용
화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
26장 ES6 함수의 추가 기능#화살표 함수
var value = 1;
const obj = {
value: 100,
foo() {
// 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
setTimeout(() => console.log(this.value), 100); // 100
}
};
obj.foo();
메서드 호출
메서드 내부의 this 에는 메서드를 호출한 객체( . 앞의 객체)가 바인딩된다.
메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function () {
return this.name;
};
const me = new Person('Lee');
// getName 메서드를 호출한 객체는 me다.
console.log(me.getName()); // ① Lee
Person.prototype.name = 'Kim';
// getName 메서드를 호출한 객체는 Person.prototype이다.
console.log(Person.prototype.getName()); // ② Kim
생성자 함수 호출
this에 생성자 함수가 생성할 인스턴스가 바인딩된다
// 생성자 함수
function Circle(radius) {
// 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// 반지름이 5인 Circle 객체를 생성
const circle1 = new Circle(5);
// 반지름이 10인 Circle 객체를 생성
const circle2 = new Circle(10);
console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20
Function.prototype.apply/call/bind
얘네들은 Function.prototype 메서드 여서 모든 함수가 상속받아 사용가능
this로 사용할 객체와 인수 리스트를 전달받아 함수를 호출시킴.
apply
와 call
메서드는 함수를 호출시키는 것은 동일하나, 인수 전달방식만 다름.
function getThisBinding() {
console.log(arguments);
return this;
}
// this로 사용할 객체
const thisArg = { a: 1 };
// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다.
// apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다.
console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {a: 1}
// call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다.
console.log(getThisBinding.call(thisArg, 1, 2, 3));
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {a: 1}
apply
와 call
메서드의 대표적인 용도는 arguments 객체 같은 유사 배열 객체에 배열 메서드 사용하는 경우이다.
function convertArgsToArray() {
console.log(arguments);
// arguments 객체를 배열로 변환
// Array.prototype.slice를 인수없이 호출하면 배열의 복사본을 생성한다.
const arr = Array.prototype.slice.call(arguments);
// const arr = Array.prototype.slice.apply(arguments);
console.log(arr);
return arr;
}
convertArgsToArray(1, 2, 3); // [1, 2, 3]
bind
메서드는 함수를 호출하지 않고 this로 사용할 객체를 전달만 함.
아까 말했었던, 중첩함수, 콜백함수의 this가 메서드의 this와 불일치하는 문제해결
const person = {
name: 'Lee',
foo(callback) {
// bind 메서드로 callback 함수 내부의 this 바인딩을 전달
setTimeout(callback.bind(this), 100);
}
};
person.foo(function () {
console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee.
});
요약
호출방법 | this |
---|---|
일반 함수 호출 | 전역객체 |
메서드 호출 | 메서드를 호출한 객체 |
생성자 함수 호출 | 생성자 함수가 생성할 인스턴스 |
apply, call, bind | 첫번째 인수로 전달한 객체 |