this 참조는 자바스크립트 코드의 어디서든 사용할 수 있는 읽기 전용 변수입니다. 자바나 C++에도 이것과 역할이 비슷한 this 참조가 있습니다. 자바나 C++의 this 참조는 메서드에 암묵적으로 전달하는 인자라고 보는 것이 적절하지만 자바스크립트의 경우에는 탑 레벨 코드(함수 밖)에서도 this 참조를 사용할 수 있으므로 언제나 사용할 수 있는 읽기전용 변수로 보는 것이 맞습니다.
this 참조는 객체를 참조합니다. 참조하는 곳은 탑 레벨 코드와 함수 안에서 다릅니다. 또한 함수 안에서도 해당 함수를 어떻게 호출하느냐에 따라 참조할 객체가 바뀝니다. this 참조는 코드의 컨텍스트에 따라 자동으로 참조할 객체가 변하는 특별한 것으로 생각할 수 있습니다.
5-14-1 this 참조의 규칙
다음은 this 참조의 규칙을 정리한 내용입니다.
- 탑 레벨 코드의 this 참조는 전역 객체를 참조
- 함수 안의 this 참조는 함수의 호출 방법에서 다르다(표 5.3 참조)
함수 안의 this 참조에서 참조할 객체는 함수를 작성하는 방법이나 선언하는 방법으로 바뀌는 것이 아니라 함수를 호출하는 방법에 따라 바뀌는 것임에 주의해야 합니다. 즉, 같은 함수라도 호출 방법이 다르면 this 참조는 다른 객체를 참조합니다.
표 5.3 함수 안의 this 참조
함수 호출 방법 | this 참조의 참조하는 객체 |
---|---|
생성자 호출 | 생성한 객체 |
메서드 호출 | 리시버 객체 |
apply 또는 call 호출 | apply 또는 call의 인자로 지정한 객체 |
그 외의 호출 | 전역 객체 |
생성자를 호출했을 때 this 참조는 생성자가 생성하는 객체를 참조합니다. 자세한 내용은 ‘5-7-2 생성자와 new 식’을 참고합니다.
표 5.3의 메서드 호출의 설명에 나오는 리시버 객체는 다음과 같은 객체입니다.
- 점 연산 또는 대괄호 연산에서 객체의 메서드를 호출했을 때 연산자의 좌변에 지정한 객체
앞 절에서 설명한 것처럼 메서드란 객체의 프로퍼티가 참조하는 함수입니다. 메서드와 리시버 객체의 구체적인 예를 아래에서 보겠습니다.
// 객체 정의
js> var obj = {
x:3,
doit: function() { print('method is called. ' + this.x ); }
};
js> obj.doit(); // 객체 obj가 리시버 객체. doit이 메서드
method is called. 3
js> obj['doit'](); // 객체 obj가 리시버 객체. doit이 메서드
method is called. 3
위 예제의 동작 방식을 설명하겠습니다. 처음에 객체의 참조를 변수 obj에 대입합니다. 이 객체는 두 개의 프로퍼티를 가집니다. 프로퍼티 x의 값은 숫자 3으로, 프로퍼티 doit의 값은 함수입니다. 이 함수를 메서드 doit이라고 하겠습니다.
obj에 점 연산자 또는 대괄호 연산자로 메서드 doit을 호출할 수 있습니다. 이때 메서드 호출 대상인 객체를 리시버 객체라고 합니다(즉 변수 obj가 참조하는 객체가 리시버 객체입니다). 호출된 메서드 안의 this 참조는 리시버 객체를 참조합니다.
이 this 참조의 동작 방식에 의해 자바나 C++의 메서드와 비슷하게 동작하게 됩니다. 단 미묘하게 다른 식으로도 동작하므로 다음 절에서 좀 더 자세히 설명하겠습니다. apply와 call의 자세한 사항은 나중에 ‘5-15 apply와 call’에서 설명하겠습니다. 이것들은 리시버 객체를 명시적으로 지정하는 데 사용할 수 있습니다.
5-14-2 this 참조의 주의점
앞에서 설명한 메서드 호출 시의 this 참조의 동작 방식은 자바나 C++와 같아 보이지만 조금 다르므로 주의할 필요가 있습니다. 자바와 같은 클래스 기반 언어에서는 메서드 안의 this가 참조하는 리시버 객체는 항상 해당 클래스의 인스턴스입니다. 자바스크립트에서는 그와 같은 보장을 하지 않으므로 주의해야 합니다.
자바스크립트의 this 참조가 참조할 곳은 메서드의 호출 방법에 따라 달라집니다. 자의적인 예이긴 하지만 아래와 같이 리시버 객체가 없거나 다른 리시버 객체를 통해 같은 함수를 호출하면 동작 방식이 바뀝니다.
js> var obj = {
x:3,
doit:function() { print('method is called. ' + this.x ); }
};
js> var fn = obj.doit; // obj.doit이 참조하는 함수 객체를 전역 함수 fn에 대입
js> fn(); // 함수 안의 this 참조는 전역 객체를 참조
method is called. undefined
js> var x = 5; // this 참조가 전역 객체를 참조하고 있는지 확인
js> fn();
method is called. 5
js> var obj2 = { x:4, doit2:fn }; // 다른 객체 obj2의 프로퍼티에 obj의 메서드
// (함수 객체의 참조)를 대입
js> obj2.doit2(); // 메서드 안의 this 참조는 객체 obj2를 참조
method is called. 4
자바에서는 메서드 안에서 this 작성을 생략할 수 있습니다. 메서드 안의 이름을 해석할 때 같은 클래스의 필드나 메서드를 찾기 때문입니다. 자바스크립트에서는 this 작성을 같은 맥락으로 생략할 수 없습니다. 위 예제에서 this.x 대신 x이라고 쓰면 그것은 전역 변수 x를 의미합니다.
메서드 안에서 다른 메서드를 호출하는 경우
유사한 예를 들자면 메서드 안에서 다른 메서드를 호출하는 경우에도 마찬가지로 주의할 필요가 있습니다. 일반적으로 자바나 C++에서는 메서드 안에서 다른 메서드를 호출할 때 this를 생략합니다. 하지만 자바스크립트에서는 메서드 안에서 별도의 메서드를 호출하려면 아래 예제와 같이 this 참조를 통해 호출해야 합니다.
// doit 메서드 안에서 doit2 메서드를 호출할 때 this.doit2()처럼 this 참조를 통해 호출해야 함
js> var obj = {
x:3,
doit: function() { print('doit is called. ' + this.x ); this.doit2(); },
doit2: function() { print('doit2 is called. ' + this.x); }
};
js> obj.doit();
doit is called. 3
doit2 is called. 3
위 예제에서는 this.doit2()를 doit2()라고 쓰면 전역 함수인 doit2를 찾습니다. 문법적으로 정확하게 표현하면 함수가 중첩되면 바깥 쪽 스코프를 향해 이름을 찾습니다(‘5-4 변수명의 해석’을 참조).
- 이미지 출처: Dmitry Baranovskiy / CC BY 2.0