thumbnail

prototype

JavaScriptはプロトタイプベースの言語。別のオブジェクト(=プロトタイプ)を参照して性質を引き継ぐ。 あるオブジェクトにプロパティやメソッドが見つからなければ、プロトタイプをたどって探す。

サンプル

class構文の実態はprototype構文である。

class構文

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a sound`);
  }
}
class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  
  bark() {
    console.log(`${this.name} barks!`);
  }
}

prototype構文

function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function() {
  console.log(this.name + " makes a sound");
};

// Animal 概念
/*
  [[Prototype]]: Function.prototype,
  [[Call]]: (thisArg, [name]) => {
    // Animal("Pochi")
    thisArg.name = name;
    return undefined;
  },
  [[Construct]]: ([name], newTarget) => {
    // new Animal("Pochi")
    const obj = OrdinaryCreateFromConstructor(newTarget, Animal.prototype);
    Animal.[[Call]](obj, [name]);
    return obj;
  },
  prototype: {
    [[Prototype]]: Object.prototype,
    constructor: Animal,
    speak: function() {
      console.log(this.name + " makes a sound");
    }
  },
  name: "Animal",
  length: 1 // 仮引数の数(name)
*/
function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
  console.log(this.name + " barks!");
};

// Dog 概念
/*
  [[Prototype]]: Function.prototype,
  [[Call]]: (thisArg, [name, breed]) => {
    // Dog("Pochi", "Shiba")
    Animal.[[Call]](thisArg, [name]);
    thisArg.breed = breed;
    return undefined;
  },
  [[Construct]]: ([name, breed], newTarget) => {
    // new Dog("Pochi", "Shiba")
    obj = OrdinaryCreateFromConstructor(newTarget, "%Object.prototype%", internalProto = Dog.prototype);
    Dog.[[Call]](obj, [name, breed]);
    return obj;
  },
  prototype: {
    [[Prototype]]: Animal.prototype,
    constructor: Dog,
    bark: function() {
      console.log(this.name + " barks!");
    }
  },
  name: "Dog",
  length: 2 // 仮引数の数(name, breed)
*/
const dog = new Dog("Pochi", "Shiba");
dog.speak(); // Pochi makes a sound
dog.bark();  // Pochi barks!

// dog 概念
/*
  [[Prototype]]: Dog.prototype,
  name: "Pochi",
  breed: "Shiba"
*/

// dog 全体像
/*
{
  [[Prototype]]: {
    [[Prototype]]: {
      [[Prototype]]: Object.prototype,
      constructor: Animal,
      speak: [Function: speak]
    },
    constructor: Dog,
    bark: [Function: bark]
  },
  name: "Pochi",
  breed: "Shiba"
};
*/

Object.getPrototypeOf

prototypeを取得する。

インスタンスオブジェクト

Object.getPrototypeOf(dog) === Dog.prototype;                   // true
Object.getPrototypeOf(Dog.prototype) === Animal.prototype;      // true
Object.getPrototypeOf(Animal.prototype) === Object.prototype;   // true
Object.getPrototypeOf(Object.prototype) === null;               // true

コンストラクタオブジェクト

Object.getPrototypeOf(Dog) === Function.prototype;              // true
Object.getPrototypeOf(Function.prototype) === Object.prototype; // true
Object.getPrototypeOf(Object.prototype) === null;               // true

instanceof

インスタンスオブジェクトが特定のコンストラクタオブジェクトから生成されたかどうかを判定する演算子。

サンプル

dog instanceof Dog;       // true
dog instanceof Animal;    // true
dog instanceof Object;    // true

内部処理イメージ

プロトタイプチェーンをたどって判定。

function instanceOf(left, right) {
  const proto = right?.prototype;
  let cur = Object.getPrototypeOf(left);
  while (cur) {
    if (cur === proto) return true;
    cur = Object.getPrototypeOf(cur);
  }
  return false;
}