prototype ベースの継承方法いろいろ

JavaScript Patterns』より、prototype ベースの継承方法いろいろ。

1. 最もベーシックな prototype 継承

子クラスの prototype に親クラスのインスタンスを指定します。

function Parent(name) {
  this.name = name || "Adam";
}

Parent.prototype.say = function() {
  return this.name;
};

function Child(name) {
}

function inherit(C, P) {
  C.prototype = new P();
}

欠点は、親クラスの this に設定されたプロパティと prototype が両方継承され、また親クラスのコンストラクタが呼ばれないことです。

2. 親クラスのコンストラクタを利用する

1 番の欠点をである、親のコンストラクタが呼ばれない問題を解決する方法です。

function Parent(name) {
  this.name = name || "Adam";
}

Parent.prototype.say = function() {
  return this.name;
};

function Child(name) {
  Parent.apply(this, arguments);
}

ただし、prototype が継承されないので say メソッドは使えません。継承したいメンバは this に定義する必要があります。

この方法では多重継承も可能です。

function Cat() {
  this.legs = 4;
  this.say = function() {
    return "myao";
  };
}

function Bird() {
  this.wings = 2;
}

function CatWings() {
  Cat.apply(this);
  Bird.apply(this);
}

3. prototype 継承で親のコンストラクタも呼ぶ

1 番と 2 番の合わせ技です。

function Child(name) {
  Parent.apply(this, arguments);
}
Child.prototype = new Parent();

個人的にはこの方法がシンプルで好きです。

4. prototype を共有する

子クラスの prototype に親クラスの prototype を代入しちゃえというやり方。継承するのは prototype に定義されたプロパティ/メソッドのみ、というルールの場合に使います。

function inherit(C, P) {
  C.prototype = P.prototype;
}

ただし prototype を共有することになるので子クラスの変更が親クラスに影響するので、このままでは現実的ではないです。

5. 一時的なクラスを用意する

4 番の欠点を補う方法です。一時的なクラスを用意して間接的に prototype の代入を行います。

function inherit(C, P) {
  var F = function() {
  }
  F.prototype = P.prototype;
  C.prototype = new F();
}

私は最初にこのコードを見たとき 1 番と同じでは…?と思いました。ただし、実際に実行すると違いがわかりました。

function Parent(name) {
  this.name = name || "Adam";    
}

Parent.prototype.say = function() {
  return this.name;
};

function Child() {
}

inherit(Child, Parent);

var child = new Child();
child.say();//undefined

親クラスの this.name はルール通り継承されていません。1 番の方法では Adam が出力されてしまいます。