Javascript에서의 this 란?
MDN에서는 자바스크립트의 this
를 아래와 같이 정의하고 있습니다.
JavaScript에서 함수의 this
키워드는 다른 언어와 조금 다르게 동작합니다. 또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있습니다. 대부분의 경우 this
의 값은 함수를 호출한 방법에 의해 결정됩니다. 실행중에는 할당으로 설정할 수 없고 함수를 호출할 때 마다 다를 수 있습니다. ES5는 함수를 어떻게 호출했는지 상관하지 않고` this `값을 설정할 수 있는
bind
메서드를 도입했고, ES2015는 스스로의this
바인딩을 제공하지 않는 화살표 함수를 추가했습니다(이는 렉시컬 컨텍스트안의this
값을 유지합니다).`
위 설명과 같이 자바스크립트에서의 this
는 함수가 호출되는 문맥에 따라 가리키는 대상이 달라지는 키워드입니다. 일반적인 프로그래밍 언어들과 다르게 동작할 수 있으며, 호출 방법에 따라 다르게 결정됩니다. 비엄격 모드와 엄격 모드에서도 this
의 동작이 다를 수 있습니다.
this
를 제대로 이해하기 위해 자바스크립트에서의 다양한 this
사용 사례를 알아보겠습니다:
1. 전역 문맥에서의 this
전역에서의 this
는 비엄격 모드에서 전역 객체를 참조하며, 브라우저 환경에서는 window
가 됩니다.
console.log(this === window); //true
a = 10;
console.log(window.a, this.a, window.a === this.a); // 10 10 true
2. 함수 문맥에서의 this
함수 문맥에서는 함수의 선언 위치가 아닌 호출 방법에 따라 this
가 결정됩니다.
function functionThisLogger() {
console.log(this);
}
const food = {
name: '김치볶음밥',
price: 5000,
print() {
console.log(`${this.name}의 가격은 ${this.price}원 입니다.`);
},
detail: {
count: 10,
print() {
console.log(`${this.count}개 남았습니다.`);
}
},
callThisInFood: functionThisLogger,
}
food.print(); // 김치볶음밥의 가격은 5000원 입니다.
food.detail.print(); // 10개 남았습니다.
functionThisLogger(); // window
food.callThisInFood(); // food
이처럼 this
는 함수를 호출한 객체에 따라 food
, food.detail
, window
로 각각 달라집니다. 마찬가지로, 다음처럼 할당한 후에 호출하면 다르게 동작할 수 있습니다.
const callThisInFood = food.callThisInFood;
callThisInFood(); // window
이 경우, 호출 시점에 this
를 참조할 객체가 없으므로 this
는 window
를 가리키게 됩니다.
3. 화살표 함수에서의 this
화살표 함수는 일반 함수와 달리 선언된 시점의 상위 스코프에서 this
를 상속받습니다. 호출 시점이 아닌 선언 시점에 this
가 결정되며, 이후 변경되지 않습니다
const arrowFunctionThisLogger = () => {
console.log(this);
};
const food = {
name: '새우볶음밥',
price: 4000,
print: () => {
console.log(`${this.name}의 가격은 ${this.price}원 입니다.`);
},
callThisInFood: arrowFunctionThisLogger,
callThisInFood2() {
arrowFunctionThisLogger();
}
}
food.print(); // undefined의 가격은 undefined원 입니다.
arrowFunctionThisLogger(); // window
food.callThisInFood(); // window
food.callThisInFood2(); // window
위 예에서 arrowFunctionThisLogger
는 전역 환경에서 선언되었으므로 this
는 window
를 가리키고 다양한 방법으로 호출하더라도 변경되지 않습니다.
추가 예시 (잘못된 사용 예)
const getAppleInformation = function() {
return {
name: 'Apple',
stores: [
{ region: '대구', stock: 100 },
{ region: '서울', stock: 200 },
{ region: '부산', stock: 300 }
],
price: 1000,
getTotalPriceByRegion() {
return this.stores.map(function(store) {
return this.price * store.stock;
});
}
}
};
const appleInformation = getAppleInformation();
console.log(appleInformation.getTotalPriceByRegion()); // [NaN, NaN, NaN]
여기서 getTotalPriceByRegion
내부의 this.stores.map
은 호출한 객체가 appleInformation
기 때문에 문제 없이 동작하지만, map
함수의 콜백 함수 내의 this
는 호출한 객체가 별도로 없기 때문에 window
를 가리키며, this.price
는 undefined
로 처리되어 NaN
이 반환됩니다.
수정 방법
이 문제는 여러 가지 방법으로 해결할 수 있습니다.
수정 1: 화살표 함수 사용
getTotalPriceByRegion() {
return this.stores.map(store => this.price * store.stock);
}
화살표 함수를 사용하면 선언된 시점의 상위 스코프의 this
를 가르키기 때문에 map
함수의 콜백 함수로 선언이 될 때 this
가 결정이 되고, map
함수가 호출된 환경의 this
는 appleInformation
이 되면서 정상 동작 하게 됩니다.
수정 2: map의 두 번째 인자인 thisArg
사용
getTotalPriceByRegion() {
return this.stores.map(function(store) {
return this.price * store.stock;
}, this);
}
map
함수의 두 번째 인자 thisArg
로 this
를 명시하여 값을 설정할 수 있습니다.
수정 3: bind
사용
getTotalPriceByRegion() {
return this.stores.map(function(store) {
return this.price * store.stock;
}.bind(this));
}
수정2와 비슷한 방법으로 함수의 bind
메소드를 통해 this
값을 명시하여 값을 설정할 수 있습니다. 이 때 주의해야 할 점은 동일한 함수에 여러 번 bind
를 할 경우 가장 처음 bind
한 값으로 고정된 후에 변경되지 않는다는 점 입니다.
결론
위에 작성한 내용 외에도 this
는 apply
메소드 등 여러 방법으로 설정할 수 있습니다. 자바스크립트의 this
는 어렵게 생각하면 정말 어려운 개념이지만 일반 함수는 호출 시점에 따라 호출한 객체로 this
가 동적으로 변경되고, 화살표 함수는 선언 시점의 환경에서 this
를 상속받아 고정된다는 점만 기억해도 대부분의 상황에서 올바르게 이해할 수 있을 것입니다.
위 내용 관련해서 궁금한 점이 있거나 잘못된 부분이 있다면 댓글 부탁드립니다.
'Javascript' 카테고리의 다른 글
원시 타입과 참조 타입 - 4. V8엔진의 참조 타입 (2) | 2024.10.23 |
---|---|
원시 타입과 참조 타입 - 3.V8 엔진의 Oddball 타입 (0) | 2024.10.21 |
원시 타입과 참조 타입 - 2. V8 엔진의 문자열 처리 메커니즘 (2) | 2024.10.17 |
원시 타입과 참조 타입 - 1. V8 엔진의 숫자 처리 방식 (0) | 2024.10.17 |