this를 포함한 property를 구조분해할당하면
this가 가리키는 객체가 다를 것이라는 것은 머릿 속으로는 알고 있었는데,
정리해보려고 남겨보는 글이다.
this를 포함한 구조 분해 할당의 문제와 해결 방법
구조 분해 할당은 객체나 배열에서 원하는 속성 또는 값을 추출해 별도의 변수로 할당하는 방식이다.
코드의 간결성과 가독성을 높이는데 자주 사용된다.
예시: 단순 속성 구조 분해
this
를 사용하지 않는 경우, 구조 분해 할당은 아무 문제 없이 동작한다.
함수형 예제
const obj = { a: 1, b: 2, c: 3 };
const { a, b } = obj;
console.log(a, b); // 출력: 1, 2
클래스 예제 (this 제외)
class MyClass {
constructor() {
this.name = 'MyClass';
}
}
const instance = new MyClass();
const { name } = instance; // 단순 속성만 구조 분해
console.log(name); // 출력: MyClass
하지만 메서드를 구조 분해하거나, this
와 관련된 속성을 다룰 경우, 예상치 못한 문제가 발생한다.
this를 구조 분해하면 왜 문제가 생길까?
문맥 상실 문제
this
에서 메서드를 구조 분해하면 해당 메서드는 원래 객체와의 연결을 잃게 된다.
JavaScript에서 this
는 함수가 호출되는 방식에 따라 결정되므로, 메서드를 분리하여 호출하면 엉뚱한 객체나 전역 문맥(global context)을 참조하게 된다.
문제가 발생하는 예제
class MyClass {
constructor() {
this.name = 'MyClass';
}
greet() {
console.log(`Hello from ${this.name}`);
}
}
const instance = new MyClass();
const { greet } = instance; // 메서드를 구조 분해
greet(); // 오류 발생: Cannot read properties of undefined
왜 이런 문제가 발생할까요?
greet
메서드가 원래 객체(instance
)와의 연결을 잃어버렸기 때문이다.- 엄격 모드(strict mode)에서는
this
가undefined
가 된다. - 비엄격 모드(sloppy mode)에서는
this
가 전역 객체(window
또는global
)를 참조한다.
문제 해결 방법
1. bind()
사용
this
를 명시적으로 바인딩하여 메서드가 항상 올바른 객체를 참조하도록 설정할 수 있다.
예제: bind()
const { greet } = instance;
const boundGreet = greet.bind(instance);
boundGreet(); // 출력: Hello from MyClass
2. 화살표 함수 사용
화살표 함수는 자신만의 this
를 가지지 않으며, 외부 문맥의 this
를 상속받는다.
예제: 화살표 함수
class MyClass {
constructor() {
this.name = 'MyClass';
this.greet = () => {
console.log(`Hello from ${this.name}`);
};
}
}
const instance = new MyClass();
const { greet } = instance;
greet(); // 출력: Hello from MyClass
3. 구조 분해 사용하지 않기
가장 단순한 방법은 메서드를 구조 분해하지 않고 원래 객체에서 직접 호출하는 것이다.
예제: 직접 호출
instance.greet(); // 출력: Hello from MyClass
4. 클래스 필드 사용
최신 JavaScript 문법(ECMAScript 2022 (ES13))에서는 클래스 필드를 사용하여 메서드를 자동으로 객체에 바인딩 할 수 있다.
예제: 클래스 필드
class MyClass {
name = 'MyClass';
greet = () => {
console.log(`Hello from ${this.name}`);
};
}
const instance = new MyClass();
const { greet } = instance;
greet(); // 출력: Hello from MyClass
this를 구조 분해해도 안전한 경우
메서드가 아닌 단순 속성을 구조 분해하는 경우에는 문제가 발생하지 않는다. 속성은 this
와 무관하게 동작하기 때문이다.
안전한 예제
const instance = { name: 'MyClass', version: 1.0 };
const { name, version } = instance;
console.log(name, version); // 출력: MyClass, 1.0
정리
this
를 구조 분해하면 다음과 같은 문제가 발생할 수 있다:
- 문제점: 메서드가 원래 객체와의 연결을 잃어
this
가 잘못된 값을 참조한다. - 해결 방법:
bind()
를 사용하여 명시적으로this
를 바인딩한다.- 화살표 함수를 사용하여 외부
this
를 상속받는다. - 메서드를 구조 분해하지 않고 객체에서 직접 호출한다.
- 최신 문법인 클래스 필드를 사용하여 자동으로 객체에 바인딩한다.
리액트 개발자 입장에서 React 16.8 훅 등장이후로는 함수형 컴포넌트가 주를 이룬다곤 하지만,
클래스형 컴포넌트를 맞닥뜨리지 않기 힘들 것이고 때로는 클래스형 컴포넌트가 필요한 경우들이 있을 것이니
당연한 내용이더라도 이렇게 정리해보는 게 좋을 것 같다.