プロトタイププロパティ
JavaScriptで関数を生成すると自動で、prototype
プロパティが生成されます。
function MyClass(x) { this.x = x; } var foo = new MyClass(10); console.log(foo);
このprototype
プロパティにメソッドを追加することで、生成したインスタンスからメソッドをコールすることができるようになります。
function MyClass(x) { this.x = x; } MyClass.prototype.show = function() { return this.x; }; var foo = new MyClass(10); console.log(foo);
new演算子の暗黙のルール
prototype
プロパティを使用せずに、下記のように定義することもできます。
function MyClass(x) { this.x = x; this.show = function() { return this.x; }; } var foo = new MyClass(10); console.log(foo);
しかし、これをコンソールで確認してみるとshow
メソッドがインスタンスに生成されています。
これはnew演算子の暗黙のルールで、関数の生成と共に var this = {};が定義されて、それを return this;していることにあります。
function MyClass(x) { // var this = {}; this.x = x; this.show = function() { return this.x; }; // return this; } var foo = new MyClass(10); console.log(foo);
2行目と7行目のコメントアウトしている箇所が暗黙のルールになります。無駄な関数(オブジェクト)の生成をしないという点でもprototype
プロパティを使用しての定義が良いようです。
プロトタイプチェーン
new演算子で一度インスタンス化したオブジェクトにしたあとに、クラス自体にメソッドやプロパティを追加するとインスタンスは、それを参照することができます。読み込みと書き込みで継承動作が異なります。違いを以下のコードで確認します。まずは、読み込み編。
function MyClass(x) { this.x = x; } var foo = new MyClass(10); console.log(foo.x); // 10 console.log(foo.y); // undefined MyClass.prototype.y = 5; console.log(foo.y); // 5
8行目でy
というプロパティを追加しました。その後、インスタンスfoo
はy
を参照できることが確認できました。続いて、下記のコードは、prototype
に実装したプロパティの値を上書きして、別のインスタンスからその値を参照した場合の例。
function MyClass(x) { this.x = x; } MyClass.prototype.y = 5; var foo = new MyClass(10); foo.y = 555; console.log(foo.y); // 555 var bar = new MyClass(10); console.log(bar.y); // 5
prototype
で定義したプロパティの値を書き換えたにも関わらず、インスタンスbar
のy
の値は元のままです。書き込み時にはプロトタイプチェーンをたどらないことがわかります。インスタンスが直接のプロパティをみているということになります。
インスタンスからプロパティを削除してみるとわかります。
function MyClass(x) { this.x = x; } MyClass.prototype.y = 5; var foo = new MyClass(10); foo.y = 555; console.log(foo.y); // 555 var bar = new MyClass(10); console.log(bar.y); // 5 delete foo.y; console.log(foo.y); // 5
12行目でインスタンスfoo
からy
プロパティをdelete
した後に、y
を参照してみると「5」という値が出力されました。これは親(prototype
)のy
を参照しているということになります。なるほど、スッキリ。