[JavaScript] 자바스크립트 this의 모든 것
this란 무엇인가?
C++, JAVA 등 다양한 프로그래밍 언어에서도 this가 존재하는데, 여기서 this는 객체 자신을 나타내는 포인터, 레퍼런스다.
하지만 자바스크립트에서 this는 함수의 호출 맥락(Context)를 의미한다. 즉 함수를 어떻게 호출하냐에 따라 this가 가리키는 대상이 달라질 수 있다는 것이다. 함수와 객체의 관계가 느슨한 자바스크립트에서 this는 이 둘을 연결 시켜주는 역할을 한다.
console.log(this); // window
function func1() {
console.log(this);
}
func1(); // window
우선 브라우저의 개발자 도구에서 this를 출력 해보면 전역 객체인 window 객체를 출력한다. (node.js에서는 this를 출력하면 빈 객체 { } 가 나온다) 뿐만 아니라 func라는 함수 안에서 this를 출력 해보아도 똑같이 window 객체를 출력한다.
strict 모드에서는 this에 디폴트 바인딩이 없기 때문에 undefined를 출력한다.
"use strict";
function myFunction() {
return this;
}
console.log(myFunction()); //undefined
메소드에서 this
const obj = {
func2: function () {
console.log(this); // obj 객체를 출력
}
}
obj.func2();
위의 코드를 보면 obj라는 객체의 메소드 func2에서의 this는 func2가 소속 되어있는 객체 obj를 가리키는 것을 알 수 있다. 사실 처음 설명한 코드와 같은 원리인데, 처음 설명한 func1는 window라는 객체의 메소드 window.func1()라고도 볼 수 있기 때문에 func1 안의 this는 window를 가리켰던 것이다.
생성자에서 this
생성자 안에서 this는 그 생성자가 만든 객체를 나타낸다.
function func () {
console.log(this);
}
func(); // window
const A = new func(); // func { }
다양한 형식의 메소드와 this를 출력해 보았다.
var obj = {
func: function() {
return this;
},
func2: function() {
var obj2 = {
func: function() {
return this;
}
}
return obj2.func();
},
func3: function() {
function func4() {
return this;
}
return func4;
},
func5: () => {
return this;
}
}
function func6() {
return this;
}
console.log(obj.func()); // obj
console.log(obj.func2()); // obj2
console.log(obj.func3()); // func4
console.log(obj.func5()); // { }
console.log(func6); // func6
console.log(new func6()); // func5 {}
때문에 만약 함수를 생성자를 사용하지 않고 호출을 한다면 해당 함수에서 this 는 전역 객체인 window를 가리키므로 원치않는 데이터 변경이 되지 않도록 주의해야한다.
var data = 'important';
function func(value) {
this.data = name;
}
var tmp = func('Hello');
console.log(window.data); // Hello
이벤트 핸들러 안의 this
이벤트 핸들러에서 this는 이벤트를 받는 HTML 요소를 가리킨다.
const btn = document.querySelector('#btn');
btn.addEventListener('click', function () {
console.log(this); //#btn
});
함수 바인딩 (apply, call, bind)
일반적으로 객체와 메소드는 Master-Slave 관계를 가지고 있다.
하지만 함수의 메소드인 bind, apply, call를 사용한다면 Master인 객체를 (this의 값을) 제어 할 수 있다.
그렇다면 이 세가지 메소드는 어떤 차이가 있는지 알아보자.
1. apply
함수의 메소드인 apply를 이용하면 해당 함수에서 사용되는 this가 어떤 객체를 나타 낼 것인지 직접 설정한다.
var o = {};
var p = {};
function func(){
console.log(this);
}
func(); // window
func.apply(o); // o { }
func.apply(p); // p { }
func(); // window
apply를 사용하더라도 실제 func가 속한 객체를 바꾼 것이 아니기 때문에 마지막에 다시 호출한 func()에서는 다시 window 객체를 가리킨다.
2. call
apply와 call 두 메소드는 거의 흡사하다. 둘의 유일한 차이점은, 바인딩 할 함수에 인자가 있을 때, 파라미터로 배열을 넣냐, 여러개의 파라미터를 값으로 넣냐다. apply는 배열을 인자로 받고, call은 다수의 파라미터를 인자로 받는다. 아래의 예시를 보자.
window.num = 0;
function sum(num1, num2){
return console.log(this.num + num1 + num2);
}
const obj = { num : 100 }
sum(1, 2); // 3
sum.call(obj, 1, 2); // 103
sum.apply(obj, [1, 2]); // 103
예시를 보면 call이나 apply나 인자의 형태만 다를 뿐 바인딩 된 객체나 전달 된 인자들은 모두 같다는 것을 알 수 있다.
3. bind
bind는 새롭게 바인딩 한 함수를 만든다. 여기서 주의 할 점은 bind()는 call(), apply()와 같이 함수가 가리키고 있는 this를 바꾸지만 호출되지는 않는다.
window.num = 0;
function sum(num1, num2){
return console.log(this.num + num1 + num2);
}
const obj = { num : 100 }
const sum2 = sum.bind(obj); // 함수가 호출되지 않기 때문에 출력 없음
sum(1, 2); // 출력: 3
sum2(1, 2); // 출력: 103