JavaScriptにクラスはありません - プロトタイプで継承編 -
前回の内容はこちら。
JavaScriptにクラスはありません - オブジェクトリテラルとコンストラクタパターン編 - - MoyaSystem
方法3: プロトタイプを拝借する
クラスのないJavaScriptに用意されている、オブジェクト間でコードを再利用する方法の一つがプロトタイプです。同一のプロトタイプを持つオブジェクトでは、プロトタイプに定義されているプロパティにアクセスすることができます。
以下のコードではObject.create関数を使って、共通のプロトタイプを持つオブジェクトを生成しています。第1引数はプロトタイプ、第2引数はオブジェクト固有のプロパティとなります。
var Person = function Person(){}; Person.prototype.sayName = function(){ console.log("I am " + this.name); }; Person.prototype.sayHo = function(){ console.log("Ho!"); }; var Singer = function Singer(){}; Singer.prototype = new Person(); // Personのプロトタイプを継承 Singer.prototype.sing = function(){ console.log(this.lyric); }; var John = Object.create(Person.prototype,{ name: {value: "John"}, lyric: {value: "Imagine there's no heaven..."} }); John.sayName(); // "I am John" // John.sing(); // undefined error var Bob = Object.create(Person.prototype,{ name: {value: "Bob"} }); Bob.sayName(); // "I am Bob" John.sayHo = function(){ console.log("HOOOOOOOOOOOOO"); }; John.sayHo(); // "HOOOOOOOOOOOOO" Bob.sayHo(); // "Ho!" var LadyGaga = Object.create(Singer.prototype,{ name: {value: "Gaga"}, lyric: {value: "lalalala lalalala"} }); LadyGaga.sayName(); // "I am Gaga" LadyGaga.sayHo(); // "Ho!" LadyGaga.sing(); // "lalalala lalalala"
Personを空のオブジェクトとして定義し、共有したいプロパティはすべてPerson.prototypeに定義するのがキモです。LadyGagaはPersonオブジェクトのプロトタイプを拡張したSingerオブジェクトとして作成しています。Singer.prototypeにPersonオブジェクトを代入することでPerson.prototypeにもアクセスすることができるようになります。ここでPerson.prototypeを代入してしまうと、Singer.prototypeで拡張した要素にPersonオブジェクトもアクセスできるようになる(John, Bobもsingできるようになる)ため、間違えないように注意してください。
ということで、Personオブジェクトを継承したSingerオブジェクトを作ることができました!
もう一工夫
継承ができたところで、クラスベースのオブジェクト指向言語でいうところのstatic変数っぽいものを作ってみます。ここではPersonオブジェクトすべてに付与されるmyIdプロパティを作成し、Personオブジェクトが作成されるたびにインクリメントされるようにしてみます。
function Person(){}; Person.prototype.sayName = function(){ console.log("I am " + this.myName); } Person.prototype.sayId = function(){ console.log("My Id is " + this.myId); } // ファクトリ var PersonFactory = function(){ var nextId = 0; // static変数的なもの return function(name){ nextId++; return Object.create( Person.prototype,{ myName: {value: name}, myId: {value: nextId} } ); }; }; var pfactory = PersonFactory(); var John = pfactory("John"); var Bob = pfactory("Bob"); var Mary = pfactory("Mary"); John.sayName(); // I am John John.sayId(); // My Id is 1 Bob.sayName(); // I am Bob Bob.sayId(); // My Id is 2 Mary.sayName(); // I am Mary Mary.sayId(); // My Id is 3
オブジェクトやプロトタイプそのものにmyIdをもたせるのはうまくいかないので、ファクトリメソッドを作成してその中で管理するようにしました。pfactory関数が実行されるたびにnextIdがインクリメントされます。
気をつけるべきこと
継承に頼り過ぎない
オライリー『JavaScriptパターン』でも述べられていますが、継承はコードを再利用するための一つの方法にすぎません。このコード再利用したいなーと思ったときに、どんな実装をすれば効果的に再利用できるのか、引き出しをたくさんもっていることが重要だと思います。
JavaScriptパターン ―優れたアプリケーションのための作法
- 作者: Stoyan Stefanov,豊福剛
- 出版社/メーカー: オライリージャパン
- 発売日: 2011/02/16
- メディア: 大型本
- 購入: 22人 クリック: 907回
- この商品を含むブログ (77件) を見る