JavaScript의 헷갈리는 개념 중 하나인 this
호출되는 시점과 상황에 따라 값이 달라져 디버깅을 유발하는 키워드다.
그간 삽질한 경험과 학습(Javascript 핵심 개념 알아보기 - JS Flow)을 바탕으로 this에 어떤 값이 바인딩 되는지 간략하게 정리했다.
전역공간에서 this는 window(global) 객체
// 전역공간
console.log(this); // Window {parent: Window ... }
함수내부에서 this는 window(global) 객체
함수를 전역객체의 메소드라 생각하면 코드상 this를 파악하기 용이하다. (아래 메소드 호출 시 this 참고)
// 함수내부
function foo() {
console.log(this);
}
foo(); // Window {parent: Window ... }
// 함수의 함수내부
function foo() {
function bar() {
console.log(this);
}
bar();
}
foo(); // Window {parent: Window ... }
// 메소드의 함수내부
const obj = {
a: function() {
function b() {
console.log(this);
}
b();
}
}
obj.a(); // Window {parent: Window ... }
메소드 호출 시 this는 메소드 호출 주체
// 메소드 호출
const obj = {
a: function() {
console.log(this);
}
}
obj.a(); // {fn: ƒ}
// 메소드 호출
const obj = {
a: {
b: function() {
console.log(this);
}
}
}
obj.a.b(); // {b: ƒ}
화살표 함수에는 함수 이름, this, arguments가 없다. 따라서 예제 코드를 화살표 함수로 바꾸면 스코프 체인에 따라 global 객체가 this 바인딩 된다.
ES6 화살표 함수에 없는 3가지?
// 화살표 함수 호출
const obj = {
a: () => {
console.log(this);
}
}
obj.a(); // Window {parent: Window ... }
콜백 함수에서 this는 기본적으로 함수내부에서와 동일하다.
하지만 언급했던 것 처럼 상황에 따라 this 값이 달라지는데 가장 흔한 경우가 콜백 함수안에 쓰인 this 이다.
1. call, apply, bind 메서드를 통해 명시적인 this 바인딩 한 경우와 그렇지 않은 경우
// this 바인딩
const callback = function() {
console.log(this);
}
const obj = {
a: 1,
b: function(cb) {
cb();
// cb.call(this); // {a: 1, b: ƒ}
}
}
obj.b(callback.bind(obj)); // {a: 1, b: ƒ}
setTimeout(callback.bind(obj), 100); // {a: 1, b: ƒ}
// this 바인딩 하지 않은 경우
const callback = function() {
console.log(this);
}
const obj = {
a: 1,
b: function(cb) {
cb();
}
}
obj.b(callback); // Window {parent: Window ... }
setTimeout(callback, 100); // Window {parent: Window ... }
2. 이벤트 리스너
addEventListener 콜백 함수의 this는 내부 규칙으로 인해 event 객체의 currentTarget으로 바인딩 된다.
// 이벤트 핸들러
document.querySelector('body').addEventListener('click', function() {
console.log(this); // <body>...<body>
});
// 하지만 this를 명시적으로 대체한 경우
const obj = { a: 'a' };
document.querySelector('body').addEventListener('click', function() {
console.log(this); // {a: "a"}
}.bind(obj));