프로토타입 — 없으면 위에 물어보는 상속

내가 만들지도 않은 toString·map 같은 메서드를 쓸 수 있는 이유. 객체가 다른 객체를 참조해 속성을 물려받는 구조다.

Seobway · · 8분

만든 적 없는 메서드를 왜 쓸 수 있는가

빈 객체를 만들었을 뿐인데 메서드가 딸려 온다.

const obj = {};
obj.toString();          // "[object Object]"   ← 만든 적 없다

const arr = [1, 2, 3];
arr.map((n) => n * 2);   // 이것도 만든 적 없다

obj 에는 아무것도 없다. 그런데 toString 을 찾아 준다. 자기한테 없으면 위에 물어보기
때문
이다.


프로토타입은 무엇인가

프로토타입은 객체가 참조하는 다른 객체이고, 자기한테 없는 속성을 여기서 찾는다.
모든 객체는 내부에 [[Prototype]] 이라는 숨은 링크를 하나 갖고 있다.

속성을 읽을 때 자바스크립트는 이렇게 움직인다.

  1. 객체 자신에게 있나? 있으면 끝.
  2. 없으면 [[Prototype]] 을 따라 올라가 거기서 찾는다.
  3. 거기도 없으면 또 올라간다.
  4. 끝(null)까지 갔는데 없으면 undefined.

이 사슬을 프로토타입 체인이라고 한다.

인터랙티브 다이어그램: arr.map 을 찾아 위로 올라간다 (단계별로 재생할 수 있다)

  1. arr.map 을 읽는다. arr 자신에는 0,1,2 와 length 뿐이다.
  2. 없으니 [[Prototype]] 을 따라 Array.prototype 으로 올라간다.
  3. Array.prototype 에 map 이 있다. 여기서 멈춘다.
  4. 만약 toString 이었다면 여기도 없어 한 칸 더 올라간다.
  5. Object.prototype 에서 찾는다.
  6. 여기에도 없었다면 null 에 닿고 undefined 를 돌려준다.


어떻게 연결하는가

가장 직접적이다. "이 객체를 프로토타입으로 삼는 객체를 만들어라".

const animal = {
  speak() { return `${this.name} 이(가) 소리를 낸다`; },
};

const dog = Object.create(animal);
dog.name = "바둑이";
dog.speak();     // "바둑이 이(가) 소리를 낸다"

dog 에는 name 뿐이지만 speakanimal 에서 찾아 쓴다.


직접 확인

체인을 끝까지 따라가 보면 구조가 한눈에 보인다.

function walk(label, value) {
  const chain = [];
  let cur = Object.getPrototypeOf(value);
  while (cur) {
    chain.push(cur.constructor ? cur.constructor.name : "(익명)");
    cur = Object.getPrototypeOf(cur);
  }
  console.log(label, "→", chain.join(" → "), "→ null");
}

walk("[1,2,3]  ", [1, 2, 3]);
walk("{}       ", {});
walk("'문자열' ", "문자열");
walk("function ", function () {});

// 자기 것인지 물려받은 것인지 구분
const dog = Object.create({ speak() {} });
dog.name = "바둑이";
console.log("name 은 자기 것?", Object.hasOwn(dog, "name"));
console.log("speak 은 자기 것?", Object.hasOwn(dog, "speak"));
console.log("speak 을 쓸 수는 있나?", typeof dog.speak);

speak 은 자기 것이 아닌데도 쓸 수 있다. 이것이 프로토타입 상속이다.


흔한 실수


한 줄 정리

프로토타입은 객체가 참조하는 다른 객체이고, 자기한테 없는 속성은 이 링크를 따라
위로 올라가며 찾는다. class 도 이 구조 위의 문법이고, 메서드를 한 벌만 두어
인스턴스가 공유하게 하는 것이 목적이다.

참고

  1. [1] Inheritance and the prototype chain — MDN Web Docs
  2. [2] Object.getPrototypeOf() — MDN Web Docs
  3. [3] Objects — ECMAScript 사양

관련 글