ポイント6
継承ではプロタイプの鎖をたどってメソッドを検索する

 続いて,オブジェクトの継承について見ていきましょう。JavaScriptでも,クラス・ベースのオブジェクト指向言語におけるクラスの継承と同様に,あるオブジェクトのメソッドやプロパティを引き継いだ新しいオブジェクトを定義できます。

 リスト4は,Animalというオブジェクトを継承したDogというオブジェクトを作成し,それを基にpochiというオブジェクトを生成するサンプルです。実行すると,図3の二つのダイアログが順番に表示されます。

function Animal () {}
Animal.prototype.isAnimal = function () {
   return true;
}

function Dog (name) {
   this.name = name;
}

Dog.prototype = new Animal();    // <------(1)

Dog.prototype.bark = function () {
   alert(this.name + ': わんわん');
}

pochi = new Dog('ぽち');
pochi.bark();
alert( pochi.isAnimal() );
リスト4●オブジェクトの継承のJavaScriptのコード

図3●リスト4を実行したときに順に表示される二つのダイアログ
図3●リスト4を実行したときに順に表示される二つのダイアログ

 ポイントは,Dogのprototypeプロパティにnew演算子でAnimalオブジェクトを生成して代入していることです(リスト4(1))。これで,pochi.barkではDog.prototype.barkで定義された関数が実行され,pochi.isAnimalではAnimal.prototype.isAnimalで定義された関数が実行されることになります。

 このような動作を実現できるのは,実行するメソッドを探す方法に関係があります。すでに述べたように,JavaScriptのオブジェクトでは,メソッドが実行されると,まずそのオブジェクトに直接定義されているメソッドを検索し,続いてnewで指定したオブジェクトのprototypeプロパティから該当するメソッドを検索します。しかし,それでもメソッドが見つからなかった場合で,かつprototypeプロパティに格納されたオブジェクトの生成元のprototypeプロパティが存在している場合は,それを検索します。

 リスト4では,Animalオブジェクトを生成してDog.propertyに代入しています。このため,Dogから生成したpochiオブジェクトでメソッドを指定すると,まずpochiオブジェクト自身のメソッドを検索し,続いてDog.prototype内のメソッドを検索します。それでも見つからなかった場合は,Animal.prototypeに指定されたメソッドを検索するわけです。pochi.isAnimalというメソッドが実行されると,検索の結果,Animal.prototypeで定義されているメソッドが実行されることになります。

 JavaやC++のようなクラス・ベースのオブジェクト指向での継承と,JavaScriptのプロトタイプ・ベ-スの継承を比較すると図4のようになります。メンバーをプロパティに置き換えて考えてみてください。

図4●クラス・ベースとプロトタイプ・ベースのオブジェクト指向の「継承」の違い
図4●クラス・ベースとプロトタイプ・ベースのオブジェクト指向の「継承」の違い

 継承を多段階にすれば,ずっとさかのぼって検索が行われます。このようにprototypeプロパティをさかのぼってメソッドやプロパティの検索を行う仕組みを「プロトタイプ・チェーン」と呼びます。

 JavaScriptの継承はprototypeプロパティをさかのぼるだけなので,C++のように,複数のオブジェクトの性質を一つのオブジェクトに受け継がせる多重継承を実現することはできません。しかし,プロトタイプ・ベースであるがゆえに,オブジェクトの継承元を動的に切り替えることができます(図5)。

図5●JavaScriptでは親オブジェクトを動的に変えられる
図5●JavaScriptでは親オブジェクトを動的に変えられる

 リスト5を見てみましょう。ObjCのprototypeをプログラム中で変更することで,inst1とinst2には同じObjCが入っているにもかかわらず,その継承元はそれぞれObjA,ObjBと異なっています。一度生成されたオブジェクトではプロトタイプ・チェーンは保存されます。このため,リスト5を実行すると,「ObjA」と「ObjB」が順番に表示されます(図6)。

function ObjA () {}
ObjA.prototype.test = function () {
   alert('ObjA');
}

function ObjB () {}
ObjB.prototype.test = function () {
   alert('ObjB');
}

function ObjC () {}

ObjC.prototype = new ObjA();
inst1 = new ObjC();

ObjC.prototype = new ObjB();
inst2 = new ObjC();

inst1.test();
inst2.test();
リスト5●オブジェクトの継承元を動的に切り替えるJavaScriptのコード

図6●リスト5を実行したときに順に表示される二つのダイアログ
図6●リスト5を実行したときに順に表示される二つのダイアログ

ポイント7
組み込みオブジェクトの機能を拡張できる

 JavaScriptでは,オブジェクトにメソッドを後から追加できます。そのObject自身にメソッドを追加することもできますし,prototypeプロパティにメソッドを追加することで,new演算子でそのオブジェクトから生成するオブジェクトすべてにメソッドを追加することも可能です。自分で作成したオブジェクトに限らず,組み込みのオブジェクトでも同様です。すでに存在するメソッドを置き換えてしまうこともできます。そこで,組み込みオブジェクトのメソッドを置き換えるサンプルを作成して,この機能を見てみましょう。

 例として,Dateオブジェクトを取り上げます。Dateオブジェクトには,現在の日時を返す「toLocalString」というメソッドが用意されています。ただ,FirefoxやInternet Explorer(IE)では「2006年1月27日14:26:51」,Operaでは「2005/12/2714:28:31」と表示されるなど,ブラウザによって表示が異なります。FirefoxやIEでは日付が漢字表示なのに時間が漢字表示でないのも気になります。そこでtoLocalStringを,どんなブラウザでも「2006年01月27日14時26分51秒」のような文字列を返すメソッドに置き換えてみます(リスト6)。

Date.prototype.toLocaleString = function (){
   return this.getFullYear()+'年'+
   this.getMonth()+1+'月'+
   this.getDate()+'日'+
   this.getHours()+'時'+
   this.getMinutes()+'分'+
   this.getSeconds()+'秒';
}
リスト6●漢字で日時を表示するよう改造したtoLocalStringメソッド

 これをプログラムの先頭で実行しておけば,その後以下のようなプログラムを実行することで,すべて日時を漢字入りで表示することができます(図7)。

図7●改造したtoLocalStringメソッドを実行したところ
図7●改造したtoLocalStringメソッドを実行したところ

now = new Date;
alert( now.toLocaleString() );

 このようなことが可能なのも,JavaScriptという言語の特徴だといえるでしょう。

 組み込みオブジェクトの機能拡張を行ってくれるライブラリは,多く公開されています。カコミ記事「様々なライブラリが利用可能に」で紹介しているprototype.jsでは,FunctionオブジェクトやObjectオブジェクトなどの組み込みオブジェクトにメソッドやプロパティを追加してくれます。ぜひ,参考にしてください。

様々なライブラリが利用可能に

 JavaScriptが注目されるようになったことで,便利なライブラリがたくさん公開されるようになりました。ほかの言語のライブラリと同様,様々な処理をライブラリを呼び出すことで簡単に行うことができるようになります。

 JavaScriptでこれらのライブラリを利用するには,通常のJavaScriptの外部指定と同じように,以下のように指定するだけです。

<script type="text/javascript"
 src="prototype.js"></script>

 数あるライブラリの中で,最も注目されているのが「prototype.js」です。37signalsという米国の企業に所属するSamStephenson氏というプログラマが開発しました。「Ajaxを簡単に利用できるようにする」「既存のオブジェクトに便利な機能を追加する」「ブラウザの違いを吸収する」といった様々な機能を追加してくれる,かなり便利なライブラリです。

 例えば,イベント・ハンドラを追加したい場合,InternetExplorerでは「element.attachEvent」,Firefoxでは「element.addEventLisner」という異なるメソッドを利用しなければなりません。これらの違いを吸収するためのコードをいちいち書くのは面倒です。そこでprototype.jsは,「Event.observe」という両者の違いを吸収してイベント・ハンドラを登録できるメソッドを用意しています。prototype.jsを利用することで,頻繁に行わなければならないこうした処理を簡単に行えるようになります。

 さらにprototype.jsは,C++やJavaのようなクラス・ベースのオブジェクト指向のスタイルを利用できるようにする機能なども備えています。

 prototype.jsが高機能であるため,prototype.jsの利用を前提としたライブラリも多く登場しています。フェードアウトやドラッグアンドドロップといったインタフェース関連の機能を数多く提供する「script.aculo.us」というライブラリがその代表格です。

 これら以外にも,通信やクロスブラウザ,入力したフォーム・データのチェックなどを行う,様々なライブラリが公開されています。こうしたライブラリのおかげで,頻繁に現れるありがちな処理を書かずにすみ,開発時間を短縮できます。

水野 貴明

草野 太輔


出典:日経ソフトウエア 2006年3月号 72ページより
記事は執筆時の情報に基づいており、現在では異なる場合があります。

この先は会員の登録が必要です。有料会員(月額プラン)は登録月無料!

日経 xTECHには有料記事(有料会員向けまたは定期購読者向け)、無料記事(登録会員向け)、フリー記事(誰でも閲覧可能)があります。有料記事でも、登録会員向け配信期間は登録会員への登録が必要な場合があります。有料会員と登録会員に関するFAQはこちら