this
자바스크립트에서 this가 가리키는 대상은 함수 호출 방식
에 따라 결정된다. 이러한 호출 방식에 의해 결정되는 환경을 실행 문맥(execution context)
이라고 하며, this는 그 문맥에 따라 어떤 객체에 바인딩
되어 참조 대상이 동적
으로 결정된다. 즉, this는 실행되는 시점과 방식에 따라 연결되는 객체(바인딩 대상)가 달라지는 키워드다.
less
this
└── 함수 호출 방식
├── 일반 함수 호출 -> 전역 객체
├── 메서드 호출 -> 메서드를 포함하고 있는 객체
├── 생성자 함수 호출 -> 생성할 객체
└── 콜백 함수 호출 -> 전역 객체 (일반 함수처럼 호출될 경우)
전역 컨텍스트(Global Context)
전역 코드에서 this는 실행 환경에 따라 전역 객체를 가리킨다.
js
console.log(this); // 브라우저에서는 window
브라우저 환경
: window 객체 참조
window는 브라우저의 전역 객체로, 전역 변수나 함수, 타이머 함수(setTimeout 등), 브라우저 API 등을 포함한다.Node.js 환경
: global 객체 참조
global은 Node.js의 전역 객체로, 브라우저의 window와 유사하게 전역 변수, 함수, 모듈, 타이머 등을 포함한다.
함수 호출 방식
일반 함수 호출
js
function func() {
console.log(this);
}
func(); // 브라우저에서는 window
일반 함수
가 호출될 때, 함수 내부의 this는전역 객체(window)
를 참조한다.- 이는 호출한 주체가 없기 때문이며, this는 기본적으로 전역 객체에 바인딩된다.
Strict mode
에서는 this가undefined
가 되며, 암묵적으로 전역 객체에 바인딩되지 않는다.
메서드 호출
js
const cafe = {
brand: "starbucks",
menu: "americano",
print: function () {
console.log(this);
},
};
cafe.print();
// { brand: "starbucks", menu: "americano", print: function () { ... }}
- 메서드란, 객체의 프로퍼티로 저장된 함수를 말한다.
- 메서드 내부에서 this는 해당 메서드를 호출한
객체 자신
을 가리킨다. - 위 예제에서는
cafe.print()
가 호출되었기 때문에, this는 cafe 객체를 참조한다. 즉,this === cafe
이다.
js
const cafe = {
brand: "starbucks",
menu: "americano",
newCafe: {
brand: "starbucks",
menu: "latte",
print: function () {
console.log(this);
},
},
};
cafe.newCafe.print();
// { brand: "starbucks", menu: "latte", print: function () { ... }}
- print 함수는 newCafe 객체의 메서드로 호출되므로, this는 newCafe 객체를 가리키게 된다.
cafe.newCafe.print()
에서 print는 newCafe 객체에 속해 있으므로,this === cafe.newCafe
가 된다.
js
const cafe = {
brand: "starbucks",
menu: "americano",
print: function () {
console.log(this);
},
};
const myCafe = cafe.print;
myCafe(); // window
- 함수가 객체의 프로퍼티로 존재해도,
호출 방식이 일반 함수
라면 this는 해당 객체를 참조하지 않는다. - this는 함수를 어떻게 호출했는지에 따라 결정되는 키워드다. 위 코드에서 print 함수는 cafe 객체의 메서드지만, myCafe에 할당된 뒤
일반 함수로 호출
되었기 때문에 this는 전역 객체(window)를 가리킨다. - 함수가 객체의 메서드로 호출되지 않으면, this는 해당 객체를 잃고 전역 컨텍스트에서 동작한다.
생성자 함수 호출
js
function Cafe(menu) {
console.log(this); // new에 의해 만들어진 빈 객체: Cafe { }
this.menu = menu;
}
let myCafe = new Cafe("latte");
console.log(myCafe); // Cafe { menu: "latte" }
// Cafe { }
// Cafe { menu: "latte" }
- 생성자 함수는
new
키워드와 함께 호출되어새로운 객체(인스턴스)
를 생성한다. - 이때 this는
새로 생성 중인 객체
를 가리키며, 생성자 함수 내부에서 해당 객체에 프로퍼티를 추가할 수 있다. - 생성자 내부의
this
는 빈 객체로 시작해, 프로퍼티를 추가하고 그 객체를 반환하게 된다.
💡 new와 생성자 함수가 함께 작동하는 방식
js
// Cafe 함수 정의
function Cafe(menu) {
console.log(this); // Cafe { }
this.menu = menu;
}
생성자 함수로 사용하기 위해 함수 이름은 일반적으로 대문자로 시작하는 관례를 따른다.
js
let myCafe = new Cafe("latte");
Cafe 함수를 생성자 함수로 호출하면서, 새로운 객체(Cafe의 인스턴스)를 만들어 myCafe에 할당한다.
내부적으로는:
- 빈 객체가 생성된다:
this = {}
- Cafe 함수가 실행되며
this.menu = menu
→this.menu = "latte"
가 실행된다. - 마지막에 this가 자동으로 반환되어 myCafe에 할당된다.
js
// new 없이 호출
function Cafe(menu) {
console.log(this);
this.menu = menu;
}
let myCafe = Cafe("latte");
console.log(myCafe);
// window
// undefined
- new 없이 함수를 호출하면
일반 함수
로 실행되며, 내부의 this는 전역 객체(window)를 가리킨다. - 이 경우 Cafe 함수는 객체를 반환하지 않기 때문에(return문이 존재하지 않음) myCafe에는 undefined가 할당된다. 따라서 생성자 함수는 반드시 new와 함께 사용해야 의미가 있다.
콜백 함수 호출
js
const cafe = {
brand: "starbucks",
menu: "",
setMenu: function (menu) {
this.menu = menu;
},
};
function getMenu(menu, callback) {
callback(menu); // 일반 함수처럼 호출
}
getMenu("핫초코", cafe.setMenu);
console.log(cafe);
// { brand: "starbucks", menu: "", setMenu: [Function: setMenu] }
- 함수가
콜백 함수
로 전달되어 일반 함수처럼 호출되면, 해당 함수의 this는전역 객체
(window 또는 global)를 가리킨다. getMenu("핫초코", cafe.setMenu)
에서cafe.setMenu
는 함수 자체(참조)를 참조 형태로 전달한 것이다. 이때 함수는 cafe 객체의 메서드로 묶인 상태가 아니며, 단순한일반 함수
처럼 callback(menu)으로 호출된다.- 그래서, setMenu 내부의 this는 더 이상 cafe를 가리키지 않고, 전역 객체(window 또는 global)를 가리킨다.
- 결국
this.menu = menu;
는 cafe 객체의 프로퍼티를 바꾸지 못하고, 전역 객체에 menu를 추가하려고 시도한다.