본문 바로가기

Frontend Study

[FE_Bootcamp] 23일차_프로토타입 체인

프로토타입을 이용한 상속 방법

 

우리는 어제 프로토타입에 대해 공부를 했다.

간략하게 요약해 보자면

  • 자바스크립트는 프로토타입 기반 언어
  • 객체를 상속할 때, 메서드들은 프로토타입에 저장되었다가 하위 객체로 전달
  • 상속하는 객체의 프로토타입은 prototype 메서드로 접근
  • 상위 객체의 프로토타입은 __proto__ 메서드로 접근

대강 이런 내용을 공부하였다.

이번 시간에는 단순히 상위 객체가 아닌, 몇단계 위에 있는 객체들은 어떻게 상속하는지에 대해 알아보도록 하자.

 

4. 프로토타입 체인

A. extends를 이용한 상속

이번에는 새로운 예시를 들어보자.

 

class Band {
  constructor(name, vocal, guitar, bass, drum){
    this.name = name
    this.vocal = vocal
    this.guitar = guitar
    this.bass = bass
    this.drum = drum
  }
  member(){
    console.log(`${this.name}의 멤버는 보컬 ${this.vocal}, 
    기타 ${this.guitar}, 베이스 ${this.bass}, 드럼 ${this.drum} 입니다.`) 
  }
}


class RockBand extends Band{
  constructor(name, vocal, guitar, bass, drum, debut){
    this.name = name
    this.vocal = vocal
    this.guitar = guitar
    this.bass = bass
    this.drum = drum
    this.debut = debut
  }
  debutYear(){
    console.log(`${this.name}의 데뷔 년도는 ${this.debut}년입니다.`)
  }
}

class Queen extends RockBand{
  constructor(name, vocal, guitar, bass, drum, debut, representative){
    this.name = name
    this.vocal = vocal
    this.guitar = guitar
    this.bass = bass
    this.drum = drum
    this.debut = debut
    this.representative = representative
  }
  repreSong(){
    console.log(`${this.name}의 대표곡은 ${this.representative}입니다.`)
  }
}

 

Band를 상속한 Rockband 클래스가 존재하고, 이 Rockband를 상속한 Queen이 존재한다.

이때, 상속은 extends라는 키워드를 통해 이루어지며, extends 뒤에 상속할 클래스를 사용해 해당 클래스의 메서드들을 상속받게 된다.

 

그렇다면, 먼저 Rockband 클래스를 만들어 보자.

let newBand = new Rockband('Beatles', '존 레논', '조지 해리슨', '폴 매카트니', '링고 스타', 1960)

 

오류가 난다

콘솔에 입력하자마자 에러가 뜨는 모습을 볼 수 있다.

에러 내용을 확인해 보면, 대충 '파생 클래스에서 super constructor를 불러와야 한다'라는 것 같은데.,.,,.

스택오버플로우에 해당 에러에 대한 질문이 있어 찬찬히 읽어보았다.

 

에러에 대한 답변

  • 하위 클래스 생성자에서는 super를 호출할 때까지 이 값을 사용할 수 없습니다.
  • ES6 클래스 생성자는 하위 클래스인 경우 super를 호출해야 하며, 초기화되지 않은 개체를 대체하려면 개체를 명시적으로 반환해야 합니다.

요약하자면, 'super'라는 것을 호출하지 않으면 상위 객체에서 this.로 정의된 값들을 사용할 수 없다는 것이었다.

대체 super가 뭐길래!

 

B. super()

super() 키워드는 '부모 오브젝트의 생성자 함수를 호출할 때 사용'된다.

위 예시같은 경우, RockBand에서 super를 사용하면, Band의 constructor 함수를 불러올  수 있다는 것이다.

 

class RockBand extends Band{
  constructor(name, vocal, guitar, bass, drum, debut){
    this.name = name
    this.vocal = vocal
    this.guitar = guitar
    this.bass = bass
    this.drum = drum
    this.debut = debut
  }
  debutYear(){
    console.log(`${this.name}의 데뷔 년도는 ${this.debut}년입니다.`)
  }
}

class RockBand extends Band{
  constructor(name, vocal, guitar, bass, drum, debut){
    super(name, vocal, guitar, bass, drum, debut)
    this.debut = debut
  }
  debutYear(){
    console.log(`${this.name}의 데뷔 년도는 ${this.debut}년입니다.`)
  }
}

 

즉, 위 코드와 아래 코드가 동일한 역할을 한다는 것이다.

하지만, this로 정의된 몇몇 변수들은 super 연산자를 불러오지 않으면 사용할 수 없으므로, 아래 코드만 정상 작동할 것이다.

 

정상 작동한다

 

실제로, 코드를 바꾸니 제대로 작동하는 모습을 보였다.

그렇다면 맨 위의 코드도 전부 super을 이용해 다시 써보자.

 

class Band {
  constructor(name, vocal, guitar, bass, drum){
    this.name = name
    this.vocal = vocal
    this.guitar = guitar
    this.bass = bass
    this.drum = drum
  }
  member(){
    console.log(`${this.name}의 멤버는 보컬 ${this.vocal}, 
    기타 ${this.guitar}, 베이스 ${this.bass}, 드럼 ${this.drum} 입니다.`) 
  }
}


class RockBand extends Band{
  constructor(name, vocal, guitar, bass, drum, debut){
    super(name, vocal, guitar, bass, drum)
    this.debut = debut
  }
  debutYear(){
    console.log(`${this.name}의 데뷔 년도는 ${this.debut}년입니다.`)
  }
}

class Queen extends RockBand{
  constructor(name, vocal, guitar, bass, drum, debut, representative){
    super(name, vocal, guitar, bass, drum, debut)
    this.representative = representative
  }
  repreSong(){
    console.log(`${this.name}의 대표곡은 ${this.representative}입니다.`)
  }
}

 

C. 프로토타입 체인

이제 본격적으로 새로운 Queen 객체를 만들어 보자.

 

새로운 queen 객체

 

자신이 가진 메서드 repreSong은 물론이고 상위 클래스의 debutYear 역시 정상 작동하는 것을 볼 수 있다.

그런데, 자기 자신과 부모 클래스에도 없는 member() 메서드 역시 문제없이 작동된다.

분명 상속받은 클래스는 RockBand이고, RockBand에는 member() 메서드가 없는데,..,.,

 

당황할 것 없다. 이는 RockBand가 상속받은 Band 클래스에 member() 메서드가 존재하기 때문이다.

 

객체들은 각각의 프로토타입을 가지고 있고, 각 프로토타입끼리는 연결어 있다.

따라서, 어떤 메서드를 불러왔을 때, 상위 객체의 프로토타입에 메서드가 존재하지 않는다면 그 위에 있는 객체의 프로토타입에서 메서드를 찾는다는 것이다.

 

프로토타입 체인

 

이렇게 프로토타입끼리 서로 연결되어 있는 것을 '프로토타입 체인'이라고 한다.

클래스의 경우, 프로토타입 체인의 가장 상위에는 'Object' 객체가 존재한다.

이 덕분에 Object의 프로토타입에 들어있는 메서드들을 사용 가능한 것이다.

 

Object 객체의 메서드 valueOf()를 사용했을 때

 

반대로, Object 객체에 Array 객체의 프로토타입 메서드를 사용하면 당연히 사용 불가능하다.

 

Array 객체의 메서드 map()을 사용했을 때.

 

한 객체의 상위 객체. 즉, 프로토타입 체인의 구성 요소들을 보고 싶으면 __proto__ 메서드를 이용한다.

 

 

최상위 객체인 Object 위에는 아무것도 존재하지 않으므로, null이 출력되는 것을 볼 수 있다!