[javascript] 클래스 상속

YIM_JI·2023년 3월 28일
0

javascript

목록 보기
10/15
post-thumbnail

상속

아래 코드를 보면 class Bird는 class Animal의 속성을 그대로 사용하고 자신만의 고유한 속성을 추가해서 확장했다. 이와 같이 상속으로 클래스를 확장하면 코드 재사용에 매우 유용하다.

class Animal {
  constructor(weight, age) {
    this.weight = weight;
    this.age = age;
  }

  eat() { return 'eat'; }

  move() { return 'move'; }
}

class Bird extends Animal {
  fly() { return 'fly'; }
}

const bird = new Bird(100, 10);

console.log(Bird);                    // { weight: 100, age: 10 };
console.log(bird instanceof Bird);    // true
console.log(bird instanceof Animal);  // true

console.log(bird.eat());              // eat
console.log(bird.move());             // move
console.log(bird.fly());              // fly


extends 키워드

상속을 통해 클래스를 확장하려면 extends 키워드를 사용하여 상속받을 클래스를 정의한다.

// super class
class Base {}

// sub class
class Derived extends Base {}

상속을 통해 확장된 클래스를 서브클래스라고 하고, 서브클래스에게 상속된 클래스를 수퍼클래스라고 한다. 서브클래스를 파생, 자식이라고도 하고 수퍼클래스를 베이스, 부모 클래스라고도 한다.

extends 키워드의 역할은 수퍼클래스와 서브클래스 간의 상속 관계를 설정하는 것이다. 클래스도 프로토타입을 통해 상속 관계를 구현한다.



서브클래스의 constuctor

클래스에 constructor를 생략하면 클래스에 다음과 같이 비어 있는 constructor가 암묵적으로 정의된다.

constructor() {}

서브클래스에서 constructor를 생략하면 클래스에 다음과 같은 constructor가 암묵적으로 정의된다. args는 new 연산자와 함께 클래스를 호출할 때 전달한 인수의 리스트다.

constructor(...args) { super(...args); }

super()는 수퍼클래스의 constructor(super-constructor)를 호출하여 인스턴스를 생성한다.
즉, 부모 클래스의 생성자 함수를 호출한다.



super 키워드

super 키워드는 함수처럼 호출할 수도 있고 this와 같이 식벽자처럼 참조할 수 있는 특수한 키워드다. super는 다음과 같이 동작한다.

  • super를 호출하면 수퍼클래스의 생성자함수를 호출한다.
  • super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.

super 호출

super를 호출하면 수퍼클래스의 constructor(super-constructor)를 호출한다.

서브 클래스의 constructor를 생략했을 때, new 연산자와 함께 서브클래스를 호출하면서 전달한 인수는 모두 서브클래스에 암묵적으로 정의된 constructor의 super 호출을 통해 수퍼클래스의 constructor에 전달된다.

// 수퍼클래스
class Base {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }
}

// 서브클래스
class Derived extends Base {
  // 다음과 같이 암묵적으로 constructor가 정의된다.
  // constructor(...args) { super(...args); }
}

const derived = new Derived(1, 2);
console.log(derived); // Derived {a: 1, b: 2}

또 수퍼클래스의 변수를 사용하면서 서브클래스의 고유한 변수를 갖고 싶다면 다음과 같이 하면 된다.

// 수퍼클래스
class Base {
  constructor(a, b) { // ④
    this.a = a;
    this.b = b;
  }
}

// 서브클래스
class Derived extends Base {
  constructor(a, b, c) { // ②
    super(a, b); // ③
    this.c = c;
  }
}

const derived = new Derived(1, 2, 3); // ①
console.log(derived); // Derived {a: 1, b: 2, c: 3}

💡 super를 호출할 때 주의할 사항은 다음과 같다.
1. 서브클래스에서 constructor를 생략하지 않는 경우 서브클래스의 constructor에는 반드시 super를 호출해야 한다.
2. 서브클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.
3. super는 반드시 서브클래스의 constructor에서만 호출한다. 서브클래스가 아닌 클래스의 constructor나 함수에서 super를 호출하면 에러가 발생한다.


super 참조

메서드 내에서 super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.

  1. 서브클래스의 프로토타입 메서드 내에서 super.sayHi는 수퍼클래스의 프로토타입 메서드sayHi를 가리킨다.
// 수퍼클래스
class Base {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return `Hi! ${this.name}`;
  }
}

// 서브클래스
class Derived extends Base {
  sayHi() {
    // super.sayHi는 수퍼클래스의 프로토타입 메서드를 가리킨다.
    return `${super.sayHi()}. how are you doing?`;
  }
}

const derived = new Derived('Lee');
console.log(derived.sayHi()); // Hi! Lee. how are you doing?
  1. 서브클래스의 정적 메서드 내에서 super.sayHi는 수퍼클래스의 정적 메서드 sayHi를 가리킨다.
// 수퍼클래스
class Base {
  static sayHi() {
    return 'Hi!';
  }
}

// 서브클래스
class Derived extends Base {
  static sayHi() {
    // super.sayHi는 수퍼클래스의 정적 메서드를 가리킨다.
    return `${super.sayHi()} how are you doing?`;
  }
}

console.log(Derived.sayHi()); // Hi! how are you doing?


상속 클래스의 인스턴스 생성 과정

💡 상속 클래스의 인스턴스 생성 과정(딥다이브 p462 부터 참고)

  1. 서브클래스의 super호출
  2. 수퍼클래스의 인스턴스 생성과 this 바인딩
  3. 수퍼클래스의 인스턴스 초기화
  4. 서브 클래스 constructor로의 복귀와 this바인딩
  5. 서브클래스의 인스턴스 초기화
  6. 인스턴스 반환
  1. 서브클래스는 자신이 직접 인스턴스를 생성하지 않고 수퍼클래스에게 인스턴스 생성을 위임한다. 이것이 바로 서브클래스의 constructor에서 반드시 super를 호출해야 하는 이유다.
    만약 서브클래스 constructor 내부에 super 호출이 없으면 에러가 발생한다. 실제로 인스턴스를 생성하는 주체는 수퍼블래스이므로 수퍼클래스의 constructor를 호출하는 super가 호출되지 않으면 인스턴스를 생성할 수 없기 때문이다.

4.super의 호출이 종료되고 제어 흐름이 서브클래스 constructor로 돌아온다. 이때 super가 반환한 인스턴스가 this에 바인딩된다. 서브클래스는 별도의 인스턴스를 생성하지 않고 super가 반환한 인스턴스를 this에 바인딩하여 그대로 사용한다.

// 서브클래스
class ColorRectangle extends Rectangle {
  constructor(width, height, color) {
    super(width, height);

    // super가 반환한 인스턴스가 this에 바인딩된다.
    console.log(this); // ColorRectangle {width: 2, height: 4}
...

이처럼 super가 호출되지 않으면 인스턴스가 생성되지 않으며, this 바인딩도 할 수 없다. 서브클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없는 이유가 바로 이 때문이다.

즉, 서브클래스의 constructor안에서 super를 호출하면 수퍼클래스에서의 constructor에서는 this는 수퍼클래스에 바인딩 되는 것이 아니라 서브클래스에 바인딩된다. 그리고 그 this를 return하여 서브클래스가 쓸 수 있도록 한다.

0개의 댓글