[잔뿌리 호기심]은 잔뿌리처럼 메인 주제에서 뻗어나온 개인적인 궁금증을 다룹니다.
여러가지 this 바인딩
우리는 Chapter 22 - this👨💻 에서 여러가지 this의 바인딩에 대해서 알아봤다.
그 과정에서 나는 책을 바탕으로 글을 작성하다 보니, 책에는 나와있지 않은 두 가지 가벼운 궁금증이 생겼다.
- 일반 함수에서는 왜 전역객체(window) 가 바인딩될까?
- 나머지 4가지 경우의 this바인딩은 어떻게 이루어질까?
총 2개의 글을 통해 위 두가지 주제에 대해서 알아볼까 한다.
이번 주제는 ‘나머지 4가지 경우의 this바인딩은 어떻게 이루어질까?’ 이다.
앞선 [잔뿌리 호기심]일반함수의 this에는 왜 전역객체가 바인딩될까? 도 읽어보시길 추천한다.
나머지 4가지 경우에서의 this 바인딩
앞선 글에서도 작성했지만 여기서의 4가지 경우는 기본 4가지의 심화 및 응용과정에 가깝다고 생각한다.
책에 정리되어있는 4가지 이외의 다른 this 바인딩에 대해서 알아보자.
- Arrow Function 호출
- 이벤트 핸들러 내부에서의 this
- 전역 컨텍스트 호출
- 클래스에서의 this
화살표 함수 호출
화살표 함수 호출에서의 this에서 가장 중요한 특징은
자신의 this를 가지는 것이 아닌, 화살표 함수를 둘러 싼 상위 스코프의 this값을 상속받아 사용한다 라는 점이다.
1
2
3
4
5
6
7
8
9
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // 여기서 `this`는 Person 인스턴스를 가리킨다.
}, 1000);
}
const p = new Person();
위 코드에서 setInterval 내부의 화살표 함수는 ‘항상’ Person 인스턴스를 가리키게 된다.
이러한 특징은 다른 함수들은 실행될 때 바인딩되는 반면, 화살표 함수는 정의될 때 바인딩 된다는 또 다른 특징을 만든다.
특히나 JS는 호출될 때 실행되거나 정의되어 예상치 못한 오류가 발생하는 빈도가 높았던 언어인 만큼,
정의 단계에서 this바인딩을 명확히 할 수 있다는 점은 함수를 더욱 직관적으로 사용할 수 있게 해주고 보다 예측 가능한 this바인딩을 사용할 수 있게 해준다.
이벤트 핸들러 내부에서의 this
이벤트 핸들러 내부에서의 this는 이벤트 리스너가 바인딩 된 요소를 가리킨다.
1
2
3
document.querySelector('button').addEventListener('click', function() {
console.log(this); // 여기서 `this`는 버튼 DOM 요소를 가리킨다.
});
이 또한 일반 함수 호출과 마찬가지로 실행 될 떄 바인딩 된다.
위 예제 코드에서의 this는 해당 이벤트 리스터가 부착 된 버튼 DOM 요소를 가리키게 된다.
그래서 다시 생각해보면 실제로 this.value 와 같은 값을 사용해 본 기억이 있을 것이다.
여기서 한 발짝 더 나아가 우리는 이벤트리스너에 화살표 함수를 콜백 함수로 넣을 수 있다는 것을 안다.
그렇다면 콜백함수로서 화살표 함수를 넣는다면 this는 어디에 바인딩 될까?
정답은 위에서 설명한 것과 마찬가지로 상위 스코프의 this 값을 가지게 된다.
1
2
3
4
5
6
7
8
9
class MyClass {
constructor() {
this.name = 'MyClass instance';
document.querySelector('button').addEventListener('click', () => {
console.log(this.name); // 여기서 `this`는 MyClass 인스턴스를 가리킨다.
});
}
}
new MyClass();
위 예제 코드에서의 this 는 버튼 DOM 요소가 아닌, 화살표 함수의 상위 스코프 MyClass 인스턴스를 가리키게 된다.
여기서 또 한 발짝 더 나아가자면 최근 우리는 Vanilla JS 가 아닌 React를 주로 사용해서 개발한다. 보통 React에서의 이벤트 리스너는 on???? 속성을 통해 부여하는데, 이 경우엔 this바인딩이 어떻게 되는것일까?
이것 또한 추후에 잔뿌리 호기심에서 알아보도록 하겠다.
전역 컨텍스트 호출
전역 컨텍스트는 전역 코드가 실행되는 환경을 의미한다. 여기서의 this는 ‘항상’ 전역객체를 가리킨다.
1
2
3
console.log(this === window) // true
this.a = "GLOBAL CONTEXT"
console.log(window.a) // "GLOBAL CONTEXT"
이 부분에서 ‘일반 함수 호출’ 과 매우매우 유사하다는 것을 알 수 있다. 일반 함수 호출과 전역 컨텍스트 호출의 공통점을 말해보자면,
- 두 호출 모두 non-strict mode에서 전역객체를 가리킨다.
- strict mode를 적용하면 undefined를 가리킨다.
- 어느 객체에도 속하지 않은 상태로 호출된다.
위 예시 코드와 공통점을 놓고 비교하자면 거의 똑같아보인다.
그러나 명확히 하자면 실행컨텍스트의 단위에서 차이점을 보인다.
일반 함수 호출의 경우엔 함수가 실행되며 실행 컨텍스트가 콜스택에 푸시되며, 이 과정에서 this 바인딩이 결정된다.
즉, 전역 컨텍스트 위에 일반 함수의 컨텍스트 내부에서 생성된다.
하지만 전역 컨텍스트 호출은 이름 그대로 전역 컨텍스트에서 this바인딩이 결정되어있다.
정리하자면 일반 함수 호출과, 전역 컨텍스트 호출은 동작의 차이 는 없으나 개념의 차이 가 존재한다고 받아들이면 될 것 같다.
클래스에서의 this
JS 에서의 Class 는 ES6에서 도입된 Syntax Sugar 이다. 이미 JS에서는 생성자 함수를 통해 상속을 구현할 수 있으나, Class를 통해 조금 더 전통적인 선언적 방법을 통해 상속을 정의할 수 있게 되었다.
Class에서의 this 또한 새로 생성된 인스턴스에 바인딩되는데,
이는 생성자 함수 호출에서의 this 바인딩과 유사함을 넘어 동일하다.
애초에 Javascript에서의 Class의 개념과 사용법이 생성자함수와 완전히 동일해서 그런 것 같다.
실제로 Class 가 Syntax Sugar 라고 불리는 이유 또한 동작은 동일한데 단순히 선언방법만 달라졌기 때문이기도 한 것 같다.
결론
- 클래스와 전역컨텍스트 호출의 this 바인딩은 거의 유사하다. 개념적인 차이만 있을 뿐.
- 화살표함수는 선언 시 this가 바인딩되어 조금 더 명확한 this 바인딩을 돕는다.
- 이벤트 리스너의 this은 해당 이벤트리스너가 부착된 DOM 요소를 가리킨다.
