자바스크립트에서의 this
자바스크립트 내에서 this 는 '누가 나를 불렀느냐'를 뜻한다고 합니다. 즉, 선언이 아닌 호출에 따라 달라진다는 거죠.
그럼 각 상황별로 this 가 어디에 바인딩되는지 알아보겠습니다.
우선 this 가 무엇인지 콘솔에 찍어보겠습니다.

this 를 단독으로 써도, 함수 내에서 써도 Window 를 가리키고 있습니다. 일반적으로 Window 라고 생각하면 됩니다.
하지만, 제가 알아볼 건 this 가 바뀌는 경우가 있다는 겁니다. 바로 객체 내에서 this 의 값이 변경되는데요. 다음 예제를 보겠습니다.

obj.sayName(); 의 값이 jh 가 나오는 것을 볼 수 있는데요. 만약 this 가 Window 였으면 Window.name 을 값으로 출력해 마지막 줄의 출력값처럼 빈칸입니다. 여기서 this 는 Window 가 아닌 obj 로 값이 변경되었다는 것을 확인할 수 있습니다.

sayName 함수를 sayN 이라는 변수에 대입을 하고, sayN 을 출력하면 Window.name 의 값인 빈칸이 출력되는 것을 볼 수 있습니다. 여기서 알아야 할 부분은
const obj = {
name: 'jh',
sayName() {
console.log(this.name);
}
};
이러한 구조를 갖고 있다고 해서 this 가 꼭 해당 객체인 obj 가 아니라는 점입니다.
굉장히 혼란스럽습니다. 그렇다면 바로 변경되는 기준 및 규칙이 어떻게 되는지 알아보겠습니다.
this 가 변경되는 경우
this 의 값이 변경되는 경우는 다음 세 가지입니다.
객체.함수
this 는 함수가 호출될 때 그 값이 정해집니다. 함수를 호출할 때 별 다른 작용이 없다면 Window 가 되고, 함수를 호출할 때 다음과 같이 함수 앞에 객체가 붙거나 한다면 this 가 해당 객체가 됩니다.
obj.sayName(); // this 의 값은 window 에서 obj 로 변경됨
new 함수
예전 자바스크립에서 객체를 생성하는 방법에 대해 알아보겠습니다. 다음 코드를 function 생성자 함수라고 부릅니다. 함수를 호출할 때, new 를 붙여 호출하는 경우, this 는 객체 본체가 됩니다.

함수.bind, call, apply
해당 코드에서 this 는 아직 Window 를 가리키고 있습니다.

하지만, 함수.bind, call, apply 를 통해 this 의 값을 변경할 수 있습니다. 각각 예제로 사용해보겠습니다.
1. bind

여기서 함수.bind() 를 통해 소괄호 안에 들어있는 객체, {name: 'JH'} 를 this 의 값으로 변경했습니다. bind 는 새로운 함수를 만들어주는 역할을 해주기 때문에 끝에 소괄호를 붙여 함수를 호출해줘야 합니다.
2. apply
apply 는 bind 와 사용방법이 매우 비슷하지만, 다른 점은 함수를 호출할 때, 소괄호를 붙이지 않는다는 점입니다. 그래서 apply 는 this 의 값을 변경하면서 함수를 바로 호출해줍니다.

3. call
call 은 apply 와 같습니다.

결국 this 는 렉시컬 스코프 처럼 코드를 적으면서 해당 값이 정해지는 것이 아닌, 함수가 호출될 때 그 값이 결정된다는 것을 알 수 있습니다.
화살표 함수
화살표 함수 내에서 this 는 어떤 값이 될까요? 직접 해보겠습니다.

이 같은 경우, sayName 메소드의 this 는 자신을 호출한 객체 obj 가 아니라 함수 선언 시점의 상위 스코프인 전역객체를 가리키게 됩니다. 어차피 일반 함수를 사용해도 메소드로 호출하면 자신을 호출한 객체를 가리키기 때문에 메소드에서 화살표 함수를 쓸 필요는 없겠죠? 그렇다면 화살표 함수 내에 있는 모든 this 의 값은 Window 일까요? 상위 스코프를 따르기 때문에 물론 그렇지 않습니다.
퀴즈입니다! 다음 코드에서 반환되는 값은 어떻게 될까요?
const obj = {
name: 'JH',
sayName() {
console.log(this.name);
function inner() {
console.log(this.name);
}
inner();
}
}
obj.sayName();
정답은 다음과 같습니다.

자세히 설명해보자면
1. obj.name(); 을 통해 함수 sayName 을 호출합니다. 그리고 일반 함수인 sayName 은 객체.함수 의 영향으로 this 의 값이 기존의 Window 에서 자신이 속해 있는 객체인 obj 로 변경되어 첫 번째 값으로 sayName 의 console.log(this.name); 은 JH(obj.name) 이 됩니다.
2. sayName 함수는 아직 끝나지 않았습니다. inner 함수를 선언하고, 이를 호출하고 있죠. 호출 당시 inner 함수의 모습을 보면 일반적으로 함수를 호출하는 형태로 앞에서 본 this 가 변경되는 경우에 속하지 않습니다. 예상대로 this 의 값은 Window인 것이죠. 때문에 inner 함수의 console.log(this.name); 은 빈칸(Window.name)을 반환합니다.
화살표 함수를 이용한 응용 퀴즈입니다!
const obj = {
name: 'JH',
sayName() {
console.log(this.name);
const inner = () => {
console.log(this.name);
}
inner();
}
}
obj.sayName();
정답은 다음과 같습니다.

항상 함수를 호출할 때 this 의 값이 정해진다고 했습니다. 함수를 호출한 코드를 보았는데 this 의 값을 변경하는 모습은 아닙니다. 문제는 화살표 함수에 있습니다. 화살표 함수는 전역객체인 Window 를 가져오는 것이 아닌 해당 함수의 부모함수의 this 를 그대로 가져옵니다. 하지만, this 는 호출할 때 판단해야 합니다. 다시 한 번 강조하는 이유는 아래 obj.sayName 에서 함수를 호출하는 코드를 보지 않은 채 부모함수인 sayName 의 this 를 알긴 어렵습니다.
즉, 화살표 함수의 this 는 화살표 함수의 부모함수의 this 값을 가져오는데, 단순히 부모함수를 쳐다봐서는 this 의 값을 알아내기 어렵고, 해당 부모 함수가 어떻게 호출이 되었는지 보면 쉽게 알 수 있습니다.
'zerocho > 인간 JS 엔진 되기(JS 고급 강좌)' 카테고리의 다른 글
블록 스코프와 매개변수 (0) | 2023.03.21 |
---|---|
this를 분석할 수 없는 케이스 (0) | 2023.03.21 |
호이스팅, TDZ(Temporal Dead Zone) (0) | 2023.03.20 |
스코프 체인 (0) | 2023.03.20 |
호출 스택 분석 (0) | 2023.03.17 |