자바스크립트의 THIS

봐도 봐도 이해가 되지 않고 계속해서 헷깔리는 this 에 대하여 정리하고자 한다.

이번 글에서는 기본바인딩 암시적 바인딩 명시적 바인딩 new 바인딩 에 대해 정리할 것이다.

🙄 This 란

This 는 작성 시점이 아닌 런타임 시점에 바인딩 되며 함수 호출 당시 상황에 따라 콘텍스트가 결정된다. 함수 선언 위치와 상관 없이 this 바인딩은 오로지 어떻게 함수를 호출했느냐에 따라 정해진다.

🔨 This 의 기본 규칙

1. 기본바인딩

  • 단독 함수 실행 Standalone Function Invocation 에 관한 규칙이다.

  • this 의 기본 규칙이다.

  • 일반적으로 호출 했을 때 (전역 객체가 바인딩 된다.)

    function foo() {
      console.log(this.a);
    }
    var a = 2;
    foo(); //2
  • 엄격모드 (전역 객체가 기본 바인딩 대상에서 제외된다. 이 때 thisundefined 이다.)

    function foo() {
      "use strict";
      console.log(this.a);
    }
    var a = 2;
    foo(); // 타입 에러 'this'는 'undefined'입니다

2. 암시적 바인딩

  • 호출부에 콘텍스트 객체가 있는지, 객체의 소유/포함 여부를 확인하는 것이다.

  • object에서 호출 (object가 바인딩 된다.)

    function foo() {
      console.log(this.a);
    }
    var obj = {
      a: 2,
      foo: foo,
    };
    obj.foo(); //2
    • 호출부는 obj콘텍스트로 foo() 를 참조하므로 obj 객체는 함수 호출 시점에 함수의 레퍼런스를 '소유'하거나 '포함' 한다고 볼 수 있다.
    • foo() 호출 시 objthis 이니 this.aobj.a 가 된다.
  • 객체 프로퍼티 참조 체이닝 (최상위object가 바인딩)

    function foo() {
      console.log(this.a);
    }
    
    var obj2 = {
      a: 42,
      foo: foo,
    };
    
    var obj1 = {
      a: 2,
      obj2: obj2,
    };
    
    obj1.obj2.foo(); //42
    • 최상위 수준의 정보만 호출부와 연관된다. obj1.obj2.foo() 처럼 체이닝 호출을 할 경우 중간 단계인 obj1.a 값은 무시된다.
  • 암시적 소실 - 암시적으로 바인딩 된 함수에서 바인딩이 소실되는 경우

    function foo() {
      console.log(this.a);
    }
    
    var obj = {
      a: 2,
      foo: foo,
    };
    
    var bar = obj.foo; // 함수 레퍼런스
    var a = "엥, 전역이네!"; //a는 전역객체의 프로퍼티
    bar(); //엥, 전역이네!
    • barfoo 를 가리키는 또 다른 레퍼런스다.
    • 그냥 일반적으로 bar() 를 호출하므로 기본 바인딩이 적용된다.
  • 암시적 소실2 - 콜백함수

    function foo() {
      console.log(this.a);
    }
    
    function doFoo(fn) {
      fn();
    }
    
    var obj = {
      a: 2,
      foo: foo,
    };
    var a = "엥, 전역이네";
    doFoo(obj.foo); //엥, 전역이네
    • 인자로 전달하는 건 일종의 암시적 할당이다. 함수를 인자로 넘기면 암시적으로 레퍼런스가 할당되어 이전 예제와 결과가 같다.

3. 명시적 바인딩

  • call apply : this 에 바인딩 할 객체를 첫째 인자로 받아 함수 호출 시 이 객체를 this 로 세팅한다.

  • 명시적 바인딩 - this 는 반드시 호출 시 넘긴 인자가 된다.

    function foo() {
      console.log(this.a);
    }
    
    var obj = {
      a: 2,
    };
    
    foo.call(obj); //2
  • 하드 바인딩 - this 바인딩이 중간에 소실되거나 프레임워크가 임의로 덮어쓰는 문제 해결

    function foo() {
      console.log(this.a);
    }
    
    var obj = {
      a: 2,
    };
    
    var bar = function () {
      foo.call(obj);
    };
    
    bar();
    setTimeout(bar, 100);
    
    //하드 바인딩된 'bar'에서 재정의된 'this'는 의미없다.
    bar.call(window);
    • 함수 bar() 내부에서 foo.call(obj)foo 를 호출하면서 objthis 에 강제로 바인딩 하도록 하드 코딩한다.
  • 하드 바인딩 bind

    • 하드 바인딩은 매우 자주 쓰는 패턴이라 ES5 내장 유틸리티에 구현되어 있다.
    function foo(something) {
      console.log(this.a, something);
      return this.a + something;
    }
    
    var obj = {
      a: 2,
    };
    
    var bar = foo.bind(obj);
    var b = bar(3); //2 3
    console.log(b); //5
    • bind 는 주어진 this 콘텍스트로 원본 함수를 호출하도록 하드 코딩된 새 함수를 반환한다.

4. new 바인딩

  • 먼저 짚고 넘어갈 점

    • 자바스크립트의 new 연산자는 클래스 지향적인 기능과 아무 상관 없다는 것
    • 자바스크립트의 생성자 는 앞에 new 연산자가 있을 때 호출되는 일반함수다.
    • 클래스에 붙은 것도 아니고 클래스 인스턴스화 기능도 없다. 생성자 함수(Constructor Function) 가 아니라 함수를 생성하는 호출(Construction Calls Of Functions) 이라고 옳다.
  • new 를 붙여 생성자 호출을 했을 때 벌어지는 일

    1. 새 객체가 만들어진다.
    2. 새로 생성된 객체의 [[Prototype]] 이 연결된다.
    3. 새로 생성된 객체는 해당 함수 호출 시 this로 바인딩 된다.
    4. 이 함수가 자신의 또 다른 객체를 반환하지 않는 한 new 와 함께 호출된 함수는 자동으로 새로 생성된 객체를 반환한다.
    function foo(a) {
      this.a = a;
    }
    
    var bar = new foo(2);
    console.log(bar.a); //2
    • new 는 함수 호출 시 this를 새 객체와 바인딩 하는 방법이고, 이것이 new 바인딩이다.

This에 대해 기본적으로 많이 알고 있는 내용들에 대하여 정리해봤다.

예시를 보면서 몇 번이고 되돌아가서 많은 양이 아닌데도 읽는데 꽤 오랜 시간이 걸리는 것 같다.

순수 js를 쓰면서 This 때문에 당황스러웠던 적이 몇 번 있는데, 그 때의 경험이 있어서 조금 더 와닿았다. 다음 번에 읽을 때는 지금보다는 쉽게 읽을 수 있을거라는 기대를.............해본다....

(🍕TIP) new 에 대한 체크는 이런 식으로 할 수 있다..

function Foo() {
  if (!new.target) {
    throw "Foo() 함수는 new 연산자로 호출되어야 합니다!";
  }
}
Foo(); //Foo() 함수는 new 연산자로 호출되어야 합니다!

이 다음에는 this가 바인딩 되는 순서(우선순위), 바인딩 예외 , 어휘적(lexical) this 에 대해 정리할 것이다. 😊

출처

[You don't know JS - this와 객체 프로토 타입, 비동기와 성능] - 카일심슨