기본적으로 this
에는 전역 객체가 바인딩된다.
브라우저 환경에서 this
는 window
이고, node.js에선 global
을 가리킨다.
자바스크립트에서 this
는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다.
실행 컨텍스트는 함수를 호출할 때 생성되므로, this
는 함수를 호출할 때 결정된다고 할 수 있다. 함수를 어떤 방식으로 호출 하느냐에 따라 값이 달라지는 것이다.
바인딩이란?
this
와this
가 가리킬 객체를 연결하는 것을 말한다.- 즉, 식별자와 값을 연결하는 과정을 의미한다. 예를들어, 변수 선언은 변수 이름(식별자)과 확보된 메모리 공간의 주소를 바인딩하는 것이다.
this
바인딩은this
(식별자)와this
가 가리킬 객체를 바인딩 하는 것이다.
var foo = function () {
console.dir(this);
};
// 1. 함수 호출
// 여기서 this = 전역객체
foo(); // window
// window.foo();
// 2. 메소드 호출
// 여기서 this= 메소드를 호출한 객체
var obj = { foo: foo };
obj.foo(); // obj
// 3. 생성자 함수 호출
// 자바스크립트에서 모든 함수는 생성자 함수 이기도 한다.
// 여기서 this = 생성자 함수가 생성한 인스턴스
var instance = new foo(); // instance
// 4. apply/call/bind 호출
// 여기서 this = 명시적으로 this와 바인딩한 대상 객체
var bar = { name: 'bar' };
foo.call(bar); // bar
foo.apply(bar); // bar
foo.bind(bar)(); // bar
기본적으로 this
는 전역객체 (global object)에 바인딩된다.
전역객체는 모든 객체의 유일한 최상위 객체이다.
this
는 브라우저에서 window
를 가리키고, Server side(node.js)에서는 global
객체를 의미한다.
// in browser console
this === window
// in Terminal
this === global
그리고 전역 객체는 전역 스코프를 갖는 전역 변수를 프로퍼티로 소유한다.
글로벌 영역에 선언한 함수는 전역 객체의 프로퍼티에 접근 할 수 있는 전역 변수의 메소드이다.
let a = 'a';
console.log(a); // 'a'
console.log(window.a); // 'a'
function foo() {
console.log('foo');
}
window.foo(); // 'foo'
일반 함수 내부에서도 window
를 가리킨다. 하지만 이는 non-strict mode에서이고, strict mode에서 this 는 undefined
를 바인딩한다.
function foo() {
console.log(this); // window
function bar() {
console.log(this); // window
}
bar();
}
foo();
function a() {
console.log(this); // window
};
function b() {
'use strict'
console.log(this); // undefined
};
메소드의 내부 함수 일경우에도 this
는 전역객체에 바인딩 된다.
var value = 1;
var obj = {
value: 100,
foo: function() {
console.log(this); // foo() 함수의 this = obj
console.log(this.value); // foo() 함수의 this.value = 100
function bar() {
console.log(this); // 메소드의 내부 함수일 경우 this = window
console.log(this.value); // 전역 변수 value = 1
}
bar();
}
};
obj.foo();
콜백 함수의 내부 this
는 무엇을 가리킨다고 말할 수 없다.
이는 콜백 함수의 제어권을 가지는 함수가 콜백 함수의 this
를 결정한다.
(1)setTimeout 함수와 (2) forEach 메서드는 그 내부에서 콜백 함수를 호출할 때 대상이 될 this
를 지정하지 않는다. 따라서 콜백함수 내부에서의 this
는 전역객체
를 참조하게 된다.
setTimeout(function() { // ---- (1)
console.log(this); // window
}, 1000);
[1,2,3,4,5].forEach(num => { // ---- (2)
console.log(this.num); // this=window
});
document.body.querySelector('#id').addEventListener('click', function(e) {
console.log(this, e); // '#id' 엘리먼트와 클릭 이벤트에 대한 객체
});
내부 함수는 일반 함수, 메소드, 콜백 함수 어디에선 선언되었든 관계없이 this는 전역객체를 바인딩한다.
메소드 내부의 this
는 해당 메소드를 소유한 객체, 즉 해당 메소드를 호출한 객체에 바인딩 된다.
var obj = {
name: 'Lee',
sayName: function() { // 해당 메소드
console.log(this.name); // 여기서 this = obj
}
}
var obj2 = {
name: 'Kim'
}
obj2.sayName = obj1.sayName;
obj1.sayName(); // 'Lee'
obj2.sayName(); // 'Kim'
프로토타입 객체 메소드 내부의 this
도 일반 메소드와 마찬가지로 해당 메소드를 호출한 객체에 바인딩 된다.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function() {
return this.name;
}
var me = new Person('Lee');
console.log(me.getName());
Person.prototype.name = 'Kim';
console.log(Person.prototype.getName());
this
에 바인딩될 객체는 함수 호출 패턴에 의해 결정된다. 이는 자바스크립트 엔진이 수행하는데, 이러한 자바스크립트 엔진의 암묵적 this
바인딩 이외에 this
를 특정 객체에 명시적으로 바인딩하는 방법도 제공한다.
이를 가능하게 하는 것이 Function.prototype.apply
, Function.prototype.call
메소드 이다.
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])
call
메소드는 메소드의 호출 주체인 함수를 즉시 실행하도록 하는 명령이다.
call
메소드의 첫번째 인자를 this
로 바인딩하고, 이후 인자들을 호출할 함수의 매개변수로 한다.
일반적으로 함수의 this
는 전역객체지만 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
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
Function.prototype.apply(thisArg[, argsArray])
기능적으로 call
메소드와 완전히 동일하다.
차이점 이라면 call
은 함수에 전달된 매개변수를 받는데, apply
는 인수들의 배열
을 받는다.
var func = function foo(a,b,c){
console.log(this, a, b, c);
};
// apply는 두번째 인자로 배열을 받는다.
foo.applay({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
Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])
es5에서 추가된 기능으로, call
과 비슷하지만 즉시 호출하지 않고 넘겨받은 this
및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메소드 이다.
bind
메소드는 함수에 this
를 미리 적용하는 것과 부분 적용함수를 구현하는 두 가지 목적을 모두 가진다.
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
는 다음과 같은 메소드 내부의 중첩 함수 또는 콜백 함수의 this
가 일치하지 않는 문제를 해결 할 수 있다.
const person = {
name: 'go',
foo(callback) {
setTimeout(callback, 100);
}
};
person.foo(function() {
console.log(`hi, i'm ${this.name}`); // "hi, i'm "
// 일반 함수로 호출된 콜백 함수 내부의 this는 전역객체다. (window.name)
// 따라서 this.name이 출력되지 않았다.
});
이는 bind
로 callback에 this
를 명시 해 줌으로써 해결할 수 있다.
const person = {
name: 'go',
foo(callback) {
setTimeout(callback.bind(person), 100);
}
};
person.foo(function() {
console.log(`hi, i'm ${this.name}`); // hi, i'm go
자바스크립트에서 생성자 함수는 객체를 생성하는 역할을 한다. 기존 함수에 new
연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작하게 된다.
new
연산자를 불러 호출한다. new
연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.this
는 instance 사진이 된다. // 생성자 함수
function Person(name) {
this.name = name;
}
var me = new Person('Lee');
console.log(me); // Person {name: "Lee"}
// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');
console.log(you); // undefined
ES6에서 새롭게 도입된 화살표 함수는 실행 컨텍스트 생성 시 this
를 바인딩하는 과정이 제외됐다. 즉, 이 함수 내부에는 this
가 아예 없고, 접근하고자 하면 스코프체인상 가장 가까운 this
에 접근하게 된다.
화살표 함수 내부에서 this
를 참조하면 상위 스코프의 this
를 그대로 참조한다.
const foo = () => console.log(this); // window
화살표 함수가 중첩된 경우는 inner 화살표 함수는 outer 함수 바깥의 상위 함수 중에서 화살표 함수가 아닌 함수의 this
를 참조한다.
function () {
const foo = () => {
const var = () => {
console.log(this);
};
bar();
};
foo();
}.call({a: 1})); // { a: 1 }
<코어 자바스크립트>
다음 규칙은 명시적 this
바인딩이 없는 한 늘 성립한다.
this
는 전역객체를 참조한다. (브라우저 =>window
, node.js => global
)this
는 메소드 호출 주체(메서드 명 앞의 객체)를 참조한다. this
는 전역객체를 참조한다. 메소드의 내부함수에서도 같다.this
는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르고, 정의하지 않은 경우에는 전역객체를 참조한다.this
는 생성될 인스턴스를 참조한다.다음은 명시적 this
바인딩이다.
this
를 명시적으로 지정하면서 함수 또는 메소드를 호출한다.this
및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다.this
를 받기도 한다. 참조
- 코어 자바스크립트 <전재남>
- 모던 자바스크립트 Deep Dive <이웅모>
- 자바스크립트의 this는 김춘수의 〈꽃〉이다