화살표 함수를 super 키워드로 호출하면 에러가 발생하는 이유
2022.06.14
들어가며
클래스를 사용하다 보면 화살표 함수와 일반 함수의 동작이 다른 경우를 종종 마주치게 됩니다. 특히 super 키워드를 사용할 때 이러한 차이가 두드러지게 나타납니다.
아래 코드는 Fruit라는 클래스를 상속한 Orange 클래스에서 super 키워드를 사용해서 부모 클래스에서 정의된 함수를 호출하는 코드입니다.
이 때 정의된 두 함수 중 하나는 일반함수로, 그리고 또 다른 함수는 화살표 함수로 정의되어 있습니다.
class Fruit {
generalPrint() {
console.log("general");
}
ArrowPrint = () => {
console.log("arrow");
};
}
class Orange extends Fruit {
print() {
super.generalPrint(); // 정상 동작
super.ArrowPrint(); // TypeError: (intermediate value).ArrowPrint is not a function
}
}
const orange = new Orange();
orange.print();위 코드에서 super.generalPrint()는 정상적으로 동작하지만, super.ArrowPrint()를 호출하면 에러가 발생합니다.
이러한 문제가 발생하는 이유는 클래스에서 메서드를 정의하는 방식이 다르기 때문입니다.
클래스에서 메서드를 정의하는 방식
클래스에서 메서드를 정의할 때는 두 가지 방식을 사용할 수 있습니다:
- 클래스 메서드 방식 (일반 함수)
class Fruit {
generalPrint() {
// 프로토타입에 정의됨
console.log("general");
}
}- 클래스 필드 방식 (화살표 함수)
class Fruit {
ArrowPrint = () => {
// 인스턴스에 정의됨
console.log("arrow");
};
}클래스 메서드와 클래스 필드의 특징
각 방식의 특징은 다음과 같습니다:
- 클래스 메서드의 특징
- 프로토타입에 정의됨
- 모든 인스턴스가 공유
super키워드로 접근 가능
- 클래스 필드의 특징
- 각 인스턴스에 개별적으로 정의됨
- 인스턴스마다 독립적인 복사본 생성
super키워드로 접근 불가
Babel로 변환된 코드를 보면 이 차이를 더 명확하게 이해할 수 있습니다:
// Babel로 변환된 코드
class Fruit {
constructor() {
// 클래스 필드는 생성자에서 초기화됨
this.ArrowPrint = () => {
console.log("arrow");
};
}
// 클래스 메서드는 프로토타입에 정의됨
generalPrint() {
console.log("general");
}
}실제 객체의 구조를 출력해보면 다음과 같습니다:
const fruit = new Fruit();
console.log(fruit);
// {
// ArrowPrint: () => {...} // 인스턴스 프로퍼티
// }
console.log(Fruit.prototype);
// {
// generalPrint: f() // 프로토타입 메서드
// }super 키워드는 프로토타입 체인을 통해서만 메서드를 찾을 수 있습니다.
그렇기 때문에 요약하면 다음과 같습니다:
generalPrint는 프로토타입에 있으므로super로 접근 가능ArrowPrint는 인스턴스 프로퍼티이므로super로 접근 불가
해결 방법
이 문제는 다음과 같은 방법으로 해결할 수 있습니다:
- 일반 메서드로 정의하기
class Fruit {
ArrowPrint() {
// 화살표 함수 대신 일반 메서드 사용
console.log("arrow");
}
}- this를 사용하여 접근하기
class Orange extends Fruit {
print() {
this.ArrowPrint(); // super 대신 this 사용
}
}this로 ArrowPrint에 접근할 수 있는 이유는 상속의 동작 방식 때문입니다:
Orange클래스가Fruit를 상속할 때,Fruit의constructor가 먼저 실행됩니다.- 이때
Fruit의constructor에서this.ArrowPrint가 정의됩니다. - 따라서
Orange인스턴스는 이미ArrowPrint프로퍼티를 가지고 있게 됩니다. this.ArrowPrint()를 호출하면 자신의 프로퍼티인ArrowPrint를 찾아 실행합니다.
결론
화살표 함수와 일반 함수의 이러한 차이는 JavaScript의 클래스 구현 방식과 프로토타입 기반 상속의 특성에서 비롯됩니다. 요약하자면:
- 일반 메서드는 프로토타입에 정의되어 상속 체인을 통해 접근 가능
- 화살표 함수로 정의된 클래스 필드는 각 인스턴스에 개별적으로 생성되어 상속 체인으로 접근 불가
이러한 차이를 이해하고 적절한 방식을 선택하는 것이 중요합니다.