class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
run (speed) {
this.speed = speed;
}
stop () {
this.speed = 0
}
}
let animal = new Animal('동물');
clas Rabbit extends Animal {
hide() {
}
}
let rabbit = new Rabbit()
클래스 Rabbit을 이용해 만든 객체는 Animal에 정의된 메서드에도 접근할 수 있습니다.
class Rabbit은 class Animal에 있는 메서드를 그대로 상속받습니다.
class Rabbit extends Animal {
stop() {
}
} //둘다 같은 메서드가 있을 경우, 가까이 있는 메서드를 사용
부모 메서드 전체를 교체하지않고 , 부모 메서드 일부만 변경하고 싶을 때나 확장하고 싶을 때에는 super
를 사용
super.method(...)
는 부모 클래스에 정의된 메서드, method를 호출super(...)
는 부모 생성자를 호출, 자식 생성자 내부에서만 사용 할 수 있음class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
run() {
this.speed =speed;
}
stop() {
this.speed = 0
}
}
class Rabbit extend Animal {
hide() {
}
stop() {
super.stop(); // 부모클래스의 stop을 호출해 멈춤
this.hide()
}
}
let rabbit = new Rabbit()
클래스가 다른 클래스를 상속받고 constructor가 없는 경우에는 아래처럼 비어있는 constructor가 만들어짐
class Rabbit extends Animal {
constructor(...args) {
super(...args);
}
}
constructor가 없으면 상속받은 Rabbit은 자동적으로 constructor와 인수를 모두 전달 받음
class Animal {
constructor(name) {
this.speed = 0;
this.name= name;
}
}
class Rabbit extends Animal {
constructor(name, earLength) {
this.speed = 0;
this.name = name;
this.earLength = earLength
}
} // 상속받는 객체가 super가 없기 떄문에 에러발생
자바스크립트는 '상속 클래스의 생성자 함수'와 그렇지 않은 생성자 함수를 구분함
상속 클래스 생성자 함수에는 특수 내부 프로퍼티인 [[ConstructorKind]]: "derived"
가있음
일반 클래스가 new와 함께 실행되면, 빈 객체가 만들어지고 this에 이 객체가 할당되지만 상속 클래스의 생성자 함수가 실행되면, 상속 클래스의 생성자 함수는 빈 객체를 만들고 this에 할당하는 일을 부모 클래스 생성자가 처리해주길 기대함
그래서 this에 객체가 들어오지않기 떄문에 에러가 발생
class Animal {
name = 'animal'
constructor() {
alert(this.name)
}
}
class Rabbit extends Animal {
name = 'rabbit' // 오버라이딩
}
new Animal(); // animal
new Rabbit(); // animal
부모생성자는 자식 클래스에서 오버라이딩한 값이 아닌, 부모 클래스 안의 필드 값을 사용한다
class Animal {
showName() {
alert('animal')
}
constructor() {
this.showName();
}
}
class Rabbit extends Animal {
showName() { //오버라이딩
alert('rabbit')
}
}
new Animal(); // animal
new Rabbit(); // rabbit
위에 것과 차이 발생, 이유는 필드 초기화 순서 때문
super()
실행 직후에 초기화new Rabbit을 실행하면 super()가 호출되고 부모 생성자가 실행, 하위 클래스 필드 초기화 순서에 따라 하위 클래스 Rabbit의 필드는 super()실행 이후에 초기화됨
let animal = {
name : "동물",
eat() {
alert(`${this.name} 이/가 먹이를 먹습니다.`)
}
};
let rabbit = {
__proto__ : animal,
name: "토끼",
eat() {
this.__proto__.eat.call(this);
} // eat의 프로토타입 (animal)을 가져오고 현재 객체 컨택스트 기반하여 eat을 호출
}
rabbit.eat() // 토끼 이/가 먹이를 먹습니다.
let animal = {
name: "동물",
eat() {
alert(`${this.name} 이/가 먹이를 먹습니다.`);
}
};
let rabbit = {
__proto__: animal,
eat() {
// call을 사용해 컨텍스트를 옮겨가며 부모(animal) 메서드를 호출합니다.
this.__proto__.eat.call(this); // (*)
}
};
let longEar = {
__proto__: rabbit,
eat() {
// longEar를 가지고 무언가를 하면서 부모(rabbit) 메서드를 호출합니다.
this.__proto__.eat.call(this); // (**)
}
};
longEar.eat(); // RangeError:
모든 객체 메서드는 프로토타입등이 아닌 현재 객체 this로 가짐, 그래서 this.__proto__
에는 같은 값, rabbit이 할당됨
체인위로 올라가지않고 모두 rabbit.eat을 호출하기 때문에 무한 루프에 빠짐
이런 문제를 해결하기 위해 함수 전용 특수 내부 프로퍼티 [[HomeObject]] 사용
클래스 이거나 객체 메서드인 함수의 [[HomeObject]]프로퍼티는 해당 객체가 저장
super는 [[HomeObject]]를 이용해 부모 프로토타입과 메서드를 찾음
[[HomeObject]]는 오직 super 내부에서만 유효
let animal = {
name: "동물",
eat() { // animal.eat.[[HomeObject]] == animal
alert(`${this.name} 이/가 먹이를 먹습니다.`);
}
};
let rabbit = {
__proto__: animal,
name: "토끼",
eat() { // rabbit.eat.[[HomeObject]] == rabbit
super.eat();
}
};
let longEar = {
__proto__: rabbit,
name: "귀가 긴 토끼",
eat() { // longEar.eat.[[HomeObject]] == longEar
super.eat();
}
};