모던 자바스크립트 deep dive에서 this 관련 부분을 읽던 도중에 헷갈려서
자세히 공부한 내용을 블로그에 남김.
외부 함수인 메서드와 중첩 함수 또는 콜백 함수의 this가 일치하지 않는다
외부 함수인 메서드와
중첩 함수 또는 콜백 함수의 this가 일치하지 않는다는 의미는,
함수의 호출 방식에 따라 this가 가리키는 대상이 달라진다는 것을 의미한다.
const obj = {
value: 100,
outerFunc: function() {
console.log("outerFunc's this:", this); // {value: 100, outerFunc: ƒ}
const innerFunc = function() {
console.log("innerFunc's this:", this); // window
};
innerFunc();
}
};
obj.outerFunc();
위의 예시에서 obj.outerFunc()를 호출하면, outerFunc 함수 내부에서의 this는 obj 객체를 가리킨다.
그러나 innerFunc 함수 내부에서의 this는
일반 함수 호출과 같은 방식으로 실행되기 때문에 전역 객체인 window를 가리킨다.
이로 인해 외부 함수와
중첩 함수 또는 콜백 함수의 this가 일치하지 않는 것을 확인할 수 있다.
이러한 문제를 해결하기 위해서는 화살표 함수를 사용하면 된다.
화살표 함수는 자신만의 this 바인딩을 가지지 않고,
외부 스코프의 this를 그대로 사용하기 때문이다.
이를 활용하여 예시를 수정하면 다음과 같다.
const obj = {
value: 100,
outerFunc: function() {
console.log("outerFunc's this:", this); // {value: 100, outerFunc: ƒ}
const innerFunc = () => {
console.log("innerFunc's this:", this); // {value: 100, outerFunc: ƒ}
};
innerFunc();
}
};
obj.outerFunc();
이제 innerFunc 함수 내부에서도 this가 obj 객체를 가리키게 되어,
외부 함수와 중첩 함수 또는 콜백 함수의 this가 일치하게 된다.
이 코드에서는 setTimeout 콜백함수의 this가 obj 가 아니라 왜 window를 가리키는걸까?
var value = 1;
const obj = {
value: 100,
foo() {
console.log("foo's this: ", this); // {value: 100, foo: ƒ}
// 콜백 함수 내부의 this에는 전역 객체가 바인딩된다.
setTimeout(function () {
console.log("callback's this: ", this); // window
console.log("callback's this.value: ", this.value); // 1
}, 100);
}
};
obj.foo();
setTimeout 함수 내부의 콜백 함수에서
this가 window를 가리키는 이유는 JavaScript 엔진의 동작 방식 때문이다.
기본적으로 JavaScript에서 함수 내부에서 this가 가리키는 것은 해당 함수가 호출된 방식에 따라 달라진다.
- obj.foo() 메서드 호출에서의 this는 obj 객체를 가리킨다. 이는 객체의 메서드로서 호출되었기 때문이다.
- setTimeout 함수 내부의 콜백 함수에서의 this ⇒ setTimeout 함수의 콜백 함수는 일반적인 함수 호출과 달리, 전역 컨텍스트에서 실행된다.
- 따라서 this는 기본적으로 전역 객체인 window를 가리키게 된다.
따라서 setTimeout 내부에서 this.value를 참조하면 window.value가 되고,
이 값은 전역 스코프에서 정의한 var value = 1;의 value 값인 1을 가리키게 되는거다.
이러한 동작을 원하지 않고, obj 객체 내부의 값을 참조하려면
다음과 같이 setTimeout의 콜백 함수 내에서 화살표 함수를 사용하면 된다.
화살표 함수는 자신만의 this 바인딩을 가지지 않으며, 외부 스코프의 this를 그대로 사용하기 때문이다.
var value = 1;
const obj = {
value: 100,
foo() {
console.log("foo's this: ", this); // {value: 100, foo: ƒ}
// 화살표 함수를 사용하여 this를 유지
setTimeout(() => {
console.log("callback's this: ", this); // {value: 100, foo: ƒ}
console.log("callback's this.value: ", this.value); // 100
}, 100);
}
};
obj.foo();
위와 같이 코드를 수정하면 setTimeout 콜백 함수에서 this는 obj 객체를 가리키고, this.value는 100이 된다.
그리고 모든 콜백함수가 this바인딩을 window 객체로 하는 것은 아니다!
콜백함수를 넘겨받는 대상에 따라 달라진다. this를 처리하는 방식이 달라지기 때문이다.
밑에 예시를 보자.
[1, 2, 3].forEach(function() {
console.log(this)
}, [10, 20, 30] )
// 출력 결과
// [10, 20, 30]
// [10, 20, 30]
// [10, 20, 30]
위의 코드에서 this 는 window를 바라보는게 아니라,
콜백함수 뒤에 전달해준 [10, 20, 30] 배열을 바라보게 된다.
이처럼 콜백함수를 넘겨받는 대상에 따라 this 바인딩이 달라질 수 있다.
그렇다면 이 코드에서는 this 바인딩이 어떻게 이루어질까?
const person = {
name : 'Lee',
getName() {
return this.name;
}
};
const anotherPerson = {
name: 'Kim'
};
anotherPerson.getName = person.getName;
console.log(anotherPerson.getName());
const getName = person.getName;
console.log(getName());
주어진 코드에서 this 바인딩은 다음과 같이 이루어진다.
anotherPerson.getName = person.getName;
이때 person.getName 메서드는 person 객체의 메서드이므로,
anotherPerson.getName에 할당된 함수는 person.getName와 같은 함수를 참조하게 된다.
person.getName 메서드를 anotherPerson 객체의 getName 속성에 할당한다.
console.log(anotherPerson.getName());
이때 this는 호출한 객체인 anotherPerson 객체를 가리키게 된다.
따라서 this.name은 'Kim'이 되어 결과는 'Kim'이 출력된다.
anotherPerson 객체의 getName 메서드를 호출한다.
console.log(getName());
이때 함수는 어떤 객체에 속해있지 않으므로 this는 전역 객체를 가리키게 된다.
요약하자면,
anotherPerson.getName() 호출 시
this는 anotherPerson 객체를 가리키고 'Kim'을 반환하며,
3번에서 person.getName 메서드가 getName 변수에 할당되는 시점에 this 바인딩은 설정되지 않는다.
따라서 console.log(getName()); 이 실행될 때,
일반적인 함수 호출 getName()에서는 this는 전역 객체를 가리키게 된다.
함수 호출 시 this는 호출 컨텍스트에 따라 동적으로 결정되는데,
따라서 this 바인딩은 함수 호출 시점에 결정되므로,
console.log(getName()); 이 실행될 때, this는 전역 객체를 바라보게 된다.
만약 person.getName 메서드를 그대로 호출하려면
다음과 같이 bind를 써서 명시적으로 this를 지정하면 된다.
그러므로 getName()을 호출하면 전역 객체에 this가 바인딩되며,
person 객체와는 아무 관련이 없는 함수로 동작하게 된다.
getName 함수가 호출될 때 person.getName과는 무관한 전역 객체를 가리키게 된다.
이것은 JavaScript의 동작 방식으로 인해 일어나는 현상이다.
getName 함수가 호출되면 그 시점에서 this 바인딩이 결정된다.
this 바인딩은 함수가 호출될 때 동적으로 결정된다.
const getName = person.getName;
이 코드에서
getName() 호출 시 this는 전역 객체를 가리키고 빈 문자열을 반환한다.
전역 객체에는 name 속성이 정의되어 있지 않기 때문에
this.name은 빈 문자열('')이 되고, 결과로 빈 문자열이 출력되게 된다.
getName 변수에 할당된 함수를 호출한다.
'개발 > JavaScript' 카테고리의 다른 글
실행 컨텍스트 (Execution Context) (1) | 2024.09.06 |
---|---|
Set 객체: 값의 고유한 집합 저장소 (1) | 2023.12.17 |
클래스를 이용한 모듈화 (0) | 2023.07.01 |
생성자 함수와 프로토타입의 차이? (0) | 2023.06.23 |
JS에서의 Boxing 처리 (0) | 2023.06.21 |
개발 블로그
포스팅이 좋았다면 "좋아요❤️" 누르기 !