대부분의 언어에서 'this'는 클래스가 인스턴스화 한 현재 객체에 대한 참조인 반면, 자바스크립트에서 'this'는 일반적으로 함수를 호출하는 객체에 대한 참조입니다.
이번 글에서는 자바스크립트의 this에 대해서 많이 사용되는 케이스의 예제 위주로 다루어 보겠습니다.
this는 어떻게 정의되었느냐가 아니라 어떻게(how) 호출되었느냐에 따라 결정됩니다.
console.log(this); //window{...}
function whatIsThis() {
console.log(this);
}
whatIsThis(); // window
함수가 호출될 때 binding된 사항 없이 독립적으로 실행되었는데,
이와 같이 전역에서 실행된 함수 내부의 this는 window가 됩니다.
물론 예외 상황도 있습니다.
function whatIsThis() {
'use strict'
console.log(this);
}
whatIsThis(); // undefined
객체 method로의 호출하는 경우와 일반함수로 호출하는 경우의 차이를 예제를 통해 비교해보겠습니다.
백설공주가 사과먹는함수(eatAppleFn)을 어떻게 호출했느냐에 대한 차이를 보겠습니다.
var apple = '독이 든 사과';
var home = {
apple: '맛있는 사과',
eatApple: eatAppleFn
}
function eatAppleFn() {
console.log(`백설공주가 ${this.apple}를 먹습니다.`);
}
// (1) 객체 method 호출
home.eatApple(); // 백설공주가 맛있는 사과를 먹습니다.
// (2) 함수 직접 호출
eatAppleFn(); // 백설공주가 독이 든 사과를 먹습니다.
(1) 객체 메서드로 실행되는 경우 home.eatApple()로 home의 메서드로 호출.
객체 메서드로 호출된 경우 this는 호출한 객체를 가리킵니다.
그래서 여기서 this는 home이 되고 백설공주는 객체 내부의 apple, 맛있는 사과(home.apple)이 됨.
(2) 반면 함수를 직접 호출하는 경우, eatAppleFn()은 어디에도 bind되어있지 않고 전역에서 독립적으로 실행되었습니다.
이 경우 this는 window를 나타냅니다.
그래서 백설공주는 독이 든 사과(var apple)을 먹게되는거죠.
이와같이 함수 eatAppleFn()를 실행하더라고 **함수를 어떻게 호출했느냐에 따라 this가 참조하는 객체는 달라질 수 있습니다.
그럼 이렇게 호출되었다면 결과는 어떻게 될까요?
var homeApple = home.eatApple;
homeApple(); // ???
일반함수 형태로 호출되므로 this는 window입니다.
따라서 정답은 '독사과를 먹는다' 임.
📎번외 1. 프로토타입의 this도 호출된 객체를 가리킴
var apple = '독이 든 사과';
function Home() {
this.apple = '맛있는 사과';
}
Home.prototype.eatApple = function() {
console.log(`백설공주가 ${this.apple}를 먹습니다.`);
}
var home = new Home();
home.eatApple(); // 백설공주가 맛있는 사과를 먹습니다.
📎번외 2* 내부 함수의 this는 전역 객체(window)를 바인딩함.
var apple = '독이 든 사과';
var home = {
apple: '맛있는 사과',
eatApple: function() {
eatAppleFn();
}
}
function eatAppleFn() {
console.log(`백설공주가 ${this.apple}를 먹습니다.`);
}
home.eatApple(); // 백설공주가 독이 든 사과를 먹습니다.
home.eatApple()의 형태로 호출되긴 했지만, 호출된 함수는 eatAppleFn()이 아닌 외부 함수입ㄴ다.
this를 사용하는 eatAppleFn() 함수는 바인딩 없이 일반 함수의 형태로 호출되었습니다.
따라서 this는 window...!
앞에서 내부 함수의 경우에는 this가 window를 나타내는 것을 확인했습니다.
어떻게 하면 내부 함수를 사용하면서도 this가 객체를 가리키도록 연결해 줄 수 있을까요?
call(apply), bind를 이용한 예제를 통해 살펴보겠습니다.
var apple = '독이 든 사과';
var home = {
apple: '맛있는 사과',
eatApple: function() {
eatAppleFn();
},
eatAppleCall: function() {
// 여기서의 this는 home
eatAppleFn.call(this);
},
eatAppleBind: function() {
// 여기서의 this는 home
(eatAppleFn.bind(this))();
}
}
function eatAppleFn() {
console.log(`백설공주가 ${this.apple}를 먹습니다.`);
}
home.eatApple(); // 백설공주가 독이 든 사과를 먹습니다.
home.eatAppleCall(); // 백설공주가 맛있는 사과를 먹습니다.
home.eatAppleBind(); // 백설공주가 맛있는 사과를 먹습니다.
위에서 eatAppleCall과 eatAppleBind 메서드의 this는 home입니다.
이 this를 call 또는 bind의 인자로 제공하여 eatAppleFn함수 내에서 this로 사용할 수 있습니다.
그래서 백설공주가 home안의 '맛있는 사과'를 먹을 수 있는것!
이와 같이 call, apply, bind등을 사용하면 내부 함수의 this를 원하는 객체로 바인딩 할 수있습니다.
📎번외 1 var that = this
var apple = '독이 든 사과';
var home = {
apple: '맛있는 사과',
eatApple: function() {
var that = this; // this == home
(function() {
console.log(`백설공주가 ${that.apple}를 먹습니다.`);
})();
}
}
home.eatApple(); // 백설공주가 맛있는 사과를 먹습니다.
that=this(또는 self, _this..)는 결국 외부의 this를 내부 함수로 전달하기 위해서 사용하는 변수 선언인 것을 알 수 있습니다.
+) 물론 bind, call 등으로 대체 가능
세상에서 제일 예쁜 사람을 알려주는 거울(newMirror)을 생성합니다.
var sejelye = '백설공주';
function mirrorReply() {
console.log(`세상에서 제일 예쁜 사람은 ${this.sejelye}입니다.`);
}
// 생성자 함수
function MagicMirror(name) {
this.sejelye = name;
this.reply = mirrorReply;
}
// 새로운 거울 생성
var newMirror = new MagicMirror('왕비님');
newMirror.reply(); // 세상에서 제일 예쁜 사람은 왕비님입니다.
new keyword를 사용하는 경우 생성자 함수 내의 this를 통해 프로퍼티와 메서드를 추가할 수 있습니다.
생성자 함수 내의 this는 새로 생성된 객체를 가리키며 생성자 함수는 암묵적으로 this를 return합니다.
따라서 newMirror는 다음과 같은 객체가 됨.
테스트해보면
mirrorReply() 함수는 newMirror의 메서드로 호출되므로 3번에서 살펴본 것처럼 함수 내부의 this는 newMirror를 가리킵니다. 새로 만든 거울에게 묻는다면 newMirror안의 'sejelye'를 찾아서 대답할 것입니다.
그래서 "세상에서 제일 예쁜 사람은 왕비님입니다."가 출력됩니다.