자바스크립트의 상속과 프로토타입 체인(JavaScript Inheritance and the Prototype Chain)

나연·2020년 5월 9일
0

Javascript

목록 보기
8/8
post-thumbnail

Youtube | JavaScript Inheritance and the Prototype Chain - Tyler McGinnis 영상을 기반으로 작성한 글입니다.

🤷‍♀️어떻게 다른 class를 손쉽게 참조할까?

의문의 시작은 '정의해 놓은 함수와 프로토타입 체인을 이용해 어떻게 다른 class를 만들 수 있을까?' 하는 것에서 부터 시작한다. 예를 들어 아래의 Animal라는 class와 같은 속성을 가지고 있지만 bark라는 속성이 추가 된 dog이라는 새로운 class를 생성해야 하는 것이다.

ES5

function Animal (name, energy) {
  this.name = name
  this.energy = energy
} 
Animal.prototype.eat = function (amount) {
  console.log (`${this.name} is eating.`)
  this.energy += amount
}
Animal.prototype.sleep = function (length) {
  console.log (`${this.name} is sleeping.`)
  this.energy += length
}  
Animal.prototype.play = function (length) {
  console.log (`${this.name} is playing.`)
  this.energy -= length
}

1. 원래있던 class를 그대로 복붙, 원하는 속성을 더해준다.

function Dog (name, energy,breed) { //Animal -> Dog로 수정
  this.name = name
  this.energy = energy
  this.breed = breed 
} 
Dog.prototype.eat = function (amount) {
  console.log (`${this.name} is eating.`)
  this.energy += amount

Dog.prototype.sleep = function (length) {
  console.log (`${this.name} is sleeping.`)
  this.energy += length
  
Dog.prototype.play = function (length) {
  console.log (`${this.name} is playing.`)
  this.energy -= length
  
Dog.prototype.bark = function(){ //bark 속성을 추가 
  console.log (`Woof Woof`)
  this.energy -= .1
}

const Charlie = new Dog('Charlie', 10, 'Goldendoodle')

Animal 함수를 그대로 가져와서 Dog이라고 이름만 바꿔준 다음에 원하는 속성을 추가해줬다. 근데 또 Cat이라는 class를 만들고 싶으면 어떡하지?? 또 전부 다 복사해서 cat의 특정 속성을 추가하는 방법을 사용해야 할까? Bird를 만들때도, Tiger를 만들 때도? 우리는 이 방법이 너무 번거롭다는 사실을 파악할 수 있다.

👆우리는 Animal이라는 이미 생성되어 있는 기본 class를 기반으로하는 다른 동물들의 class를 쉽게 만들고 싶다.
그냥 Dog를 만들때 animal의 constructor 함수를 그대로 복사해오는 방법은 없을까?

2. 더 간편하게

1. call 메소드를 활용해서 함수 호출하기

function Dog(name,energy,breed){
	Animal.call(this, name, energy) //Animal를 불러온다.
    this.breed = breed //원하는 속성을 정의해준다.

  const charlie = new Dog('Charlie', 10, 'Goldendoodle')

call을 이용하여 Dog함수 안에서 Animal 함수를 불러온 것을 확인할 수 있다.

그런데 charlie.eat을 실행해보면 eat이 이미 Animal class에 정의 된 속성임에도 불구하고 undefined가 출력된다. Animal 함수에 정의 된 속성들은 모두 불렀지만, Animal.prototype으로 연결해준 메소드들은 불러내지 못한 것이다. 그럼 Dog의 인스턴스가 Animal.prototype에 접근할 수 있게 하면 어떨까?

2. Object.create로 prototype을 복사해주기

Object.create를 이용하여 할 수 있다.

Object.create()란?
괄호 안에 들어가는 프로토타입 객체 및 속성을 가지는 새 객체를 만드는 메소드이다. 상위클래스의 메서드를 그대로 확장한 하위클래스의 객체를 생성할 때 사용한다. 반드시 하위클래스의 프로토타입의 constructor를 지정해주어야 한다. (Object.create()를 사용하면 프로토타입을 그대로 복사할 수 있지만 constructor 또한 함께 복사되어 상위클래스의 constructor로 바뀌게 된다.

function Dog(name,energy,breed){
	Animal.call(this, name, energy) 
    this.breed = breed 
  
Dog.prototype = Object.create(Animal.prototype)

const charlie = new Dog('Charlie', 10, 'Goldendoodle')  

charlie.play(4) //'Charlie is playing.' --> Dog에는 정의 되지 않은 play라는 속성을 사용할 수 있다. 

Dog에 bark라는 기능을 추가하고 싶다면 해당 코드를 추가해주기만 하면 된다.

Dog.prototype.bark = function(){
  console.log (`Woof Woof`)
  this.energy -= .1
}

3. Constructor 지정 해주기


이때 charlie라는 인스턴스는 Animal의 인스턴스가 되었지만 charlie의 constructor가 Dog가 아닌 Animal로 바뀌었다. Dog의 인스턴스인 charlie가 constructor는 Animal이라니? 어떻게 된 일이지?

Dog.prototype = Object.create(Animal.prototype)

위의 코드에서 활용한 Object.create는 Dog의 프로토타입에 새로운 constructor를 생성해주는 것이 아닌, Animal의 프로토타입을 그대로 위임해준다. 따라서 Animal constructor를 그대로 따라가게 되는 것이다.

Dog.prototype.constructor = Dog

다시 말해 constructor를 재정의해주는 과정이 필요하고 위의 코드를 추가하여 constructor를 지정해줘야 한다.

ES6

같은 동작을 ES6 class문법을 통해 더 간단하게 구현할 수 있다.

class Animal {
  constructor(name, energy) {
  this.name = name
  this.energy = energy
  } 
  eat(amount) {
  console.log (`${this.name} is eating.`)
  this.energy += amount
  }
  sleep() {
  console.log (`${this.name} is sleeping.`)
  this.energy += length
  }  
  play() = function (length) {
  console.log (`${this.name} is playing.`)
  this.energy -= length
  }
}
class Dog {
  constructor(name,energy,breed) {
    this.breed = breed
  }
  bark() {
    console.log ('Woof Woof') 
    this.energy -= .1
  }
}

Extends를 활용한 상속

Dog가 Animal의 속성을 그대로 포함하도록 하기 위해서 ES6에서는 정말 간단한 문법이 필요하다. extends라는 키워드를 사용하면 복사하고 싶은 class로 확장시킬 수 있다.

class SubClass extends BaseClass

이와 같은 원리로 코드를 수정해보면,

class Dog extends Animal { //해당 부분을 수정해주면 된다!
  constructor(name,energy,breed) {
    this.breed = breed
  }
  bark() {
    console.log ('Woof Woof') 
    this.energy -= .1
  }
}

그럼 Animal에만 있는 속성인 name,energy를 그대로 사용하고 싶은데 어떡하면 좋을까? ES5에서 call을 사용한 것 처럼 ES6에서는 super키워드를 사용하면 된다.

super는 우리가 extend한 constructor function을 부른다. 여기서는 Animal의 constructor function을 부른 것이다.

class Dog extends Animal { 
  constructor(name,energy,breed) {
   	super(name,energy) //super 키워드 추가
    this.breed = breed
  }
  bark() {
    console.log ('Woof Woof') 
    this.energy -= .1
  }
}
profile
아름다운 상상을 실현하는 개발자입니다🌈🤍

0개의 댓글