継承
継承とは、あるオブジェクトが別のオブジェクトを基にすることができるプロセスです。
私は Hero
プロトタイプを作成し、それを使用して superman
という名前の新しいオブジェクトを作成しました。 しかし、このオブジェクトでは何もしていません。 そこで、dialogue
.
function dialogue() {
console.log('I am ' + this.name);
}
という別の関数を作成することで対処しましょう。しかし、今コードを実行すると、この関数は名前が本当は何であるかを知らないので、何も起こりません。 そのためには、
この新しい関数に Hero
のプロパティと同じものを持たせる必要があります。 この関数内に書き留める代わりに、JavaScript に Hero
プロトタイプからそれらを継承するように指示すればよいのです。
dialogue
を Hero.prototype
上に置くことにより、Hero
のすべてのインスタンスがそれを使用できるようにしています。
Differential Inheritance
JavaScript には「差分継承」という別の継承モデルも用意されています。 このモデルでは、メソッドが親から子へコピーされることはない。
ここで、superman
は実際には dialogue()
という独自のメソッドを持っていません。
JavaScriptエンジンはコード内でsuperman.dialogue()
を見つけると、superman
オブジェクトの中にあるdialogue
というプロパティを探します。 それが見つからない場合、プロトタイプチェーンで superman
の親 Hero.prototype
を探します。 そこで Hero.prototype.dialogue
を見つけ、superman
にバインドされた this
で呼び出します。
Object.create()
Superman
の新しいクラスを作成し、Hero
プロトタイプの属性を継承させれば、さらに排他的にすることが可能です。 これは、Superman
のプロトタイプを Hero
のプロトタイプに次のように代入することで可能です:
function Superman() {}
Superman.prototype = Hero.prototype
しかしこれは、Superman
と Hero
を両方とも同等にしているに過ぎません。 本当に必要なのは、Hero
プロトタイプをベースにした新しいオブジェクトである。 ES5以降、JavaScriptにはObject.create()
という組み込み関数が用意されている。
Superman.prototype = Object.create(Hero.prototype);
これは、Hero
プロトタイプに基づく新しい空のオブジェクトを作成し、それを Superman
プロトタイプに割り当てます。 つまり、Hero
プロトタイプにあるすべてのプロパティは、Superman
プロトタイプからアクセスできるようになります。 new Hero
を呼び出す代わりに new Superman
を呼び出しても、すべてが正常に動作します。
しかし、出力をよく見てみると、その中に undefined
があることに気づきます。 それは、現在 Hero
が自分自身のためだけのコンストラクタであるためです。 Hero
のプロパティを Superman
プロトタイプの内部で call
しなければなりません。
function Superman() {
Hero.call(this, 'Superman', 'Clark Kent', 'Krypton')
}
以下のように、MarvelMovies
という別のコンストラクタを作ってみましょう。
function MarvelMovies(movieName, releaseYear) {
this.movieName = movieName;
this.releaseYear = releaseYear;
}
関数がコンストラクタとして使われるとき this
は新しく作るオブジェクトを指しています。 このコンストラクタでは、movieName
と releaseYear
を引数として取り、それらの値を avengers
という新しい MarvelMovies
インスタンスの movieName
と releaseYear
プロパティに代入しているわけです。
var avengers = new MarvelMovies("avengers", 2012);
次に、このプロトタイプに対して、次のように output
という新しいメソッドを作成します。
MarvelMovies.prototype.output = function() {
return "Movie: " + this.movieName + " Released in " + this.releaseYear;
}
console.log(avengers.output());
将来の継承
継承の本当にすばらしい側面は、JavaScript によって、クラスを定義した後でもその機能を修正または拡張できるということです。
これを説明するために、次のような配列を作成します:
var numbers = ;
Array.prototype.shuffle = function() {
return this.sort(function() {
return Math.round( Math.random() * 2) - 1;
});
};
console.log(numbers.shuffle());
ここで、numbers
配列は Array.prototype.shuffle
が存在する前に存在しています。 しかし、JavaScriptでは、プロパティの参照はプロトタイプの連鎖を上へ上へと進みます。 これは、配列が新しいメソッド shuffle
へのアクセスをまだ持っている理由です。なぜなら、実際に使用しようとしているときに Array.prototype
上に存在するからです。
簡単に言うと、配列を作成し、それから戻ってすべての配列に新しいメソッドへのアクセスを提供しました。