
this를 사용할 때 어떤 객체를 참조할지 정해주는 것이다.
이 this가 상황에 따라 달라질 수 있기 때문에 무척 헷갈리는데 오늘은 this가 어떻게 결정되는지와 헷갈리는 상황들을 봐 볼 것이다.
this 호출 시점에 동적으로 할당됨
화살표 함수의 바깥 스코프에서 this를 가져온다.
처음 정의될 때 한번만 결정되기 때문에 정적 바인딩
const obj = {
name: "A",
normalFn: function () { /* 일반적인 함수 호출 */
console.log(this.name); // "A"
},
arrowFn: () => { /* 화살표 함수 호출 */
console.log(this.name); // ❌ undefined
}
};
obj.normalFn(); // "A"
obj.arrowFn(); // undefined → 전역 스코프의 this를 캡처했기 때문
이걸 알기 위해서는 JS의 Call Stack, 실행 컨텍스트에 대해 알아야 된다.
이전에 JS 클로저에 대해 알아보면서 정리해 둔 내용이 있어 살짝 가져와보겠다.
https://ytlive.tistory.com/359
실행 컨텍스트(Execution Context)란 실행할 코드에 제공할 환경 정보들을 모아놓은 객체로
JS 엔진의 Call Stack에 담는 단위이다
실행 컨텍스트에는 변수 객체, 스코프 체인, this 값에 대한 정보가 담겨있다.


처음 프로그램을 실행하면 전역 컨텍스트(Global Context)가 콜스택에 담긴다.
실행 컨텍스트가 어떻게 생겼나 간단히 살펴 보자면
으로 이루어져 있다.
우리는 지금 this binding을 살펴보려고 한다.
this의 값은 실행 컨텍스트의 this binding 값에 따라 달라지는데 일반적인 함수와 화살표 함수의 동작과정이 다르다.
일반적인 함수는 호출될 때마다 새로운 실행 컨텍스트를 만들고 실행 컨텍스트 내부의 This Binding은 호출 주체에 따라 결정된다.
화살표 함수가 아니면 대부분 일반 함수로 동작한다.
화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 단계를 생략하고 상위 스코프의 실행 컨텍스트에 존재하는 this를 그대로 캡처한다.
만약 전역 함수에서 일반적인 함수와 화살표 함수를 호출한다면 아래와 같이 Call Stack이 그려진다.

원래 JS는 함수형 언어로 시작했지만 객체 지향적인 기능이 추가되면서 함수가 각 객체의 메서드로 동작할 수 있게 되었다.
그래서 함수를 누가 호출되었는지에 따라 동작을 다르게 해야 했기 때문에 this가 다르게 동작해야 되었다.
화살표 함수는 새로운 this 바인딩을 만들지 않기 때문에 콜백 함수에서의 this 문제를 해결할 수 있었다.
// sayName 함수 내부에서 timeout을 줘야 하는 상황
const obj = {
name: '철수',
sayName: function() {
setTimeout(function() {
console.log(this.name); // => 여기서 this는 철수가 아님
}, 1000);
}
};
obj.sayName(); // 출력: undefined
이를 해결하기 위해 기존의 JS에서는 임시 변수를 사용해야 했다.
const obj = {
name: '철수',
sayName: function() {
let self = this
setTimeout(function() {
console.log(self.name);
}, 1000);
}
};
obj.sayName(); // 출력: 철수
이 과정은 번거롭기도 하고 코드의 가독성을 떨어뜨렸다.
하지만 화살표가 도입되면서 이러한 문제점이 해결되었다.
const obj = {
name: '철수',
sayName: function() {
setTimeout(() => {
console.log(this.name);
}, 1000);
}
};
obj.sayName(); // 출력: 철수
화살표는 외부 함수의 this를 그대로 사용하기 때문에 sayName 내부의 this는 obj의 this이다.
때문에 setTimeout의 this.name은 철수가 될 수 있었다.
const person = {
name: '영희',
greet: function() {
console.log(this.name);
}
};
person.greet(); // 출력: 영희
이 코드의 Call Stack을 보면

greet가 실행될 때 gloal의 person이 실행했기 때문에
global 실행 Context의 Variable Environment가 참조했던 person 객체를 this로 가지게 된다.
const person = {
name: '영희',
greet: function() {
console.log(this.name); // 영희
function inner() {
console.log(this.name);
}
inner();
}
};
person.greet(); // 출력: undefined (또는 전역객체의 name)
greet 함수는 객체를 통해 호출했기 때문에 greet 내부의 this는 person을 바인딩 한다.
하지만 inner 함수는 호출하는 객체가 없었기 때문에 this 를 전역 객체(window) 또는 undefined (strick mode)로 바인딩 한다.
function Person(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
}
}
const p = new Person('철수');
p.sayName(); // 출력: 철수
이 때 sayName은 p가 호출했기 때문에 this binding은 p 인스턴스를 가리킨다.
function showThis() {
console.log(this);
}
showThis(); // 출력: 전역객체 (브라우저는 window, 엄격모드면 undefined)
이 코드는 그럼 showThis를 호출하는 객체가 없기 때문에 전역객체나 undefined를 호출한다.
const button = document.createElement('button');
button.textContent = '클릭';
document.body.appendChild(button);
button.addEventListener('click', function() {
console.log(this); // 출력: 버튼 DOM 요소
});
class User {
constructor(name) {
this.name = name;
}
printName() {
console.log(this.name);
}
}
const user = new User('지훈');
user.printName(); // 지훈
다음에 보고 싶은거