JavaScriptMedium#07
this 키워드는 어떻게 결정되며, 화살표 함수에서는 어떻게 다른가요?
#JS#this#화살표함수#함수
힌트
일반 함수는 호출 방식에 따라, 화살표 함수는 선언 시 렉시컬 환경에 따라 결정됩니다.
정답 및 해설
this 키워드는 어떻게 결정되며, 화살표 함수에서는 어떻게 다른가요?
this는 JavaScript에서 가장 혼란스러운 개념 중 하나입니다. 다른 언어와 달리, JavaScript의 this는 함수가 어떻게 호출되었는지에 따라 동적으로 결정됩니다. 단, 화살표 함수는 이 규칙에서 예외로 선언 시점의 상위 스코프 this를 사용합니다.
일반 함수에서 this의 결정 규칙
1. 전역 컨텍스트 / 단독 호출
// 브라우저 환경 (strict mode 아닌 경우)
function showThis() {
console.log(this); // window (전역 객체)
}
showThis();
// strict mode
function showThisStrict() {
'use strict';
console.log(this); // undefined
}
showThisStrict();
2. 메서드 호출 — 점(.) 앞의 객체
this는 메서드를 호출한 객체를 가리킵니다.
const user = {
name: 'Alice',
greet() {
console.log(`안녕하세요, ${this.name}입니다.`);
}
};
user.greet(); // '안녕하세요, Alice입니다.' (this = user)
// 메서드를 변수에 할당하면 this 컨텍스트를 잃음
const greetFn = user.greet;
greetFn(); // '안녕하세요, undefined입니다.' (this = 전역 or undefined)
3. 생성자 호출 (new)
new로 호출하면 this는 새로 생성된 인스턴스를 가리킵니다.
function Person(name) {
this.name = name; // this = 새로 생성된 객체
this.greet = function () {
return `Hi, ${this.name}`;
};
}
const alice = new Person('Alice');
console.log(alice.greet()); // 'Hi, Alice'
4. 명시적 바인딩 — call, apply, bind
function introduce(greeting, punctuation) {
return `${greeting}, 저는 ${this.name}입니다${punctuation}`;
}
const person = { name: 'Bob' };
// call — 인수를 쉼표로 전달
console.log(introduce.call(person, '안녕하세요', '!'));
// '안녕하세요, 저는 Bob입니다!'
// apply — 인수를 배열로 전달
console.log(introduce.apply(person, ['반갑습니다', '.']));
// '반갑습니다, 저는 Bob입니다.'
// bind — 새 함수를 반환 (즉시 실행하지 않음)
const boundIntroduce = introduce.bind(person, '처음 뵙겠습니다');
console.log(boundIntroduce('~'));
// '처음 뵙겠습니다, 저는 Bob입니다~'
this 결정 우선순위
new 바인딩 > 명시적 바인딩(call/apply/bind) > 메서드 바인딩 > 기본 바인딩(전역/undefined)
화살표 함수의 this — 렉시컬 this
화살표 함수는 자신만의 this를 가지지 않습니다. 선언된 시점의 상위(외부) 스코프의 this를 그대로 사용합니다. 이를 **렉시컬 this(Lexical this)**라고 합니다.
const timer = {
count: 0,
// 일반 함수 — this가 undefined 또는 전역 객체 (콜백이므로)
startBad() {
setInterval(function () {
this.count++; // this가 timer가 아님!
console.log(this.count); // NaN
}, 1000);
},
// 화살표 함수 — 상위 스코프(startGood 메서드)의 this = timer
startGood() {
setInterval(() => {
this.count++; // this = timer
console.log(this.count); // 1, 2, 3, ...
}, 1000);
},
};
클래스에서의 활용
class Button {
constructor(label) {
this.label = label;
this.clickCount = 0;
}
// 일반 메서드 — 이벤트 핸들러로 사용 시 this가 달라질 수 있음
handleClickBad() {
this.clickCount++;
console.log(`${this.label} 클릭 횟수: ${this.clickCount}`);
}
// 화살표 함수 필드 — this가 인스턴스에 고정됨
handleClickGood = () => {
this.clickCount++;
console.log(`${this.label} 클릭 횟수: ${this.clickCount}`);
};
mount(element) {
// 일반 메서드를 이벤트 핸들러로 등록하면 this가 element가 됨
element.addEventListener('click', this.handleClickBad); // 버그!
// 화살표 함수 필드는 안전
element.addEventListener('click', this.handleClickGood); // 정상
}
}
call, apply, bind가 화살표 함수에 통하지 않는 이유
화살표 함수는 렉시컬 this를 사용하므로 call, apply, bind로 this를 변경할 수 없습니다.
const arrowFn = () => {
console.log(this); // 항상 선언 시점의 this (모듈: undefined, 브라우저: window)
};
const obj = { name: 'test' };
arrowFn.call(obj); // 여전히 선언 시점의 this (obj 아님)
arrowFn.apply(obj); // 동일
const bound = arrowFn.bind(obj);
bound(); // 동일 (bind는 새 함수를 반환하지만 this는 변하지 않음)
React에서의 this 패턴
// 클래스 컴포넌트에서 자주 발생하는 this 문제
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// 방법 1: constructor에서 bind
this.handleClick1 = this.handleClick1.bind(this);
}
// 방법 1: constructor에서 bind
handleClick1() {
this.setState({ count: this.state.count + 1 });
}
// 방법 2: 화살표 함수 클래스 필드 (가장 권장)
handleClick2 = () => {
this.setState({ count: this.state.count + 1 });
};
// 방법 3: 렌더에서 바인딩 (렌더마다 새 함수 생성 — 성능 비효율)
render() {
return (
<div>
<button onClick={this.handleClick1}>클릭 1</button>
<button onClick={this.handleClick2}>클릭 2</button>
<button onClick={() => this.handleClick1()}>클릭 3 (비효율)</button>
</div>
);
}
}
상황별 this 정리
| 호출 방식 | this |
|---|---|
| 일반 함수 (non-strict) | 전역 객체 (window) |
| 일반 함수 (strict mode) | undefined |
메서드 obj.fn() | obj |
new fn() | 새 인스턴스 |
fn.call(ctx) | ctx |
fn.apply(ctx) | ctx |
fn.bind(ctx)() | ctx |
| 화살표 함수 | 상위 스코프의 this (고정) |
| 이벤트 핸들러 (일반 함수) | 이벤트가 발생한 DOM 요소 |
this를 올바르게 다루는 핵심은 "함수가 어떻게 호출되는가"를 항상 의식하는 것입니다. 화살표 함수를 적절히 활용하면 많은 this 관련 버그를 예방할 수 있습니다.