프로토타입 상속

  • 자바스크립트의 객체는 명세서에서 명명한 [[Prototype]]이라는 숨김 프로퍼티가 존재. 이 숨김 프로퍼티 값은 null이거나 다른 객체에 대한 참조가되는데, 다른 객체를 참조하는 경우 참조 대상을 '프로토타입(prototype)'이라 함
  • oject에서 프로퍼티를 읽으려고 하는데 해당 프로퍼티가 없으면 자바스크립트는 자동으로 프로토타입에서 프로퍼티를 탐색. 이러한 동작 방식을 '프로토타입 상속'이라 함
let animal = {
  eats: true
  walk() {
    alert("동물이 걷습니다.");
  }
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal;

alert( rabbit.jumps ); // true
alert( rabbit.eats ); // true
rabbit.walk(); // 동물이 걷습니다.
  • __proto__[[Prototype]]의 getter(획득자)이자 setter(설정자)
  • 객체 rabbit에서 프로퍼티를 얻고싶은데 해당 프로퍼티가 없다면, 자바스크립트는 자동으로 animal이라는 객체에서 프로퍼티를 탐색
  • rabbit의 프로토타입은 animal / rabbitanimal을 상속 / eats는 "상속 프로퍼티(inherited property)"
    proto_animal_rabbit_walk
let animal = {
  eats: true,
  walk() {
    /* rabbit은 이제 이 메서드를 사용하지 않습니다. */
  }
};

let rabbit = {
  __proto__: animal
};

rabbit.walk = function() {
  alert("토끼가 깡충깡충 뜁니다.");
}; // 객체 rabbit에 메서드 walk를 직접 할당

rabbit.walk(); // 토끼가 깡충깡충 뜁니다.
  • 프로토타입은 프로퍼티를 읽을 때만 사용
  • 프로퍼티를 추가, 수정하거나 지우는 연산은 객체에 직접
  • rabbit.walk()를 호출하면 프로토타입에 있는 메서드가 실행되지 않고, 객체 rabbit에 추가한 메서드가 실행
    proto_animal_rabbit_walk2
  • 접근자 프로퍼티(accessor property)는 setter 함수를 통해서 프로퍼티에 값을 할당하므로 이 규칙이 적용되지 않음. 접근자 프로퍼티에 값을 할당하는 것은 함수를 호출하는 것과 같기 때문
let user = {
  name: "John",
  surname: "Smith",

  set fullName(value) {
    [this.name, this.surname] = value.split(" ");
  },

  get fullName() {
    return `${this.name} ${this.surname}`;
  }
};

let admin = {
  __proto__: user,
  isAdmin: true
};

alert(admin.fullName); // John Smith (*)

admin.fullName = "Alice Cooper"; // (**)

alert(admin.fullName); // Alice Cooper (state of admin modified)
alert(user.fullName); // John Smith (state of user protected)
  • (*)로 표시한 줄에서 admin.fullName은 프로토타입(user)에 있는 getter 함수(get fullName)를 호출하고, (**)로 표시한 줄의 할당 연산은 프로토타입에 있는 setter 함수(set fullName)를 호출
let animal = {
  walk() {
    if (!this.isSleeping) {
      alert(`동물이 걸어갑니다.`);
    }
  },
  sleep() {
    this.isSleeping = true;
  }
};

let rabbit = {
  name: "하얀 토끼",
  __proto__: animal
};

// rabbit의 프로퍼티 isSleeping을 true로 변경
rabbit.sleep();

alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined (프로토타입에는 isSleeping이라는 프로퍼티가 없음)
  • this는 프로토타입에 영향을 받지 않음
  • 메서드를 객체에서 호출했든 프로토타입에서 호출했든 상관없이 this는 언제나 . 앞에 있는 객체
  • this에 데이터를 쓰면 animal이 아닌 해당 객체의 상태가 변화
  • 메서드는 공유되지만, 객체의 상태는 공유되지 않는다고 결론
    proto_animal_rabbit_walk3

프로토타입 체이닝

let animal = {
  eats: true,
  walk() {
    alert("동물이 걷습니다.");
  }
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

let longEar = {
  earLength: 10,
  __proto__: rabbit
};

longEar.walk(); // 동물이 걷습니다.
alert(longEar.jumps); // true
  • 순환 참조(circular reference)는 비허용. __proto__를 이용해 닫힌 형태로 다른 객체를 참조하면 에러가 발생
  • __proto__의 값은 객체나 null만 가능. 다른 자료형은 무시
  • 객체엔 오직 하나의 [[Portotype]]만 존재. 객체는 두 개의 객체를 상속받지 못함
    proto_animal_rabbit_chain
profile
Blog by Chungsik Park

0개의 댓글