자바스크립트

Section 5. 자바스크립트 상속

포칼이 2023. 3. 30. 11:40

상속 inheritance

  • 서로 다른 클래스나 생성자 함수가 같은 속성들을 공유할 때 이들의 관계를 정의함으로써 코드의 중복을 줄이고 효율을 높임
  • "B클래스는 A클래스에서 파생된다." 즉, B는 A의 하위분류가 된다. 

I. 클래스의 상속 문법

그림과 같이 클래스를 만들어 보면 다음과 같다.

class Bird {
  wings = 2;
}
class Eagle extends Bird {
  claws = 2;
}
class Penguin extends Bird {
  swim () { console.log('수영중...'); }
}
class EmperorPenguin extends Penguin {
  size = 'XXXL';
}

Bird라는 클래스는 wings 프로퍼티가 있고 Eagle 클래스는 Bird에서 파생이 됐기 때문에 Bird를 상속을 받고 claws라는 프로퍼티를 갖는다. 

즉, Eagle은 Bird의 프로퍼티인 wings프로퍼티를 가지면서 claws 프로퍼티를 가지는 것이다.

이런식으로 Penguin과 EmperorPenguin도 같은 원리로 상속이 이루어진다. 

const birdy = new Bird();
const eaglee = new Eagle();
const pengu = new Penguin();
const pengdol = new EmperorPenguin();

console.log(birdy, eaglee, pengu, pengdol);
//Bird {wings: 2}
//Eagle {wings: 2, claws: 2} 
//Penguin {wings: 2} 
//EmperorPenguin {wings: 2, size: 'XXXL'}

인스턴스의 관계를 예시에서 살펴보면 다음과 같다.

for (const i of [
  [ '1.', birdy instanceof Bird ],
  [ '2.', eaglee instanceof Bird ],
  [ '3.', eaglee instanceof Eagle ],
  [ '4.', pengdol instanceof Penguin ],
  [ '5.', pengdol instanceof Bird ],
  [ '6.', birdy instanceof Eagle ]
]) {
  console.log(i[0], i[1]);
}
//1. true
//2. true
//3. true
//4. true
//5. true
//6. false

instanceof 연산자를 사용하면 그 뒤에 해당 인스턴스의 조상이 왔을 경우에는 true를 반환하는 걸 볼 수 있다. 

pengu.swim(); //수영중...
pengdol.swim(); //수영중...
eaglee.swim(); //애러

상속관계를 잘 따라왔다면 eaglee는 swim을 하지 못한다는 것을 알 수 있다.

이유는 Penguin에 swim 기능이 있는데 eaglee는 Penguin에서 파생(상속)된 것이 아니기 때문에 그 기능이 없는 것이다.

 

정리하자면 다음과 같다.

  • 클래스에서는 extends (부모클래스)로 상속관계 정의한다.
  • 자식 클래스에서 또 다른 클래스가 상속받을 수 있다. 
  • 자식 클래스는 부모 클래스의 속성을 기본적으로 가져온다.
  • 자식 클래스의 인스턴스는 부모 클래스의 인스턴스로 인식된다.
  • *프로토타입을 살펴보면 최종적으로 Object인 것을 확인할 수 있다. Object는 모든 클래스의 부모이다. 

II. 오버라이딩 overriding

자식 클래스에서 부모로부터 물려받은 속성이나 기능을 덮어쓰는 것을 오버라이딩이라 한다.

class Bird {
  wings = 2;
  canFly = true;
  travel () { console.log('비행중...') }
}
class Eagle extends Bird {
  claws = 2;
}
class Penguin extends Bird {
  canFly = false;
  travel () { console.log('수영중...') }
}

const eaglee = new Eagle();
const pengu = new Penguin();

console.log(eaglee);
eaglee.travel();

console.log(pengu); //Penguin {wings: 2, canFly: false}
pengu.travel(); //수영중...

부모 클래스인 Bird클래스에서 물려받은 것을 Penguin 클래스에서 오버라이딩 하여 물려받은 기능이 바뀐 것을 볼 수 있다. 

 

III. super

부모 클래스의 constructor 또는 메서드를 호출한다.

class KangChicken {
  no = 0;
  menu = { '후라이드': 10000, '양념치킨': 12000 };

  constructor (name, no) {
    this.name = name;
    if (no) this.no = no;
  }
  introduce () {
    return `안녕하세요, ${this.no}호 ${this.name}점입니다!`;
  }
  order (name) {
    return `${this.menu[name]}원입니다.`
  }
}
class ConceptKangChicken extends YalcoChicken {
  #word = '';
  constructor (name, no, word) {
    super(name, no);
    this.#word = word;
  }
  introWithConcept () {
    return super.introduce() + ' ' + this.#word;
  }
  order (name) {
    return super.order(name) + ' ' + this.#word;
  }
}

const pikaChain = new ConceptKangChicken('도봉', 50, '피카피카~');

ConceptKangChicken클래스를 보면 constructor안에 super(name, no) 가 있다. 이렇게 super를 쓰면 부모 클래스의 constructor를 호출한다고 생각하면 된다. 

즉, super(name, no)를 하면 부모클래스의 constructor에서 name과 no가 설정이 되고 word가 추가적으로 설정이 되는 것이다. 

똑같은 원리로 introWithConcept 함수를 살펴보면 super.introduce() 라고 되어 있는데 부모의 introduce 함수를 호출하는 것이 된다. 

 

출력문을 살펴보면 다음과 같다.

console.log(pikaChain);
//ConceptKangChicken {no: 50, menu: {…}, name: '도봉', #word: '피카피카~'}

console.log(pikaChain.introWithConcept());
//안녕하세요, 50호 도봉점입니다! 피카피카~

console.log(pikaChain.order('후라이드'));
//10000원입니다. 피카피카~

정리하자면 다음과 같다.

  • super는 다른 클래스에서 상속받은 클래스에서만 사용이 가능하다
  • 자식 클래스의 constructor 내에서는 부모 클래스의 constructor를 가리킨다
  • 자식 클래스의 메서드 내에서는 부모 클래스를 가리킨다
  • super는 부모 클래스의 constructor나 메서드에 추가적인 동작을 넣기 위해 사용한다