[Javascript] this
나는 C를 거쳐 C++로 처음 객체 지향을 접했다. C++에서의 this
는 내부 멤버 변수를 스스로 참조하는 경우에 사용하는 포인터였고, 다른 언어에서도 대부분 그렇게 쓰였다. 파이썬에서는 self
로 쓰인다. 그래서 javascript 코드의 this
를 보고 똑같겠지~ 라고 생각했었다.
자바스크립트에서 this는 다른 언어들과 다르게 좀 더 많은 동작을 수행하니, 원리를 이해해보자.
this 키워드
자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(
self-referencing variable
)이다.this
를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.
this 가 필요한 이유
this
를 배우기 전에, 왜 등장했는지부터 살펴보자. 객체지향 프로그래밍에서, 객체는 상태와 동작을 나타내는 프로퍼티와 메서드를 하나로 묶은 자료구조이다. 이 자료구조에서, 메서드는 자신이 속한 객체의 프로퍼티를 참조하고 변경할 수 있어야 한다. 그러려면, 프로퍼티 “식별자”를 참조할 수 있어야 한다. 이게 this
가 필요한 이유이다.
자바스크립트에선 객체를 리터럴 방식, 생성자 함수 방식으로 생성할 수 있다. 리터럴 방식으로 생성하면 재귀적으로 스스로 참조가 가능한데, 생성자 함수 방식으로 선언하면 프로퍼티를 참조할 방법이 없다. 왜냐하면, 생성자 함수를 정의하는 시점에는 인스턴스를 생성하기 이전이므로 생성자 함수가 생성할 인스턴스를 가리키는 식별자를 알 수 없다. 따라서 자바스크립트에서 this
라는 식별자를 제공하는 것이다.
this 바인딩
바인딩이란, 식별자와 값(메모리에 있는 것)을 연결하는 과정.
this
바인딩은this
와this
가 가리킬 객체를 연결하는 과정이다.
this
는 엔진에 의해 암묵적으로 생성되며 코드 어디서든 참조할 수 있다. 함수를 호출하면 함수 내부에 this
가 암묵적으로 전달된다.
하지만, this
가 가리키는 값은 함수 호출 방식에 의해 동적으로 결정된다. 즉, this
바인딩은 동적으로 결정된다.
함수 호출 방식에 따른 this 바인딩
- 일반 함수 호출:
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를 가리킨다!
- 메서드 호출:
this
→ 메서드를 호출한 객체- 메서드를 호출한 객체에 바인딩 된다.
- 메서드는 객체에 포함된 것이 아니라 별도로 존재하는 객체.
- 생성자 함수 호출:
this
→ 생성자 함수가 생성한 인스턴스- new로 호출하면 인스턴스를 생성하게 되고, 이때 this가 같이 바인딩 된다.
- new없이 호출하면 그냥 1번 과 동일하게 일반함수로 호출되어 전역 객체로 바인딩 된다.
Function.prototype.bind
로 바인딩bind
는this
를 전달한다.-
bind
는 메서드와 중첩함수, 콜백함수의this
가 불일치 하는 문제를 해결하기 위해 유용하게 사용된다. 앞서 화살표 함수로 고쳤던 코드를.bind
를 통해 고쳐보자.var value = 1; const obj = { value: 100, foo() { setTimeout(function() { console.log(this.value); }.bind(this), 0); } }; obj.foo(); // 100