Javascript는 일급 함수, 프로토타입 기반 언어입니다. 이 때문에 this 키워드는 함수 실행 상황에 따라 바뀌게 됩니다. 전역 문맥과 함수 문맥으로 크게 나뉘고, 함수, 객체 메서드, 화살표 함수, 엄격모드 모두 다르게 작동 되므로, 이 모든 내용은 알아 두셔야합니다.
전역 문맥
전역 문맥의 this는 실행환경 별로 다르게 작동합니다. 브라우저에서는 전역 객체인 window를, Node.js와 Bun은 빈 객체 {}를, Deno는 undefined 로 할당되어 있습니다.
각기 다른 글로벌 객체를 다루기 위해 ES2020에서는 globalThis 라는 키워드가 추가되어있습니다. 런타임의 글로벌 객체는 Deno, global 등 다양하기 때문에 때문에 브라우저에서 전역 문맥의 this 가 globalThis 와 같다 할 수 있지만, 런타임 계열(Node.js, Bun)에서는 그렇지 않다는걸 유의하셔야 합니다.
전역 문맥의 this
함수 문맥
일반 함수
기본 함수의 this는 전역 문맥과 동일하게 동작합니다.
일반 함수 예제
문맥을 다른 문맥으로 넘기려면 Function.prototype.call, Function.prototype.apply 메서드를 사용하시면 됩니다. 매개변수 전달 방식의 차이만 있을 뿐 두개의 사용법과 기능은 거의 동일합니다.
call, apply 예제
두 메서드는 실행할 때만 문맥을 바꿔 동작을 합니다. 하지만 함수를 일급 함수를 사용하다 보면, 함수의 문맥을 고정할 필요가 있습니다. 이를 위해 Function.prototype.bind 메서드가 ES5에 도입되었습니다.
bind 예제
엄격 모드
기본 상태의 JS는 대부분의 에러를 알려주지 않은채로 넘어가게 되면서 프로그래머가 이상한 버그들을 만들어 낼 수 있습니다. 이를 느슨한 모드(Sloppy mode)라 부르고, 이를 해제하기 위해 엄격한 모드(Strict mode)가 만들어졌습니다. 이 엄격한 모드는 파일 최상단 혹은 적용할 함수의 시작점에 "use strict";를 작성하면 되고, 런타임 계열 환경이나 export default 구문을 사용할 수 있는 모듈 방식의 파일에선 기본적으로 엄격모드로 작동됩니다.
엄격 모드 사용법
일반 함수의 this 처럼 사용하게 되면, this를 할당하는 비용문제가 발생할 뿐만 아니라, 전역 객체를 쉽게 노출하는 것이 보안상 위험한 방식입니다. 이러한 문제 때문에 엄격 모드에서는 기본적으로 아무것도 할당하지 않아 undefined 가 됩니다.
엄격 모드의 함수에서 this
객체의 메서드
함수를 객체의 메서드로 사용하게 되면 this는 해당 객체가 됩니다.
객체의 메서드 예제
또한 프로토타입 체인의 메서드 또한 만들어진 객체로 동작됩니다.
프로토타입 체인의 메서드
화살표 함수
ES6에 추가된 화살표 함수는 함수가 만들어질때 문맥을 고정하게 되며 이후 바꾸는 것이 불가능합니다.
화살표 함수 예제
이러한 특성 때문에 call, apply, bind 를 통한 문맥 변경은 무시한채 동작하게 됩니다.
화살표 함수 문맥 변경 시도 예제
클래스
클래스의 메서드에서 사용하는 this는 클래스의 객체를 할당 받게됩니다. 다른 객체지향 언어에서 사용하던 방식과 동일한 방식입니다.
클래스 예제
정적 메서드 안에서는 클래스 자체가 할당됩니다. 참고로 객체의 constructor 속성에는 원본 클래스가 할당되어있어, 객체를 통해 정적 메서드를 호출 할 수 있습니다.
클래스 정적 메서드 예제
DOM 이벤트 리스너
브라우저 API 중 하나인 addEventListener 메서드에 할당되는 메서드는 콜백 함수로서 다른 문맥을 지니기 때문에 this 를 이벤트를 발생한 요소로 지정합니다. 물론 화살표 함수로 할당하면 this 는 만들어질 때의 문맥 기준의 this 입니다.
이벤트 리스너 예제
여담
React 클래스 컴포넌트
간혹 React 클래스 컴포넌트 예제를 보면 생성자에서 메서드를 바인딩하거나 화살표 함수로 작성하는 모습을 보실 수 있습니다. 이는 React의 JSX를 이용해서 클래스 컴포넌트를 렌더링할 때 나타나는 문제인데, 클래스 컴포넌트의 생성자 및 라이프 사이클 메서드를 제외한 메서드가 this 를 undefined 로 나타나는 문제가 존재합니다. 바인딩 코드를 제외하고 일반 new 키워드를 통해 생성해보시면 메서드의 this 가 정상적으로 나타나는 것을 확인 하실 수 있습니다.
리액트 클래스 컴포넌트 예제
마무리
this 키워드의 상황별 결과값에 대해 알아봤습니다. 위 특성들은 모두 실제 프로덕트에서 종종 사용되기도 하는 내용 들이고, npm에 등록된 패키지들에서 이러한 내용을 활용한 코드나 API를 확인하실 수 있습니다. JS 개발자라면 이 특성들을 숙지하시는 것이 보다 효율적이고 유지보수가 용이한 코드를 작성하실 수 있습니다.