this binding error를 공부하면서 this
에 대한 이해가 정확히 안 된 것을 깨달아 this에 관해 완전히 파헤치고자 한다.
자바스크립트는 함수가 호출될 때 기본적으로 arguements
와 this
인자를 넘겨받는다. this에 관련하여 일반함수에서 this
가 binding되는 것을 이해하는 것에 엄청 어려움을 느꼈는데 함수가 호출되는 방식(how)에 따라 this가 binding되는 객체가 달라진다
. 즉, 이는 동적 바인딩(dynamic binding)
에 해당된다고 볼 수 있으며 arrow function의 경우에는 정적 바인딩(static binding)
이라 할 수 있다.
그리고 this는 기본으로 window 또는 global
객체를 가리킨다.
함수가 호출되는 방식을 나는 아래와 같이 정리했다.
- 함수 호출
- 메서드 호출
- 생성자함수 호출
- strict mode에서 호출
- 콜백함수 호출
- 내부함수 호출
- 이벤트 호출
standard function
function printThis(){
console.log(this);
}
printThis() // window or global
환경
window
global
📒 그냥 함수 호출시에는 this는 전역객체인 window 또는 global을 가리키게된다.
arrow function
const printThis = () => {
console.log(this);
}
printThis() // window or global
arrow function은 standard function과 달리 변수를 lexical scope에서 찾기 때문에 변수가 정의된 context
에서 this를 찾아서 binding한다. 이해가 잘 안 갈 수 있는데, 한 마디로 말하면 arrow function에서 this는 정의된 곳에서부터 상위 scope를 따라서 this를 찾는다.
따라서, 이부분에서 this가 binding되는 것은 window or global
이다. 이는 위의 일반 함수에서 this가 binding되는 개념과는 다르다는 것에 유의하자.
여기서 lexical scope에 대해서 추가적으로 설명하자면 lexical scope와 반대되는 개념인 dynamic scope
도 존재한다. 이에 관한 내용은 lexical scope에 link를 걸어두었다.
📒 lexical scope: no matter a function or variable is invoked from, or even how is invoked, its lexical scope is only defined by where they delcared
lexical scope
는 함수나 변수가 어디에서 또는 어떻게 호출되었는지와 상관없이 변수 또는 함수가 선언된 부분에서 정의된다.reference here
standard function
const obj = {
name: 'someone',
printThis(){
console.log(this)
}
}
obj.printThis(); // obj
this는 객체를 통한 호출로 obj
객체와 binding된다.
📒 메서드 호출은 자신을 감싸고 있는 객체에 binding된다.
arrow function
const obj = {
name: "someone",
printThis: () => console.log(this),
};
obj.printThis(); // window or global
this를 lexical scope
에서 찾기 때문에 this는 window or global
과 binding된다.
여기서 obj가 this를 감싸는 상위 scope라고 할 수 있지만, obj는 객체로서 scope에 해당되지 않는다.
이 부분에서 처음 공부할 때 obj는 scope에 해당하므로 this는 obj와 binding되어야 하는거 아닌가라고 생각하여 애먹었다🤣
😅 obj가 상위 스코프에 해당한다고 생각하여 this는 obj와 binding되는 거 아닌가?????
더 공부하고 정리하기
📒 arrow function에서 this는 예외없이 정의된 곳에서부터 상위 scope로 this를 찾기 때문에 이 경우에도 window 또는 global객체를 가리키게 된다.
standard function
function Person() {
this.name = "someone";
this.printThis = function () {
console.log(this);
};
}
const person = new Person();
person.printThis(); // Person
this는 객체를 통한 호출로 person
객체와 binding된다.
📒 생성자 호출은 new를 통해 호출되는 순간 instance와 binding되게 된다.
arrow function
function Person() {
this.name = "someone";
this.printThis = () => {
console.log(this);
console.log(this.name);
};
}
const person = new Person();
person.printThis();
여기서 this가 선언된 곳은 Person 생성자 함수 안이다. Person 생성자 함수는 this의 lexical scope
이기 때문에 this는 Person 객체를 가리키게 된다.
📒 Person 객체가 this의 lexical scope에 해당하므로 this는 Person과 binding
standard function
"use strict";
function printThis() {
console.log(this);
}
printThis(); // undefined
strict mode에서 this는 execution context 도중에 this에 관련된 선언이 없으면 undefined
를 return한다.
🤔 execution context: 자바스크립트 엔진은 자바스크립트 코드를 실행하고 변형시키기 위해 특별한 환경을 만드는데 이러한 환경을 execution context라 한다. 즉,
자바스크립트를 실행하기 위해 필요한 모든 정보들을 담고 있는 객체라고 생각하면 된다.
reference 1. execution context from githubio 2.execution context from freecodecamp
arrow function
"use strict";
const printThis = () => console.log(this)
printThis(); // window or global
strict mode
에서 arrow function의 this는 window와 global을 가리킨다.
standard function
function Clock() {
this.value = 0;
setInterval(function () {
this.value++;
console.log(this.value);
console.log(this); // window or global
}, 1000);
}
const clock = new Clock();
setInterval에 들어가는 인자인 익명함수는 실행될 때 global context(전역 환경)
에서 실행된다.
따라서, this는 window 또는 global에 binding된다.
📒 콜백 함수 호출로 인한 this는 global 또는 window객체와 binding된다.
arrow function
function Clock() {
this.value = 0;
setInterval(() => {
this.value++;
console.log(this);
console.log(this.value);
}, 1000);
}
const clock = new Clock();
콜백함수인 익명 화살표 함수는 global context에서 실행되지만 arrow function일 때 this는 어떻게 호출되었던 상관없이 lexical binding
을 함으로 this는 Clock과 binding된다.
standard function
function foo() {
function printThis() {
console.log(this);
}
printThis();
}
foo(); // window
this는 객체의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 객체를 생성하지 않는 바깥함수(foo)는 일반함수로서 this는 의미가 없다.
📒 바깥 함수가 일반함수며 내부함수 호출은 어디에서 선언되던 this는 window와 binding된다.
arrow function
function foo() {
const printThis = () => console.log(this);
printThis();
}
foo();
위에 arrow function에서 this는 lexical scope에서 찾는다고 언급했다. foo함수가 lexical scope에 해당하지만 이 또한 객체를 생성하지 않는 바깥함수인 foo는 this와는 연관이 없다. 따라서 이 경우, this는 window 또는 global
과 binding이 된다.
객체를 생성하지 않는 일반함수 안의 내부 함수에 있는 this는 전역 객체와 바인딩된다.
const button = document.querySelector("#temp-button");
window.addEventListener("click", function (event) {
console.log("window", this);
console.log("window currentTaget", this);
});
button.addEventListener("click", function (event) {
console.log(this);
console.log("currentTaget", this);
});
이벤트에서 this
가 동작하는 방법은 상당히 신기하다. 나는 eventhandler가 callback function으로서 위에서 공부한대로 추측해보면 this의 값은 window가 나와야한다. 하지만, event 함수에서의 this는 event.currentTarget
과 일치한다.
📒 이벤트 함수 호출은 this가 event.currentTarget와 같다.
this에 관해 공부하면서
메서드 호출이 아닌 내부함수던 콜백함수던 일반함수로 호출되면 this는 무조건 전역 객체를 가리키게 된다.