[22장] this
자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-referencing variable)
this를 통해 자신이 속한 객체나 자신이 생성할 인스턴스의 프로퍼티, 메서드를 참조할 수 있다.
객체는 상태를 나타내는 프로퍼티와 동작을 나타내는 메서드를 하나의 논리적 단위로 묶은 복합적인 자료구조다.
그리고 메서드는 자신이 속한 객체의 상태(즉, 프로퍼티)를 참조하고 변경할 수 있어야되는데, 프로퍼티를 참조하려면 먼저 자기가 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.
ㅇ객체 리터럴 방식
메서드 내부에서 자신이 속한 객체를 가리키는 식별자를 재귀적으로 참조 가능
const circle = {
radius: 5,
getDiameter( ) {
return 2*circle.radius; //circle을 참조할 수 있다
}
};
위 예시에서 circle을 참조할 수 있는 이유는 getDiameter 메서드가 호출되는 시점에는 이미 객체 리터럴의 평가가 완료되어 객체가 생성된 상태이고, 당연히 const circle 식별자에 생성된 객체가 할당된 상태이므로 참조가 가능한 것이다.
하지만 이렇게 재귀적으로 참조하는 방식은 일반적이지도 않고 별로 좋지도 않음
ㅇ생성자 함수 방식
위 객체 리터럴 방식에선 객체가 이미 만들어지므로 그걸 참조하면 그만이었다.
그런데 생성자 함수 방식은 인스턴스를 생성하기 전까진 참조할 대상이 없다. 즉,
const 인스턴스1호요 = new 생성자함수( );
이런 식으로 생성을 해주기 전까진 함수만 덩그러니 있는 상태고,
생성자 함수 내부에서 프로퍼티나 메서드를 추가하려면 그 안에서 생성'할' 인스턴스를 가리키는 특수한 식별자가 필요하다,
이를 위해 자바스크립트는 this라는 특수한 식별자를 제공한다.
this는 암묵적으로 생성되며, 함수 호출시 arguments 객체와 함께 함수 내부에 전달된다.
함수 내부에서 arguments 객체를 지역 변수처럼 사용하듯 this도 지역 변수처럼 사용할 수 있다.
단, this가 가리키는 값, 즉 this 바인딩은 함수 호출 방식에 의해 동적으로 결정된다.
* 바인딩 : 식별자와 값을 연결하는 과정. 즉 this 바인딩은 this식별자와 this가 가리킬 객체를 바인딩하는 것
2. 함수 호출 방식과 this 바인딩
this 바인딩은 함수 호출 방식에 의해 동적으로 결정된다. 즉, 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다. 문제는 함수 호출 방식이 다양하다는 것이다
2.1 일반 함수 호출
뭐가됐든 일반 함수로 호출되면(중첩함수든 콜백함수든 아무튼 뭐였든간에) this엔 전역 객체(window)가 바인딩된다.
애초에 객체 생성도 안해주는 일반 함수에서 this는 별 의미가 없기 때문
(그래서 strict mode에선 undefined로 바인딩됨)
function 일반함수() {
console.log(`일반함수의 this는 ${this}`); //window
function 중첩함수() {
console.log(`중첩합수의 this는 ${this}`); // window
}
중첩함수();
}
일반함수();
const obj = {
value : 100,
// 메서드 내부에서 this는 메서드를 호출한 객체를 가리킴
메서드() {
console.log(`메서드의 this는 ${this}`); // object
console.log(`메서드의 this.value는 ${this.value}`); // 100
// 메서드 내부에서 정의한 중첩함수도 일반 함수로 호출되면
// this엔 전역객체가 바인딩됨
function 중첩함수() {
console.log(`중첩합수의 this는 ${this}`); // window
}
중첩함수();
}
}
obj.메서드();
2.2 메서드 호출
메서드 내부의 this엔 메서드를 호출한 객체가 바인딩된다. 즉, obj.메서드( ) 처럼 마침표 연산자앞에 기술한 객체가 바인딩된다(메서드를 소유한 객체말고 메서드를 호출한 객체에 바인딩)
const person = {
name: '김씨요',
getName() {
return this.name;
}
}
console.log( person.getName( ) ) // '김씨요'
// 사실 위 getName 함수는 person 객체랑 상관없이 존재하는 객체임
// 단지 person.getName 프로퍼티에 그 함수를 가리키도록 할당했을 뿐임
// getName : function getName( ) {
// return this.name;
// }
const getName = person.getName;
console.log(getName());
// 일반 함수로 호출되었으며, this = window를 가리키고있다
// 즉, window.name 프로퍼티를 참조중이며 (실제로 있는 프로퍼티임)
// 브라우저 창의 이름을 나타내는 빌트인 프로퍼티로 기본값이 '' 임
// 따라서 결과도 ''
2.3 생성자 함수 호출
생성자 함수 내부의 this엔 미래에 생성할 인스턴스가 바인딩된다
2.4 Function.prototype.apply/call/bind 메서드에 의한 간접 호출
기본적으로 Function의 프로토타입 메서드이므로 모든 함수가 상속받아 사용 가능하다.
function whatIsThis() {
return this;
}
const thisArg = { a : 10 };
console.log(whatIsThis()); // window
console.log(whatIsThis.apply(thisArg)); // {a: 10}
console.log(whatIsThis.call(thisArg)); // {a: 10}
console.log(whatIsThis.bind(thisArg)); // f whatIsThis
console.log(whatIsThis.bind(thisArg)()); // {a: 10}
ㅇapply, call( thisArg [, argsArray] )
- 함수를 호출하는 메서드.
- this로 사용할 객체를 직접 지정하거나, 인수 리스트를 직접 만들어 전달하면서 함수를 호출할 수 있다
- 차이점은 인수 전달시 apply의 경우 [1,2,3] 처럼 배열로, call은 1, 2, 3 쉼표로 구분해서 전달
ㅇbind( thisArg )
- 기존 함수에 this 바인딩을 교체한 새로운 함수를 반환
- 함수를 호출하는건 아니고 그냥 새로운 함수 하나를 만들어줌
- bind 메서드를 활용하면 콜백 함수의 this 문제를 해결할 수 있음
const person = {
이름 : '김씨요',
함수(콜백함수) {
console.log(this); // person 객체
// 지금 this는 메서드 내부이므로 메서드를 호출하는 person이 바인딩된 상태
setTimeout(콜백함수, 100);
}
}
person.함수(function(){
console.log(this) // window
// 왜냐하면 콜백 함수가 일반 함수로 호출되거든 (그러므로 this = window)
// 이걸 해결하려면 setTimeout(콜백함수.bind(this), 100) 로 고치면 됨
// 이렇게 고치면 콜백함수의 this에 person이 바인딩될 거임
})