JavaScript

[JavaScript] 프로토타입(prototype) 상속

FRDYtheme 2022. 12. 6. 21:17

기존 객체에서 프로퍼티와 메서드를 상속 받아 유사한 역할을 하는 객체를 만들 수 있음

코드의 중복을 막고 재사용을 위해 주로 사용

부모 객체인 프로토타입을 동적으로 변경할 수 있음

 

객체에서 프로퍼티를 읽으려 할 때 객체 내에 해당 프로퍼티가 없으면 자바스크립트가 자동으로 프로토타입에서 프로퍼티를 찾는데, 이런 동작 방식을 '프로토타입 상속'이라고 함.

 

자바스크립트의 객체는 prototype이라는 숨겨진 프로퍼티를 갖는데

이 값은 null이지만  __proto__ 키워드를 사용해 값을 설정할 수 있고 다른 객체에 대한 참조가 가능하다.

다른 객체를 참조하는 경우 참조 대상을 '프로토타입'이라고 부른다.

 

[[Prototype]]

객체 내부에 숨겨져 있는 숨김 프로퍼티로 아래 메서드나 키워드를 사용해 값을 설정해서 다른 객체를 참조할 수 있음.

-

Object.create(proto, [descriptors])

[[Prototype]]이 proto를 참조하는 빈 객체를 생성. 이때 설명 첨부 가능.

  //  Object.create();

  const drinkPrototype = {
    order: function () {
      return `주문한 음료는 ${this.name}입니다.`;
    },
  };

  // 빈 객체를 생성하고 프로토타입 지정
  const drink1 = Object.create(drinkPrototype);
  console.log(drink1); // drinkPrototype을 참조했지만 아직 빈 배열이다.
  drink1.name = "유자차"; // .표기법으로 name 프로퍼티 추가
  console.log(drink1); // { name: 유자차 }

  /* 
    drinkPrototype이 drink1의 프로토타입이기 때문에
    drink1에 없는 메서드 order를 프로토타입 안에서 탐색하여 호출.
  */

  console.log(drink1.order()); // 주문한 음료는 유자차입니다.

  const drink2 = Object.create(drinkPrototype);
  drink2.name = "생강차";
  console.log(drink2.order()); // 주문한 음료는 생강차입니다.

  drink1.order === drink2.order
  ? alert("drink1과 drink2는 같은 프로토타입을 가졌습니다") 
  : alert("drink1과 drink2는 다른 프로토타입을 가졌습니다.");
  
  // drink1과 drink2의 [[Prototype]]이 같은 proto(drinkPrototype)를 참조하기에
  //true의 값이 출력된다.

  // 생성자 함수로 객체 생성
  function coffee(name) {
    return {
      // 함수 내 객체
      name, // 키와 값이 같은 프로퍼티는 하나로 생략 가능 === name: name,
      order: function () {
        return `주문한 음료는 ${this.name}입니다`;
      },
    };
  }

  const coffeeList = []; // 빈 배열 생성.
  for (let i = 0; i < 5; i++) {
    coffeeList.push(coffee("아메리카노"));
  }
  console.log(coffeeList) // 배열 내 {name: 아메리카노, order()} 5개 생성

  console.log(coffeeList[0].order === coffeeList[1].order); // false

  /* Object.create()로 프로토타입을 설정해 함수를 상속받는 건
  동일한 메모리에 할당된 함수 값을 불러오는 것으로 상속 받은 객체가 달라도 true지만 */

  /* 일반 함수를 사용해 배열 내에 객체를 단순 복사하는 것은
  각 객체와 그 객체 내 각 함수가 각자의 메모리에 할당되는 것이라 false가 나온다. */

-

Object.getPrototypeOf(obj)

obj의 [[Prototype]]을 반환

-

Object.setPrototypeOf(obj, proto)

obj의 [[Prototype]]이 proto가 되도록 설정.

 

-

__proto__ 키워드

  let potato = {
    color: "brown",
  }
  let honey = {
    flavor: "sweet",
  }

  console.log(honey.color); // undefined, honey 객체에 color 프로퍼티 없음.
  
  honey.__proto__ = potato; // potato를 honey의 [[Prototype]]으로 설정.

  console.log(honey.color);
  // honey에는 color 프로퍼티가 없기 때문에 자바스크립트에서 자동으로
  // [[Prototype]]이 참조하고 있는 객체 potato의 객체 내부에서 color를 읽어 옴.

=

'honey의 프로토타입은 potato'

=

'honey는 potato를 상속받는다.'

 

-

메서드도 상속 가능.

  let IamProto = {
    message: function () {
      alert(`이 함수는 프로토타입에서 상속받았습니다.`)
    }
  }
  let whoIsProto = {
    alarm: function () {
      alert(`이 함수는 객체 내 프로퍼티입니다.`)
    }
  }
  whoIsProto.__proto__ = IamProto

  // 객체 내 같은 이름의 키가 있으면 
  console.log(whoIsProto.message()); //이 함수는 프로토타입에서 상속받았습니다.

'프로토타입 체이닝'

-프로토타입에게 상속받는 프로토타입에게 상속받는 프로토타입에게 상속받는 프로토타입에게 상속받는 프로토타입...

 

1. 순환 참조는 허용되지 않음. __proto__를 작성해 닫힌 형태로 다른 객체를 참조하면 에러 발생.

2. __proto__의 값은 객체 혹은 null만 가능하며 다른 자료형은 무시된다.

3. 객체는 오직 하나의 [[Prototype]]만 있으며 두 개의 객체에서 상속받는 것은 불가능.

 

  let IamProto = {
    message: function () {
      alert(`이 함수는 IamProto에서 상속받았습니다.`)
    }
  }
  let whoIsProto = {
    alarm: function () {
      alert(`이 함수는 whoIsProto에서 상속받았습니다.`)
    }
  }
  let heyProto = {
    getMessage: function(){alert(`이 함수는 객체 내 메서드입니다.`)}
  }

  whoIsProto.__proto__ = IamProto
  heyProto.__proto__ = whoIsProto

  // 객체 내 같은 이름의 키가 있으면 
  console.log(heyProto.message()); //`이 함수는 IamProto에서 상속받았습니다.`
  console.log(heyProto.alarm()); //`이 함수는 whoIsProto에서 상속받았습니다.`
  console.log(heyProto.getMessage()); //`이 함수는 객체 내 메서드입니다.`

'프로토타입은 읽기 전용'

-

프로토타입은 프로퍼티를 읽을 때만 사용되며 프로퍼티를 추가, 수정, 삭제하는 것은 해당 객체에 직접 적용해야 함.

 

'JavaScript' 카테고리의 다른 글

[JavaScript] 배열의 메서드 종류_more  (0) 2022.12.07
[JavaScript] 객체 프로퍼티 설정  (0) 2022.12.06
[JavaScript] 배열 (array)  (0) 2022.12.06
[JavaScript] 구조 분해 할당  (0) 2022.12.06
[JavaScript] Math 객체  (0) 2022.12.05