책 [코어 자바스크립트]를 기반으로 작성하였습니다.
안녕하세요. 휴먼스케이프 loowin입니다. 😃
오늘은 자바스크립트에서 많은 사람들이 혼란스러워하는 this에 대해 알아보겠습니다.
상황에 따라 달라지는 this
다른 대부분의 객체지향 언어에서 this는 클래스로 생성한 인스턴스 객체를 의미합니다. 클래스에서만 사용할 수 있기 때문에 혼란의 여지가 없거나 많지 않습니다. 그러나 자바스크립트에서의 this는 어디서든 사용할 수 있습니다. 상황에 따라 this가 바라보는 대상이 달라집니다.
함수와 객체의 구분이 느슨한 자바스크립트에서 this는 실질적으로 이 둘을 구분하는 거의 유일한 기능입니다.
1. 상황에 따라 달라지는 this
자바스크립트 에서 this는 함수를 호출할 때 결정된다고 할 수 있습니다. 함수를 어떤 방식으로 호출하느냐에 따라 값이 달라집니다.
1–1) 전역공간에서의 this
전역 컨텍스트를 생성하는 주체가 전역 객체이기 때문에 전역 공간에서 this는 전역 객체를 가르킵니다.
전역 공간에서의 this(브라우저 환경)
console.log(this); // {alert: f(), atob: f(), blur: f(), ... } console.log(window); // {alert: f(), atob: f(), blur: f(), ... } console.log(this === window); //true
this와 큰 관련은 없지만 전역 공간을 다루는 김에 잠시 전역 공간에서만 발생하는 특이한 성질 하나를 살펴보고 넘어가겠습니다. 전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로도 할당합니다. 변수이면서 객체의 프로퍼티이기도한 셈입니다.
전역변수와 전역객체
var a = 1; console.log(a); //1 console.log(window.a); //1 console.log(this.a); //1
자바스크립트의 모든 변수는 실은 특정 객체의 프로퍼티로서 동작합니다. 전역 변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당합니다.
1–2) 메서드로서 호출할 때 그 메서드 내부에서의 this
어떤 함수를 실행하는 방법을 여러 가지가 있는데, 가장 일반적인 방법 두 가지는 함수로서 호출하는 경우와 메서드로서 호출하는 경우입니다. 프로그래밍 언어에서 함수와 메서드는 미리 정의한 동작을 수행하는 코드 뭉치로, 이 둘을 구분하는 유일한 차이는 독립성에 있습니다. 함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행합니다.
함수로서 호출, 메서드로서 호출
var func = function (x) { console.log(this, x); }; func(1); // Window {...} 1
var obj = { method: func }; obj.method(2); // { method: f } 2
이 둘을 구분하는 쉬운 방법은 함수 앞에 점(.)이 있는지 여부만으로 간단하게 구분할 수 있습니다.
1–3) 함수로서 호출할 때 그 함수 내부에서의 this
함수 내부에서의 this
어떤 함수를 함수로서 호출할 경우에는 this가 지정되지 않습니다. this에는 호출한 주제에 대한 정보가 담긴다고 했습니다. 그런데 함수로서 호출하는 것은 호출 주체를 명시하지 않기 때문에 호출 주체의 정보를 알 수 없는 것입니다.
this가 지정되지 않은 경우 this는 전역 객체를 바라본다고 하였기에 함수에서의 this는 전역 객체를 가리킵니다.
메서드 내부에서의 this
this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지 등)은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지 없는지가 관건인 것입니다.
[퀴즈 낼지말지 고민]
1–4) 콜백 함수 호출 시 그 함수 내부에서의 this
콜백 함수의 정의와 동작 원리는 다음 글에서 자세히 다루도록 하고 여기서는 this가 어떤 값을 참조하는지만 간단히 확인해 보겠습니다.
콜백 함수도 함수이기 때문에 기본적으로 this가 전역객체를 참조하지만, 제어권을 받은 함수에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조하게 됩니다.
1–5) 생성자 함수 내부에서의 this
생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는데 사용하는 함수입니다. 객체지향 언어에서는 생성자를 클래스, 클래스를 통해 만든 객체를 인스턴스라고 합니다.
생성자 함수
var Cat = function (name, age) { this.bark = '야옹'; this.name = name; this.age = age; }; var choco = new Cat('초코', 7); var nabi = new Cat('나비', 5); console.log(choco, nabi);
/* 결과 Cat { bark: ‘야옹’, name: ‘초코’, age: 7}; Cat { bark: ‘야옹’, name: '나비', age: 5}; */
어떤 함수가 생성자 함수로서 호출된 경우 내부에서의 this는 곧 새로 만들 구체적인 인스턴스 자신이 됩니다.
2. 명시적으로 this를 바인딩하는 방법
상황별로 this에 어떤 값이 바인딩 되는지를 살펴봤지만 이러한 규칙을 깨고 this에 별도의 대상을 바인딩하는 방법도 있습니다. 위의 설명한 규칙에 부합하지 않는다면 다음 방법 중 하나를 사용했을 것이라고 추측할 수 있습니다.
2–1) call 메서드
call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령입니다. 이때 call 메서드의 첫번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로합니다. 함수를 그냥 실행하면 this는 전역객체를 참조하지만 call 메서드를 이용하면 임의의 객체를 this로 지정할 수 있습니다.
call 메서드
var func = function(a,b,c) { console.log(this, a, b, c); };
func(1,2,3); // Window{ ... } 1 2 3 func.call({ x:1 }, 4,5,6); // { x:1 } 4 5 6
메서드에 대해서도 마찬가지로 객체의 메서드를 그냥 호출하면 this는 객체를 참조하지만 call메서드를 이용하면 임의의 객체를 this로 지정할 수 있습니다.
call 메서드(2)
var obj = { a: 1, method: function (x,y) { console.log(this.a, x, y); } };
obj.method(2, 3); // 1 2 3 obj.method.call({ a:4 }, 5, 6); // 4 5 6
call 메서드란?https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/call
2–2) apply 메서드
apply 메서드는 call 메서드와 기능적으로 완전히 동일합니다. call 메서드는 첫 번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정하는 반면, apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점에서만 차이가 있습니다.
apply 메서드
var func = function(a,b,c) { console.log(this, a, b, c); };
func.apply({x:1}, [4,5,6]); //{x:1} 4 5 6
var obj = { a:1, method: function(x,y) { console.log(this.a, x, y); } };
obj.method.apply({a:4}, [5,6]); //4 5 6
apply 메서드란?https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
2–3) bind 메서드
bind 메서드는 call 메서드와 비슷하지만 즉시 호출하지는 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드입니다. 다시 새로운 함수를 호출할 때 인수를 넘기면 그 인수들은 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록됩니다. bind 메서드는 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적을 모두 지닙니다.
bind 메서드
var func = function(a,b,c,d) { console.log(this, a, b, c, d); }; func(1,2,3,4); // Window{ ... } 1 2 3 4
var bindFunc1 = func.bind({x:1}); bindFunc1(5, 6, 7, 8); // { x:1 } 5 6 7 8
var bindFunc2 = func.bind({x:1}, 4, 5); bindFunc2(6, 7); // { x: 1 } 4 5 6 7 bindFunc2(8, 9); // { x: 1 } 4 5 8 9
bind 메서드란?https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
3. 정리
3–1) 명시적 this 바인딩이 없는 한 늘 성립하는 규칙입니다.
전역공간에서의 this는 전역객체를 참조합니다.
어떤 함수를 메서드로 호출한 경우 this는 메서드 호출 주체를 참조합니다.
어떤 함수를 함수로서 호출한 경우 this는 전역객체를 참조합니다. 메서드 내부함수에서도 같습니다.
생성자 함수에서의 this는 생성될 인스턴스를 참조합니다.
3–2) 명시적 this 바인딩입니다.
call, apply 메서드는 this를 명시적으로 지정하면서 함수나 메서드를 호출합니다.
bind 메서드는 this 및 함수에 넘길 인수를 일부 지정하여 새로운 함수를 만듭니다.
요소를 순회하며 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this를 받기도 합니다.
마무리
이상 javascript의 this에 대해 알아보았습니다. 감사합니다.
Get to know us better! Join our official channels below.
Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io