본문 바로가기

FrontEnd/JavaScript

자바스크립트 "this" 동적 컨텍스트 결정을 이해하자

자바스크립트에서 this는 매우 특이하면서도 유연한 키워드입니다. 코드 내에서 this를 사용하면, 실행 중인 컨텍스트에 따라 참조하는 값이 동적으로 결정됩니다. 이를 제대로 이해하고 활용하기 위해서는 this가 어떻게 동작하는지를 이해하는 것이 중요합니다.

 

자바스크립트 this는 특정 상황마다 언제 호출되었는지, 어떻게 호출 되었는지에 따라서 매번 달라지게 됩니다.

보통 5가지 경우로 나누어 지게 되면서 아래 예제를 보면서 확인해 보겠습니다.

1. 기본 바인딩 

첫 번째로 this의 기본 바인딩 입니다.
전역상태에서 this를 호출하거나 일반 함수 내에서 this를 호출하면 해당 this는 전역 객체를 가리키게 됩니다.

 

function Hello() {
  console.log(this);
}

 

위에 예제는 일반 함수에서 this를 호출하고 있습니다. 
이렇게 일반 함수 내에서 호출했을 경우 this는 아래와 같은 결과를 나타냅니다.

 

브라우저

 

Nodejs

 

브라우저에서는 window, nodejs에서는 global 객체를 가리키는 걸 확인할 수 있습니다.

 

2.  암시적 바인딩

두 번째로 암시적 바인딩입니다.

암시적 바인딩 같은 경우는 객체 안에 메서드에서 this를 호출했을 때 상황입니다. 

아래 예제로 확인해 보겠습니다.

 

const Hello = {
  name: "환민",
  printThis() {
    return this;
  },
};

console.log(Hello.printThis());

 

 

위에 처럼 객체 안에서 printThis라는 함수에서 this를 호출하고 있습니다.

이렇게 호출했을 시 결과는 아래와 같습니다.

 

{ name: '환민', printThis: [Function: printThis] }

 

즉 객체 메서드로 this를 호출 시 해당 객체를 나타내는 걸 확인할 수 있습니다.

 

3.  명시적 바인딩

세 번째로 명시적 바인딩입니다.
명시적 바인딩이란 call, apply, bind 메서드를 통해서 this를 특정 값에 명시할 수 있는 것을 말합니다.

아래 예제로 확인해 보겠습니다.

function Hello() {
  return ` Hello ${this.name}`;
}

const obj = {
  name: "환민",
};

const bindHello = Hello.bind(obj);

console.log(bindHello());

 

 

Hello라는 함수는 this.name는 값에 접근하고 있습니다. 

하지만 이렇게 호출했을 경우 위의 예제인 1번 기본 바인딩에 의해서 this는 전역 객체를 나타내게 됩니다.

이때 사용할 수 있는 방법이 명시적 바인딩입니다. 즉 해당 this가 어떤 값을 나타내야 하는지 알려주는 방법입니다.

 

위에 예제에서는 bind라는 메서드를 사용해서 해당 Hello라는 함수가 obj를 참조할 수 있도록 했습니다. 

bind는 call, apply랑 다르게 해당 바인드된 함수를 return 해주기 때문에 return 값을 받아서 그 함수를 실행하면 결과는 아래와 같습니다.

Hello 환민

 

4.  생성자 함수 바인딩

생성자 함수 바인딩이란 클래스 혹은 생성자 함수에서 this는 생성되는 인스턴스를 나타내게 됩니다.

 

class Hello {
  constructor(name) {
    this.name = name;
  }

  printName() {
    return `hello ${this.name}`;
  }
}

const hwanmin = new Hello("hwanmin");

console.log(hwanmin.printName());


위에 예시 코드를 보면 Hello라는 클래스는 printName이라는 메서드를 가지고 있고 이 메서드는 this에 접근해서 name이라는 값을 출력하게 됩니다. 이때 this는 고정된 것이 아닌 항상 유동적으로 변하게 됩니다. 왜냐하면 해당 인스턴스는 하나로 정해진게 아닌 생성자 값을 받아서 많은 인스턴스를 가지게 되는데 이 때 this는 각 인스턴스를 가리키게 됩니다.

 

즉 결과는 아래와 같이 나옵니다.

 

hello hwanmin

 

5.  화살표 함수

화살표 함수 같은 경우 "렉시컬 this"를 가진다는 특징이 있습니다. 

이 말이 무엇이냐면 화살표 함수는 자신만의 this를 가지지 않습니다. 대신 화살표 함수가 선언될 때의 외부 스코프의 this를 가르키게 됩니다.


예제 코드를 통해 확인할 수 있습니다. 

const obj = {
  name: "외부환민",
  method: function () {
    const innerFunction = {
      name: "내부환민",

      printName: function () {
        return this.name;
      },

      printArrowName: () => {
        return this.name;
      },
    };

    return {
      method: innerFunction.printName(),
      arrowFunction: innerFunction.printArrowName(),
    };
  },
};

console.log(obj.method());

 

printName은 객체의 메서드로 호출되었을 때 상황이고 printArrorName은 화살표 함수로 호출되었을 때 상황입니다.

 

printName은 메서드이기 때문에 해당 스코프 내의 this인 "내부환민"을 참조하게 됩니다. 

하지만 화살표 함수인 printArrorName 같은 경우 현재 자신만의 this를 가지지 않고 그 상위 스코프의 this를 가지게 됩니다.
즉 상위에 있는 "외부환민"을 나타내게 됩니다. 

결과는 아래와 같습니다.

{ method: '내부환민', arrowFunction: '외부환민' }


즉 이렇듯 자바스크립트에서 this는 어떻게 선언되었는지 어떻게 호출되는지에 따라서 매번 용법이 바뀌기 때문에 내가 호출하는 this가 어떤 케이스에 해당해서 호출되는지 명확하게 알아야 합니다.