이번 장에서는 this에 대해 살펴봅니다.
를 중심으로 살펴봅니다.
this는 실행컨텍스트가 생성될 때 함께 결정됩니다.
이전 장에서 보았듯이 생성 컨텍스트는 함수를 호출할 때 생성되기 때문에, 결국 this는 함수를 호출할 때 결정됩니다.
전역 공간, 메서드 내부, 함수 내부, 콜백 함수 내부, 생성자 함수 내부에서의 this를 차례로 살펴보겠습니다.
전역 공간의 this는 전역 객체
를 기킵니다.
전역 객체는 런타임 환경에 따라 다릅니다.
사실 자바스크립트의 모든 변수는 특정 객체의 프로퍼티로 동작하며, 특정 함수 호출 시 LE(Lexical Environment)를 조회하여 일치하는 프로퍼티를 반홥합니다.
전역변수를 선언하면 자바스크립트 엔진은 전역객체의 프로퍼티로 할당하기 때문에 전역 변수인 a를 window.a 와 this.a로 호출했을 때 같은 결과를 볼 수 있습니다. 그냥 a만 작성하였을 때도 같은 결과를 볼 수 있는 까닭은, 스코프 체인에서 a를 찾다가 마지막 전역 LE (전역 객체)에서 프로퍼티 a를 발견해서 반환하기 때문입니다. 이 경우 생략되었다고 볼 수 있습니다.
전역 객체의 프로퍼티로 할당한 경우 삭제가 되지만, 전역 변수로 선언한 경우는 삭제가 되지 않습니다. 이를 통해 사용자가 삭제 하는 것을 방지할 수 있습니다. 전역 변수 선언시 전역객체 프로퍼티로 선언하며 configurable: false
로 지정해주기 때문입니다. 이를 통해 변경 및 삭제를 막을 수 있습니다.
함수와 메서드는 모두 미리 정의한 동작을 수행하는 코드 묶음입니다. 이 둘의 구분은 독립성
으로 확인할 수 있습니다.
코드로 살펴보면 다음과 같습니다.
// 함수
var func = function(x) {
console.log(this, x);
}
// 함수 호출
func(1);
// 메서드
var obj = {
method: func
};
// 메서드 호출
obj.method(2);
즉, 메서드는 객체의 메서드로서 호출할 경우 메서드로 동작하고 있습니다. 그렇지 않을 경우 일반 함수(func)으로 동작하는 것을 확인할 수 있습니다.
함수 내부에서의 this
함수로 호출할 떄는 this가 지정이 되지 않습니다. 호출 주체(여기서는 객체)에 대한 정보가 this에 담기기 때문입니다. this가 지정되지 않은 경우에는 전역 객체를 가리키기 때문에 함수에서의 this는 전역 객체를 가리키게 됩니다.
메서드 내부함수에서의 this
호출한 함수 프로퍼티 앞의 객체를 가리킵니다.
(점 표기법의 경우 마지막 점 바로 앞의 명시된 객체를 의미합니다.)
메서드 내부 함수에서의 this를 우회하는 방법
JS의 설계 때문에 우회적으로 변수를 활용해 this를 사용할 수 있습니다.
var obj = {
var outer : function () {
var self = this; // _, _this, that 등이 변수로 쓰임
var innerFunc = function () {
console.log(this); // Window {..} 전역
console.log(self); // outer: f
}
}
}
this를 바인딩하지 않는 화살표 함수
ES6에서는 함수 내부의 this가 전역개체를 바라보는 것을 해결하기 위해 this를 바인딩하지 않는 화살표 함수가 나왔습니다. 화살표 함수는 this 바인딩 과정이 빠져, 가장 가까운 상위 스코프의 this를 활용할 수 있습니다.
콜백 함수란?
함수의 제어권을 다른 함수(또는 메서드)에 넘겨주는 경우.
제어권을 넘겨준 함수를 콜백 함수라고 합니다.
콜백함수에서는 제어권을 가지는 함수가 콜백 함수에서의 this를 결정하며, 만약 지정하지 않은 경우에는 기본적인 함수와 마찬가지로 전역 객체를 바라보게 됩니다.
생성자 함수란?
공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수
생성자 함수 내부의 this는, 각 인스턴스 객체를 가리킵니다.
이 외에도 call, apply, bind 함수를 이용하여 명시적으로 대상을 바인딩 할 수 있습니다.
차례대로 살펴보겠습니다.
Function.prototype.call(thisArg[, arg1[,...]])
메서드의 호출 주체인 함수를 즉시 실행합니다.
call 메서드의 첫번째 인자를 this로 바인딩하고, 이후가 호출할 함수의 매개변수가 됩니다.
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
Function.prototype.apply(thisArg[, arg1[,...]])
call 메서드와 기능적으로 동일하나, 두 번째 인자를 배열로 받아 매개변수로 지정한다는 점에서 차이가 있습니다.
var func = function (a, b, c) {
console.log(this, a, b, c);
};
func.apply({x: 1}, [4, 5, 6]); // {x: 1} 4 5 6
유사 배열객체(array-like object)에 배열 메서드 적용
var obj = {
0: 'a',
1: 'b',
2: 'C',
length: 3
};
Array.prototype.push.call(obj, 'd'); // 3 자리에 d들어감
var arr = Array.prototype.slice.call(obj); // 얕은 복사 - ['a', 'b', 'c', 'd'];
arguments, NodeList에 배열 메서드 적용
문자열에 배열 메서드 적용
Array.from 메서드
유사배열객체 또는 순회 가능한 모든 종류의 데이터 타입을 배열로 전환할 수 있습니다.
생성자 내부에서 다른 생성자 호출
여러 인수를 묶어 하나의 배열로 전달하고 싶을 때
Function.prototype.bind(thisArg, [, arg1,...]])
ES5에 추가된 기능으로, call과 비슷하지만 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하는 메서드입니다.
var func = function (a, b, c, d) {
console.log(this, a, b, c, d);
};
func(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
console.log(func.name); // func
console.log(bindFunc2.name); // bound func
콜백함수와 함께 thisArg를 인자로 받는 메서드는 다음과 같습니다.