javascript 상속 방법 비교

지영·2021년 12월 28일
0

JavaScript

목록 보기
19/37
post-thumbnail

javascript 상속 방법 비교


생성자 함수 & 클래스

ES6 로 넘어오며 class개념을 등장시켜 적극적으로 객체 지향을 지원하기 전까지, 객체 지향 프로그래밍 구현에 가장 자주 쓰였던 방식은 pseudoclassical inheritance 방식이다.

💡pseudoclassical inheritance

사전을 찾아보면 pseudo 는 '가짜의' 라는 의미가 있다.
pseudoclassical inheritance 는 가짜로 class 개념을 흉내내는 상속 방식 이라는 뜻인 것 같다.(아마도?)


pseudoclassical inheritance

👩🏻‍💻Child.prototype = Parent.prototype

✍️부모 생성자 함수의 프로토 타입을 자식 생성자 함수의 프로토 타입으로 설정해보자.

function Parent(name, age) {
  this.name = name;
  this.age = age;
}

Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child(name, age) {
  this.name = name;
  this.age = age;
}

Child.prototype = Parent.prototype;

const parent = new Parent("지영", 35);
parent.sayName(); // 지영

const child = new Child("철수", 2);
child.sayName(); // 철수
  • 이 방식이 완전히 틀렸다고 말할 수는 없다.
  • 하지만 부모 생성자 함수의 prototype을 자식 생성자 함수에 prototype에 그대로 설정할 경우, 부모 생성자 함수의 주소 값(참조 값)까지 그대로 가져와서 두 함수가 동일시되어 버린다.
    즉, 두 함수가 연동되어 자식 생성자 함수에 속성을 추가하면 부모 생성자 함수에도 속성이 추가된다.
  • 따라서 위와 같은 방법은 자식 prototype에서 개별적인 추가나 수정이 불가하다.

👩🏻‍💻Child.prototype = { ...Parent.prototype }

✍️ 자식 생성자 함수의 프로토타입에 부모 생성자 함수의 프로토타입을 가지고 있는 객체를 대입해보자.

function Parent(name, age) {
 this.name = name;
 this.age = age;
}

Parent.prototype.sayName = function() {
 console.log(this.name);
};

Child.prototype = { ...Parent.prototype };
Child.prototype.sleep = function() {
 console.log("....zzZ");
};

const parent = new Parent("지영", 35);
parent.sayName(); // 지영
parent.sleep(); // TypeError: parent.sleep is not a function

const child = new Child("철수", 2);
child.sayName(); // 철수
child.sleep(); // ....zzZ
  • spread 연산자를 이용해 새로운 객체에 Parent.prototype을 넣어준다.
  • 이 방법은 새로운 객체가 생성되어 부모 생성자 함수를 자식 생성자 함수가 연동하지 않고 Parent.prototype에 정의된 값을 사용할 수 있다.

하지만 이 상태에서 부모 생성자 함수의 프로토타입에 새로운 메서드를 추가한다면?

Child.prototype = { ...Parent.prototype };
Child.prototype.sleep = function() {
  console.log("....zzZ");
};

Parent.prototype.sayHello = function() {
  console.log("hello");
};

const parent = new Parent("jane", 35);
parent.sayName(); // jane
parent.sayHello(); // hello

const child = new Child("john", 2);
child.sayName(); // john
child.sleep(); // ....zzZ
child.sayHello(); // TypeError: parent.sleep is not a function

-> 참조가 끊겨 Parent.prototype을 참조하지 못하기 때문에 자식 생성자 함수의 프로토타입에는 새로운 메서드가 전달되지 못한다.


👩🏻‍💻Child.prototype = Object.create(Parent.prototype)

그렇다면 우리는 어떤 방식을 써서 상속 시켜줘야 할까?

✍️ 바로 Object.create(prototype) 메서드를 사용하는 방법이 있다.
메서드의 첫번째 인자에는 새로 만든 객체의 __proto__가 가리킬 prototype을 입력한다.

function Parent(name, age) {
  this.name = name;
  this.age = age;
}

Parent.prototype.sayName = function() {
  console.log(this.name);
};

Child.prototype = Object.create(Parent.prototype);
Child.prototype.sleep = function() {
  console.log("....zzZ");
};

Parent.prototype.sayHello = function() {
  console.log("hello");
};

const parent = new Parent("지영", 35);
parent.sayName(); // 지영
parent.sayHello(); // hello

const child = new Child("철수", 2);
child.sayName(); // 철수
child.sleep(); // ....zzZ
child.sayHello(); // hello

완벽한 상속 관계를 위한 Constructor 수정

  • 하지만 위와 같이 Object.create(prototype) 메소드를 호출하여 상속하는 방법은 원하는대로 상속이 되긴 했지만 완벽한 상속 관계를 위해 수정해줘야 할 부분이 있다.

❓ child에 들어간 constructor이 변경되었는지 확인해보자.

console.log(child.__proto__.constructor);  // function Parent() {}

-> 우리가 child.proto.constructor 으로 기대한 생성자 함수는 Child이다.

❗️ 완벽한 상속 관계를 만들어 주기 위해 수정해줘야 한다.

Child.prototype.constructor = Child;
console.log(child.__proto__.constructor); // function Child() {}

👩🏻‍💻call(), apply(this, arguments)

✍️ 부모 생성자에게서 상속 받을 속성이 있다면 부모생성자.call(this) 또는 부모생성자.apply(this,arguments)를 사용해 상속을 받아 그대로 사용하거나 또는 새로운 값을 할당할 수 있다.

// Case1
function Child(name, age) {
  // ~~~~
}
const child = new Child("지영", 2);
child.sayName(); // undefined

// ---------------------------------

// Case2
function Child(name, age) {
  Parent.apply(this, arguments);
}
const child = new Child("철수", 2);
child.sayName(); // 철수

pseudoclassical inheritance의 필수 과정

  1. 자식 생성자 함수 안에서 call/apply 를 통해 부모 생성자 함수와 연결시킨다.
  2. 자식 생성자 함수의 prototype 객체를 Object.create 메소드를 통해 부모 생성자 함수의 prototype 과 연결시킨다.
  3. 자식 생성자 함수의 constructor 를 자기 자신으로 재할당해준다.

Class

  • 클래스는 ES6 기준으로 추가된 문법이다. 기존의 프로토타입 기반 상속보다 명료하게 사용이 가능하다.
  • 자바스크립트에서의 클래스는 syntactic sugar이다.
  • 새로운 객체지향 상속 모델을 제공하는 게 아니라 좀 더 단순하고 명확한 문법만 제공해 주는 것이다.

클래스를 이용한 상속

  1. 클래스에 생성자 함수를 constructor로 정의하고 속성값을 세팅한다.
  2. 클래스에 함수(메서드)를 정의하면 함수(메서드)를 바로 프로토타입에 추가할 수 있다
  3. 자식 클래스가 부모 클래스를 상속하려면 extends 키워드를 사용해 상속 받을 수 있다.
    4.부모 클래스에서 pseudoclassical inheritance 방식으로 부모 속성 값을 상속받아 사용하려면 call메소드나 apply메소드를 사용해야 하는데,
    클래스에서는 super 라는 키워드를 사용하면 쉽게 부모 클래스의 속성 값을 상속받아 사용할 수 있다.

👩🏻‍💻 pseudoclassical inheritance code -> class code

✍️ 위의 pseudoclassical inheritance 방식 코드를 클래스 방식으로 수정해 보자.

class Parent {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayName() {
    console.log(this.name);
  }

  sayHello() {
    console.log("Hello!");
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name, age);
  }

  sleep() {
    console.log("....zzZ");
  }
}

const parent = new Parent("지영", 30);
const child = new Child("철수", 2);

parent.sayName(); // 지영
parent.sayHello(); // Hello!
parent.sleep(); // TypeError: parent.sleep is not a function
child.sayName(); // 철수
child.sayHello(); //Hello!
child.sleep(); // ....zzZ

❗️ 부모 클래스에서 상속 받은 속성을 그대로 사용하고 싶다면 자식 클래스에서는 constructor 키워드를 생략해도 된다.

class Parent {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayName() {
    console.log(this.name);
  }

  sayHello() {
    console.log("Hello!");
  }
}

class Child extends Parent {
  sleep() {
    console.log("....zzZ");
  }
}

const parent = new Parent("지영", 30);
const child = new Child("철수", 2);

parent.sayName(); // 지영
parent.sayHello(); // Hello!
parent.sleep(); // TypeError: parent.sleep is not a function
child.sayName(); // 철수
child.sayHello(); //Hello!
child.sleep(); // ....zzZ
profile
천천히 운영되는 개발 블로그

0개의 댓글