this 의 정체 — 4가지 호출 방식
함수가 어떻게 호출되느냐에 따라 this 가 4가지로 결정됨.
JS 의 this 는 처음 보면 마법 같습니다 — 같은 함수가 어디서 호출되느냐에 따라 가리키는 것이 바뀝니다. 사실 규칙은 단 4가지. 22편은 이 규칙과 화살표 함수의 예외, bind/call/apply 의 정확한 차이까지.
핵심 — this 는 "호출 방식" 으로 결정
한 줄. this 는 함수가 "정의된 위치" 가 아니라 "호출된 방식" 으로 결정됩니다. 같은 함수라도 어떻게 부르냐에 따라 4가지 모양 중 하나.
① 메서드 호출 — obj.fn()
const user = {
name: "준성",
greet() {
return `Hi, ${this.name}`;
}
};
user.greet(); // "Hi, 준성" — this = user
점(.) 앞이 this. 가장 직관적인 경우.
② 일반 함수 호출 — fn()
function show() {
console.log(this);
}
show(); // 브라우저: window, Node: globalThis
// strict mode: undefined
strict mode 차이. 일반 모드에서 this = 글로벌 객체. strict mode 에서는 undefined. ESM 파일은 자동으로 strict — 모듈에서는 거의 항상 undefined.
③ 생성자 호출 — new Fn()
function User(name) {
this.name = name; // this = 새로 생성될 객체
}
const u = new User("준성");
u.name; // "준성"
// 클래스도 마찬가지
class User2 {
constructor(name) { this.name = name; }
}
④ 명시 호출 — call · apply · bind
function greet(greeting) {
return `${greeting}, ${this.name}`;
}
const user = { name: "준성" };
// call — this + 인자들 (콤마로)
greet.call(user, "Hi"); // "Hi, 준성"
// apply — this + 인자 배열
greet.apply(user, ["Hi"]); // "Hi, 준성"
// bind — this 를 고정한 새 함수 반환
const greetUser = greet.bind(user);
greetUser("Hi"); // "Hi, 준성"
greetUser("Hello"); // "Hello, 준성"
this 잃는 함정 — 18편 다시
class Counter {
count = 0;
increment() {
this.count++;
}
}
const c = new Counter();
c.increment(); // OK
// 메서드를 변수에 → 점(.) 분리
const fn = c.increment;
fn(); // ❌ TypeError — this 가 undefined (strict)
// 콜백으로 넘길 때
setTimeout(c.increment, 1000); // ❌ this 잃음
btn.addEventListener("click", c.increment); // ❌
// 해결
setTimeout(c.increment.bind(c), 1000); // ✅ bind
setTimeout(() => c.increment(), 1000); // ✅ 화살표 wrap
// 또는 클래스 필드에 화살표 (this 고정)
class Counter {
count = 0;
increment = () => { this.count++; };
}
화살표 함수의 this — 렉시컬
// 일반 함수 — 호출 방식 따라
const obj = {
name: "준성",
fn1: function () {
return this.name; // obj 의 메서드 호출 → this = obj
},
fn2: () => {
return this.name; // 화살표 — 주변 스코프의 this (전역)
}
};
obj.fn1(); // "준성"
obj.fn2(); // undefined 또는 다른 것
// 화살표는 자기 this 가 없음 — 만들어진 곳의 this 그대로 사용
class App {
name = "준성";
setup() {
setTimeout(() => {
console.log(this.name); // ✅ "준성" — setup 의 this 그대로
}, 100);
// 일반 함수면?
setTimeout(function () {
console.log(this.name); // ❌ setTimeout 이 호출하니까 this = global
}, 100);
}
}
화살표가 콜백에 강한 이유. 일반 함수는 호출 방식에 따라 this 가 바뀜 — 콜백으로 넘기면 잃기 쉬움. 화살표는 만들어진 곳의 this 를 영구히 고정. React·이벤트 핸들러에서 자연스러운 패턴.
this 결정 우선순위 — 한 표
| 우선 | 호출 방식 | this |
|---|---|---|
| 1 (최고) | 화살표 함수 | 렉시컬 (호출 무관) |
| 2 | new Fn() | 새 객체 |
| 3 | fn.call/apply/bind(obj) | obj |
| 4 | obj.fn() | obj |
| 5 (최저) | fn() | strict: undefined / 일반: 전역 |
실전 — 메서드를 콜백으로 안전하게
// React 클래스 컴포넌트 시절 (역사)
class Btn extends React.Component {
// 생성자에서 bind
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() { ... }
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}
// 또는 화살표 필드 (현대)
class Btn extends React.Component {
handleClick = () => { ... };
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}
// 또는 인라인 화살표
<button onClick={() => this.handleClick()}>Click</button>
// 함수형 컴포넌트는 클래스 자체 안 써서 이 문제 없음
전역 객체 — globalThis
// 환경마다 전역 객체 이름이 달랐음
// 브라우저: window
// Node: global
// Web Worker: self
// 통합: globalThis (ES2020+)
globalThis.window === window; // true (브라우저)
globalThis === global; // true (Node)
한 줄 가이드
- 이벤트 핸들러·콜백 → 화살표 또는 bind.
- 클래스 메서드를 변수로 빼면 this 잃음 — 분리 전 bind.
- 일반 함수의 this 는 strict 면 undefined. 가능하면 의존 안 하기.
- 화살표 함수는
new·call/apply영향 안 받음. - TS 면
noImplicitThis(18편) 가 미스 잡아줌.
23편 — 프로토타입 체인
class 뒤에 숨은 진짜. Object.create·__proto__·prototype 의 차이.
📚 쉽게 배우는 자바스크립트 교재
이전: 21편 클로저 · 현재: 22편 (고급) · 다음 → 23편 프로토타입 · 진행: 22/26
이전: 21편 클로저 · 현재: 22편 (고급) · 다음 → 23편 프로토타입 · 진행: 22/26