this
는 현재 실행 중인 함수 내에서 현재 실행 컨텍스트의 객체를 참조하는 특별한 키워드이다.this
를 가질 수 있으며, this
에는 어떤 값이든 들어갈 수 있다.this
는 함수가 호출되는 방식에 따라 값이 달라질 수 있습니다. 함수가 호출되는 상황은 4가지가 있다.
호출 방식 | this가 가리키는 값 (this 바인딩) |
---|---|
일반 함수로서 호출 | 전역 객체 |
메서드로서 호출 | 메서드를 호출한 객체(마침표 앞의 객체) |
생성자 함수로서 호출 | 생성자 함수가 생성할 인스턴스 객체 |
위와 같은 환경에 따라 this가 바뀌는 것을 동적 바인딩(Dynamic binding)이라고 한다.
bind
, apply
, call
등으로 this가 가리키는 것을 조작할 수 있다.setTimeout
함수에 전달된 함수 내부에서 this키워드는 전역 객체(window)를 가리키게 된다. 하지만, 화살표 함수를 사용해서 화살표 함수가 정의된 컨텍스트를 가리키게 할 수 있다.This는 호출되는 방식에 따라서 바인딩 대상이 달라집니다. 바인딩이라는 단어가 어렵다면 가리키는 객체라고 생각해보세요.
기본적으로 전역 컨텍스트에서 this
는 window
를 가리킵니다. 따라서 전역 렉시컬 환경에 있는 name
도 접근할 수 있습니다.
const name = "elice";
console.log(this);// window {...}
일반 함수 호출에서도 this
는 window
를 가리킵니다. 따라서 myFunc
함수 내부에 있는 name
에 접근 할 수 없습니다.
function myFunc() {
const name = "elice";
console.log(this);
}
myFunc();// 일반 함수 호출시 window {...}
생성자 함수 내부의 this는 new
키워드를 통해 앞으로 만들어질 인스턴스 객체를 가리킵니다.
생성자 함수는 함수 이름으로 된 객체를 만들어줍니다. 반환해야할 것들은 this 즉, 생성될 시점의 컨텍스트에 저장되고 자동으로 return 되기 때문에 명시적으로 써줄 필요가 없습니다. 따라서 다른 값을 return 하면 객체가 만들어지지 않습니다.
function myFunc(name) {
this.name = name;
this.getName = function () {
console.log("getName this:", this);
return this.name;
};
console.log("myFunc this:", this);
// return this; 생략 가능합니다.
}
const o = new myFunc("elice");// myFunc this: myFunc {...}
o.getName()// myFunc this: myFunc {...}
결과적으로 일반적으로 함수를 호출할 때는 this가 모두 window를 가리킵니다. 함수를 객체로 생성하는 방법에만 this가 생성하는 함수 내부를 가리킵니다.
객체에서의 this는 메소드를 호출한 객체를 가리킵니다.
const o = {
name: "elice",
myFunc: function () {
console.log(this);
},
};
o.myFunc();// {name: 'elice', myFunc: ƒ}
하지만 객체 안 함수의 내부 함수에 This는 동적바인딩으로 인해 전역 객체를 가리킵니다.
const o = {
name: "elice",
myFunc: function () {
return function () { console.log(this) }
},
};
o.myFunc()();// window {...}
this
는 전역 객체를 가리키게 됩니다. 이는 콜백 함수가 호출되는 동안 함수 호출의 주체가 명확하지 않기 때문입니다.this
는 해당 메소드를 소유하고 있는 객체를 가리킵니다.생성자 함수로 만든 객체, 객체 리터럴 방식으로 만든 객체 모두 내부에서 this
가 자기 자신을 가리키고 있습니다. 하지만 두 경우 모두 내부에서 함수를 만들고 해당 함수에서 this
를 출력하면 window
를 가리킵니다. 다음 코드를 살펴봅시다.
/* 생성자 함수 방식 */
function createObject() {
this.myFunc = function () {
console.log("myFunc this:", this);
return function () { console.log("myFunc return this:", this) };
};
}
const o = new createObject();
o.myFunc()();
// myFunc this: createObject {...}
// myFunc return this: window {...}
/* 객체 리터럴 방식 */
const o = {
myFunc: function () {
console.log("myFunc this:", this)
return function () { console.log("myFunc return this:", this) }
},
};
o.myFunc()();
// myFunc this: myFunc {...}
// myFunc return this: window {...}
객체의 최상위 스코프에서의 함수 본문 this는 객체 최상위 스코프를 정상적으로 가리킵니다. 하지만 함수 안에 또다시 함수가 리턴되는 경우에는 window 객체를 가리킵니다.
다음과 같이 화살표 함수를 사용하면 객체 최상위 스코프를 가리키도록 유지합니다.
/* 생성자 함수 방식 */
function createObject() {
this.myFunc = function () {
console.log("myFunc this:", this);
return () => { console.log("myFunc return this:", this) };
};
}
const o = new createObject();
o.myFunc()();
// myFunc this: createObject {...}
// myFunc return this: createObject {...}
/* 객체 리터럴 방식 */
const o = {
myFunc: function () {
console.log("myFunc this:", this);
return () => { console.log("myFunc return this:", this) };
},
};
o.myFunc()();
// myFunc this: myFunc {...}
// myFunc return this: myFunc {...}
앞선 상황과 동일한 코드를 가지고 원하는 객체를 가리키도록 해보겠습니다.
/* 생성자 함수 방식 */
function createObject() {
this.myFunc = function () {
console.log("myFunc this:", this);
return function () { console.log("myFunc return this:", this) };
};
}
const o = new createObject();
o.myFunc().bind(o)();// bind 메서드를 사용해 o객체로 고정시킵니다.
// myFunc this: createObject {...}
// myFunc return this: createObject {...}
/* 객체 리터럴 방식 */
const o = {
myFunc: function () {
console.log("myFunc this:", this)
return function () { console.log("myFunc return this:", this) }
},
};
o.myFunc().bind(o)();// bind 메서드를 사용해 o객체로 고정 그리고 함수를 실행
// myFunc this: myFunc {...}
// myFunc return this: myFunc {...}
두 메서드에 차이는 call 메서드는 인수 목록을 받고, apply 메서드는 인수 배열을 하나 받습니다.
ex) call(this, var1, var2, var3, …) / apply(this, [ el, el2, el3, … ])
/* 생성자 함수 방식 */
function createObject() {
this.myFunc = function () {
console.log("myFunc this:", this);
return function () { console.log("myFunc return this:", this) };
};
}
const o = new createObject();
o.myFunc().call(o, null);// call 메서드를 사용해 o객체로 고정시킨 후 함수 실행
// myFunc this: createObject {...}
// myFunc return this: createObject {...}
o.myFunc().apply(o, null);// apply 메서드를 사용해 o객체로 고정시킨 후 함수 실행
// myFunc this: createObject {...}
// myFunc return this: createObject {...}
/* 객체 리터럴 방식 */
const o = {
myFunc: function () {
console.log("myFunc this:", this)
return function () { console.log("myFunc return this:", this) }
},
};
o.myFunc().call(o, null);// call 메서드를 사용해 o객체로 고정시킨 후 함수 실행
// myFunc this: myFunc {...}
// myFunc return this: myFunc {...}
o.myFunc().apply(o, null);// apply 메서드를 사용해 o객체로 고정시킨 후 함수 실행
// myFunc this: myFunc {...}
// myFunc return this: myFunc {...}
call, apply, bind는 일반 함수 호출에도 원하는 객체를 가리킬 수 있습니다.
const o = {
name: "Elice",
}
function myFunc() {
console.log(this);
}
myFunc()// window {...}
myFunc.bind(o)()// {name: 'Elice'}
myFunc.call(o, null)// {name: 'Elice'}
myFunc.apply(o, null)// {name: 'Elice'}
<일반 함수의 this>
새롭게 생성된 실행 컨텍스트를 기리킨다.
<화살표 함수의 this>
호출된 함수를 둘러싼 실행 컨텍스트를 가리킨다.