본문 바로가기
이론/Frontend

JavaScript의 this (this of JavaScript)

by 유세지 2021. 7. 21.

현재 사용되고 있는 많은 언어들은 this 키워드를 제공하고 있습니다. 자바스크립트에서의 this는 상황에 따라 다른것을 가리키기 때문에 편리하지만 정확히 무엇을 가리키고 있는 것인지 쉽게 헷갈리는 식별자 중 하나입니다. 오늘은 이 this에 대해서 알아보겠습니다.

 

 

this는 어디에서 오는가?

코드 곳곳에서 등장하는 this는 보통 함수의 스코프를 가지고 있다고 생각하기 마련입니다. 그도 그럴것이, this 키워드를 사용하면 함수 내부 변수를 조작하거나 함수를 실행시키는등 스코프에서 할 수 있는 작업들을 할 수 있기 때문입니다. 그렇다면 자바스크립트의 this는 사용될때마다 동적으로 함수의 스코프가 바인딩된다고 생각하면 될까요? 그 전에 먼저 this가 어디에서 오는것인지 알 필요가 있습니다.

 

우리는 this라는 키워드를 따로 선언해준적이 없습니다. 그런데도 어디에서 사용하는지에 따라 다른 값을 갖고 있다는게 참 신기합니다. this 대체 어디에 저장되어 있는걸까요?

 

정답은 실행 컨텍스트 객체입니다. 자바스크립트 엔진이 코드를 실행하기위해 객체 단위로 나누어 스택에 올린다는건 알고 계실겁니다. 이 객체 안에는 변수나 함수 선언과 같은 변수 객체, 해당 스코프에서 참조할 수 있는 변수 등의 정보가 담긴 스코프 체인이 담겨있는데 this 에 대한 정보도 이 실행 컨텍스트 객체 안에 저장됩니다.

 

실행 콘텍스트 객체의 구조

 

당연하게도, 실행 콘텍스트 객체의 this 값은 어떻게 호출되는지에 따라 다른 값이 들어가게 됩니다. 호출에 따른 this가 어떻게 달라지는지 확인해보겠습니다.

 

 

 

호출에 따른 this 값

첫 번째 케이스는 기본 바인딩입니다. 다른 규칙들이 적용되지 않는 경우 switch~case 문의 default 와 같이 바인딩되는 가장 기본적인 this입니다. 

 

function defaultBinding() {
	console.log(this.a);
}

var a = 1;
defaultBinding();

 

defaultBinding() 함수가 실행된 위치(전역)에 그대로 바인딩 된 this입니다. 다른 규칙을 생각 할 것 없이 명백하게 전역에 바인딩이 이루어진 모습입니다. 우리가 아는 this의 가장 흔한 형태입니다.

 

한 가지 주의할 점은 위와 같은 코드가 엄격 모드에서 적용될 경우, 전역에 바인딩 될 수 없기 때문에 typeError를 내며 동작하지 않을 것입니다. 바인딩 전 실행 환경을 항상 잘 파악합시다.

 

 

 

두 번째 케이스는 this의 호출부에 콘텍스트 객체가 존재하는지의 여부를 먼저 확인하는 규칙입니다. 먼저 암시적 바인딩의 경우입니다. 

 

function implicitBinding() {
	console.log(this.a);
}

var object = {
    a: 1,
    implicitBinding: implicitBinding
}

object.implicitBinding();

 

위와 같은 상황에서 object는 implicitBinding 함수를 프로퍼티로 참조하고 있습니다. 호출이 이루어지지 않는 평소엔 object 객체가 함수를 레퍼런스로 참조하고 있기 때문에 소유나 포함의 관계가 아닌 인지 관계인 상태이지만, 콘텍스트로 함수를 호출하는 그 시점에서는 포함 관계라고 할 수 있습니다.

 

다만 이런 체이닝 관계가 여러번 중첩되어 있는 경우, 최상위와 최하위 수준에 있는 정보만 호출부와 연결됩니다.

 

function myValue() {
  console.log("my value is : " + this.value);
}

A.B.C.D.myValue(); // my value is : A.value

 

 

세 번째 케이스로 함수 프로퍼티를 이용한 암시적 바인딩을 사용하지 않고도 바인딩이 되는 경우입니다. 이를 명시적 바인딩이라고 합니다. call()과 apply() 메소드를 이용하여 직접 바인딩시켜줍니다.

 

function callFunction() {
  console.log(this.a);
}

var object() {
  a: 1
};

callFunction.call(object); // 1

 

위 코드에서 callFunction이 call() 메소드를 통해 명시적으로 object를 호출하는 모습을 볼 수 있습니다. 이 경우 this는 object에 바인딩 됩니다.

 

 

네 번째 케이스로 new 키워드를 이용한 바인딩이 있습니다. 일반적인 클래스 지향 언어에서의 new와는 의미가 미묘하게 다르지만, 새 객체를 만들때 사용하는 생성자의 역할을 하는 것은 같습니다.

 

function newFunction(num) {
  this.value = num;
}

var newInstance = new newFunction(123);
console.log(newInstance.value); // 123

 

new 키워드를 통해 newFunction을 호출하여 새 객체를 생성했고, 이 객체를 호출하면 this와 새 객체간의 바인딩이 이루어지게 됩니다. 이것을 New-Binding이라고 부릅니다.

 

 

 

정리

지금까지 네 가지 this 바인딩 규칙에 대해 알아보았습니다. 위에서부터 우선 순위가 낮은 순서로 작성했는데, 다시 한 번 정리해보면 아래와 같고, 이를 this의 확정 규칙이라고 부릅니다.

 

 

1. new 키워드로 함수를 호출했다면, 새로 생성된 객체가 this에 바인딩됩니다.

2. call() 또는 apply() 등의 명시적 메서드를 통해 바인딩했다면, 지정된 객체가 this에 바인딩됩니다.

3. 함수가 객체를 포함하는 컨텍스트 형태로 호출했다면, 암시적 바인딩으로 처리되어 this에 바인딩됩니다.

4. 모든 경우에 해당하지 않으면 this는 기본값인 전역 객체로 세팅 됩니다. (non-strict 모드 한정)

 

4번의 경우 엄격 모드가 적용되고 있다면 예외적으로 undefined가 바인딩 됩니다.

 

오늘은 자바스크립트의 this에 대하여 알아보았습니다. 다음 시간엔 자바스크립트의 객체에 대해 알아보겠습니다. 

댓글