[Javascript] this

나는 C를 거쳐 C++로 처음 객체 지향을 접했다. C++에서의 this는 내부 멤버 변수를 스스로 참조하는 경우에 사용하는 포인터였고, 다른 언어에서도 대부분 그렇게 쓰였다. 파이썬에서는 self로 쓰인다. 그래서 javascript 코드의 this를 보고 똑같겠지~ 라고 생각했었다.

자바스크립트에서 this는 다른 언어들과 다르게 좀 더 많은 동작을 수행하니, 원리를 이해해보자.

this 키워드

자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-referencing variable)이다. this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.

this 가 필요한 이유

this를 배우기 전에, 왜 등장했는지부터 살펴보자. 객체지향 프로그래밍에서, 객체는 상태와 동작을 나타내는 프로퍼티와 메서드를 하나로 묶은 자료구조이다. 이 자료구조에서, 메서드는 자신이 속한 객체의 프로퍼티를 참조하고 변경할 수 있어야 한다. 그러려면, 프로퍼티 “식별자”를 참조할 수 있어야 한다. 이게 this가 필요한 이유이다.

자바스크립트에선 객체를 리터럴 방식, 생성자 함수 방식으로 생성할 수 있다. 리터럴 방식으로 생성하면 재귀적으로 스스로 참조가 가능한데, 생성자 함수 방식으로 선언하면 프로퍼티를 참조할 방법이 없다. 왜냐하면, 생성자 함수를 정의하는 시점에는 인스턴스를 생성하기 이전이므로 생성자 함수가 생성할 인스턴스를 가리키는 식별자를 알 수 없다. 따라서 자바스크립트에서 this 라는 식별자를 제공하는 것이다.

this 바인딩

바인딩이란, 식별자와 값(메모리에 있는 것)을 연결하는 과정. this 바인딩은 thisthis가 가리킬 객체를 연결하는 과정이다.

this는 엔진에 의해 암묵적으로 생성되며 코드 어디서든 참조할 수 있다. 함수를 호출하면 함수 내부에 this가 암묵적으로 전달된다.

하지만, this가 가리키는 값은 함수 호출 방식에 의해 동적으로 결정된다. 즉, this 바인딩은 동적으로 결정된다.

함수 호출 방식에 따른 this 바인딩

  1. 일반 함수 호출: this → 전역 객체
    • 심지어 메서드 안의 일반 함수도 전역 객체를 가리킨다.
    • 콜백도 전역 객체를 가리킨다. 따라서 this가 가리키는 값이 일치하기를 원한다면 .bind를 사용하거나 화살표 함수를 사용해보자.

        var value = 1;
              
        const obj = { 
            value: 100,
            foo() {
                setTimeout(function() {
                    console.log(this.value);
                }, 0);
            }
        };
              
        obj.foo();  // 1
      

      1이 출력된다. 콜백함수도 일반함수로 호출되어 전역객체의 this로 바인딩이 된 것을 확인할 수 있다. 100이 출력되도록 화살표 함수를 이용해서 고쳐보자!

        var value = 1;
              
        const obj = { 
            value: 100,
            foo() {
                setTimeout(() => console.log(this.value), 0);
            }
        };
              
        obj.foo();  // 100
      

      화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다!

  2. 메서드 호출: this → 메서드를 호출한 객체
    • 메서드를 호출한 객체에 바인딩 된다.
    • 메서드는 객체에 포함된 것이 아니라 별도로 존재하는 객체.
  3. 생성자 함수 호출: this → 생성자 함수가 생성한 인스턴스
    • new로 호출하면 인스턴스를 생성하게 되고, 이때 this가 같이 바인딩 된다.
    • new없이 호출하면 그냥 1번 과 동일하게 일반함수로 호출되어 전역 객체로 바인딩 된다.
  4. Function.prototype.bind 로 바인딩
    • bindthis를 전달한다.
    • bind는 메서드와 중첩함수, 콜백함수의 this가 불일치 하는 문제를 해결하기 위해 유용하게 사용된다. 앞서 화살표 함수로 고쳤던 코드를 .bind를 통해 고쳐보자.

        var value = 1;
              
        const obj = { 
            value: 100,
            foo() {
                setTimeout(function() {
                    console.log(this.value);
                }.bind(this), 0);
            }
        };
              
        obj.foo();  // 100