핵심 개념
JavaScript에서 this는 함수가 어떻게 호출되느냐에 따라 결정됩니다. 함수를 작성할 때가 아니라, 실행될 때 결정된다는 게 핵심입니다.
1. 기본 바인딩 (Default Binding)
함수를 그냥 호출하면 this는 undefined가 됩니다 (strict mode/모듈 환경).
function sayName() {
console.log(this.name);
}
sayName(); // TypeError: Cannot read properties of undefined
2. 암시적 바인딩 (Implicit Binding)
객체의 메서드로 호출하면 this는 .앞의 객체를 가리킵니다.
const person = {
name: '철수',
sayName: function() {
console.log(this.name);
}
};
person.sayName(); // '철수'
// 하지만 함수를 변수에 할당하면 연결이 끊어집니다
const sayNameFunc = person.sayName;
sayNameFunc(); // TypeError (this를 잃어버림)
핵심: 함수를 어떻게 호출하느냐가 중요합니다!
3. 명시적 바인딩 (Explicit Binding)
call, apply, bind를 사용해 this를 직접 지정할 수 있습니다.
call vs apply vs bind
const person1 = { name: '철수' };
const person2 = { name: '영희' };
function introduce(age, city) {
console.log(`저는 ${this.name}이고, ${age}살이며, ${city}에 살아요`);
}
// call: 즉시 실행, 인자를 하나씩 전달
introduce.call(person1, 25, '서울');
// "저는 철수이고, 25살이며, 서울에 살아요"
// apply: 즉시 실행, 인자를 배열로 전달
introduce.apply(person2, [30, '부산']);
// "저는 영희이고, 30살이며, 부산에 살아요"
// bind: 즉시 실행 안 함! this가 고정된 새 함수를 반환
const introducePerson1 = introduce.bind(person1);
introducePerson1(25, '서울');
// "저는 철수이고, 25살이며, 서울에 살아요"
차이점:
- call/apply: 즉시 실행
- bind: this가 고정된 새로운 함수 반환 (이벤트 핸들러에 유용)
bind의 특별한 점
한번 bind로 고정하면 절대 변경할 수 없습니다.
const person = {
name: '철수',
sayName: function() {
console.log(this.name);
}
};
const anotherPerson = { name: '영희' };
const boundFunc = person.sayName.bind(person);
boundFunc.call(anotherPerson); // '철수' (영희가 아님!)
4. 화살표 함수의 Lexical This
화살표 함수는 자신만의 this를 만들지 않습니다. 대신 정의된 위치의 상위 스코프의 this를 사용합니다.
const person = {
name: '철수',
// 일반 함수: this = person
sayName1: function() {
console.log(this.name); // '철수'
},
// 화살표 함수: this = 전역 (객체 리터럴은 스코프가 아님)
sayName2: () => {
console.log(this.name); // undefined
},
// 일반 함수 안의 화살표 함수: 상위 함수의 this 물려받음
sayName3: function() {
const inner = () => {
console.log(this.name); // '철수'
};
inner();
}
};
왜 sayName3은 동작할까요?
sayName3: function() { // ← 이 함수의 this = person
const inner = () => { // ← 화살표 함수는 바로 위 함수의 this 사용
console.log(this.name); // ← person.name
};
}
화살표 함수는 "메아리"처럼 바깥 함수의 this를 그대로 따라합니다.
5. this 바인딩 우선순위
여러 바인딩이 충돌할 때 우선순위는 다음과 같습니다:
- new 바인딩 - new Person() (가장 강력)
- 명시적 바인딩 - bind() > call/apply
- 암시적 바인딩 - obj.method()
- 기본 바인딩 - 그냥 func() (undefined)
- 화살표 함수 - 우선순위 무시, 렉시컬 스코프만 봄
6. React 클래스 컴포넌트에서의 this
React 클래스 컴포넌트에서 this 문제가 자주 발생합니다.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// 방법 1: constructor에서 bind
this.handleClick1 = this.handleClick1.bind(this);
}
// 일반 메서드 (bind 필요)
handleClick1() {
this.setState({ count: this.state.count + 1 });
}
// 일반 메서드 (bind 안 하면 에러!)
handleClick2() {
this.setState({ count: this.state.count + 1 });
// TypeError: Cannot read properties of undefined (reading 'setState')
}
// 방법 2: 화살표 함수 (권장)
handleClick3 = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<button onClick={this.handleClick1}>작동</button>
<button onClick={this.handleClick2}>에러!</button>
<button onClick={this.handleClick3}>작동</button>
</div>
);
}
}
왜 handleClick2는 에러가 날까요?
<button onClick={this.handleClick2}>
- 버튼에 함수를 전달할 때, 함수만 전달됩니다 (객체와의 연결 끊김)
- 버튼 클릭 시 React가 handleClick2()로 호출
- 함수 안의 this는 undefined
- undefined.setState() → 에러!
화살표 함수는 왜 작동할까요?
handleClick3 = () => {
this.setState({ ... });
}
이 코드는 실제로는 이렇게 변환됩니다:
constructor() {
super();
// 화살표 함수가 여기서 생성됨!
this.handleClick3 = () => {
this.setState({ ... }); // constructor의 this = 인스턴스
};
}
화살표 함수는 constructor 안에서 만들어지고, constructor의 this(= 컴포넌트 인스턴스)를 물려받습니다.
Constructor의 역할
constructor = 초기화 함수
클래스로 객체를 만들 때 자동으로 실행되는 특별한 함수입니다.
class Person {
constructor(name, age) {
console.log('constructor 실행!');
this.name = name; // 초기값 설정
this.age = age;
this.createdAt = new Date();
}
}
const person = new Person('철수', 25); // constructor 자동 실행!
React에서는:
constructor(props) {
super(props); // 부모 클래스 초기화 (필수!)
this.state = { count: 0 }; // 상태 초기화
this.handleClick = this.handleClick.bind(this); // 메서드 바인딩
}
실전 팁
1. React에서 this 문제 해결 방법
// ✅ 권장: 화살표 함수
handleClick = () => {
this.setState({ ... });
}
// ✅ 괜찮음: constructor에서 bind
constructor() {
this.handleClick = this.handleClick.bind(this);
}
// ⚠️ 비권장: render에서 화살표 함수 (매번 새 함수 생성)
<button onClick={() => this.handleClick()}>
2. setTimeout에서 this 잃어버리는 문제
// ❌ 문제
setTimeout(person.sayName, 1000); // undefined
// ✅ 해결 1: bind
setTimeout(person.sayName.bind(person), 1000);
// ✅ 해결 2: 화살표 함수
setTimeout(() => person.sayName(), 1000);
3. 요즘은 함수형 컴포넌트!
함수형 컴포넌트와 hooks를 사용하면 this 문제가 완전히 사라집니다.
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // this 걱정 없음!
};
return <button onClick={handleClick}>+</button>;
}
핵심 정리
- this는 호출 방식에 따라 결정됩니다
- 암시적 바인딩: obj.method() → this는 obj
- 명시적 바인딩: call/apply/bind로 this 직접 지정
- 화살표 함수: 자신의 this 없음, 상위 스코프의 this 사용
- 우선순위: new > bind > call/apply > 암시적 > 기본
- React: 화살표 함수나 bind로 this 고정 필요
this는 처음엔 어렵지만, 호출 방식만 정확히 파악하면 예측 가능합니다!
반응형
'개념이해 > javascript' 카테고리의 다른 글
| JavaScript 이벤트 루프: Task Queue vs Microtask Queue (0) | 2025.10.28 |
|---|