100 ー 5 章オブジェクト作成のバター ン myarray = { isArray: isArray' indexOf: indexOf, inArray: indexOf ここには 2 つのプライベート変数と 2 つのプライベート関数、 isArray ( ) と index0f() があります。 即時関数の最後近くで、パプリックな利用に適すると判断した機能をオプジェクト myarray に追加 しています。同じプライベート関数 indexOf ( ) を、 ECMAScript5 スタイルの index0f と PHP 流 の inA Ⅱ ay の両方の名前で開示しています。この新しい myarray オプジェクトをテストしてみましよ myarray. isArray([1,2]); / / true myarray. isArray({0: 1 } ) / / false myarray. index0f( ["a" myarray. inArray(["a" たとえば仮にパプリックの index0f ( ) に何か予期せぬことが起きたとしても、 プライベートの index0f() は安全なままなので、 inArray() は本来の処理を続けられます。 myarray. index0f = null; myarray. inArray(["a" 5.4 モジュールバターン モジュールバターンは、コードの成長に合わせたコード構成のための構造を提供するので、広く 使われています。他の言語と違って JavaScript にはパッケージのための特別な構文はありませんが、 モジュールバターンは、機能ごとにそれぞれ完結したいくつかのコード群に分けるための道具を提 供します。機能別にそれぞれをプラックポックスとして扱うことができ、作成中のソフトウェアに 対する ( 常に変化する ) 要件に応じて、追加、置き換え、削除ができます。 モジュールバターンはこれまで解説してきたいくつかのパターンを組み合わせたものです。 名前空間 ・即時関数 プライベートメンバと特権メンバ 依存関係の宣言 ます最初に名前空間を準備します。この章で説明した namespace() 関数を使って、便利な配列メ ソッドを提供するユーティリティモジュールのサンプルを作ってみましよう。
102 ー 1 + = 1 ) { inArray: function (needle, haystack) { return { / / バブリック AP I / / 1 回きりの初期化手続き / / var の終わり / / プライベートメソッド 5 章オブジェクト作成のパタ ーン for (var i = 0 , max = haystack. length; if (haystack[i] ー = needle) { return true; isArray: function (a) { / / ... その他のメソッドとプロバティ array string; return ops. call(a) 1 く max; モジュールバターンは広く使われていて、とりわけコードの成長に合わせてコードを編成するた めの手法として推奨されています。 5.4.1 モジュールバターンの開示 したまま、最後にパプリック API として設定したいメソッドだけを開示します。 同様の手法でモジュールバターンを組み立てることができます。メソッドをすべてプライベートに この章ではプライバシーパターンの解説と合わせて開示パターンについてすでに説明しました。 先の例は次のように書き換えることができます。 MYAPP. utilities. array = (function ( ) { / / プライベートプロバティ " [object Array]" var array string ops = 0bject. prototype. t0String, / / プライベートメソッド inArray = function (haystack, needle) { for (var i = 0 , max = haystack. length; if (haystack[i] = = needle) { i く max; i + = 1 ) {
118 ー 5 章オブジェクト作成のバターン れが可能になります。 最後に method ( ) メソッドがどのように実装されているか見ておきましよう。 if (typeof Function. prototype. method ! = "function") { Function. prototype. method = function (name, implementation) { this. prototype[name] implementation; return this; method() の実装を見ると、ます最初にそのメソッドがすでに実装されていないか律儀に検査して います。もし実装されていなければ、引数 implementation で渡された関数をコンストラクタのプ ロトタイプに追加する処理に進みます。この場合 this はプロトタイプが拡張されたコンストラク タ関数を参照します。 5 ゴ 0 まとめ この章では、オプジェクトリテラルやコンストラクタ関数を使う基本バターンからさらに先に進 んで、オプジェクトを作成するさまざまなパターンについて学びました。 ここで学んた名前空間パターンは、グローバル空間をきれいにし、コードを構造に従って構成す るのに役立ちます。また、依存関係を宣言するパターンは、簡単ですが驚くほど役に立ちます。次 にプライバシーパターンについて詳しく説明しました。プライベートメンバ、特権メソッド、プラ イバシーがきわどい状況、プライベートメソッドをパプリックメソッドとして開示する手法を取り 上げました。これらのパターンはすべて、よく使われる強力なモジュールバターンの構成要素です。 次に名前空間の代替手段としてサンドボックスパターンについて学びました。これは他に依存し ない環境をコードやモジュールに提供するのに役立ちます。 最後に締めくくりとして、オブジェクト定数、静的メソッド ( パプリックおよびプライベート ) 、 連鎖、ちょっと変わった method ( ) メソッドについて詳しく見ていきました。
5 章 オプジェクト作成のバターン JavaScript では、オプジェクトリテラルやコンストラクタ関数を使えば、簡単にオプジェクトを 作成できます。この章ではこれ以外の方法でオプジェクトを作成するパターンについて解説します。 JavaScript は単純な言語なので、名前空間、モジュール、パッケージ、プライベートプロバティ、 静的メンバといった他の言語でよく使われる機能のための特別な構文はありません。この章では、 こうした機能を実装したり、置き換えたり、あるいは別なものとして考えるためのパターンを紹介 します。 これから見ていく名前空間、依存関係の宣言、モジュールバターン、サンドボックスパターンは、 アプリケーションのコードをしつかりした構造で構成し、暗黙のグローバルによる影響を緩和する のに役立ちます。さらに、プライベート特権メンバ、静的メンバ、プライベート静的メンバ、オプジェ クト定数、連鎖、コンストラクタを定義するクラス風の方法について解説します。 5.1 名前空間バターン 名前空間を使えば、プログラムで必要なグローバルの数を減らせると同時に、名前の衝突や長す ぎる接頭辞を避けることができます。 JavaScript では名前空間は言語の構文として組み込まれていませんが、とても簡単に実現できま す。グローバルスコープをたくさんの関数、オプジェクト、変数で汚染するかわりに、アプリケーショ ンやライプラリごとにグローバルオプジェクトをひとつ作成します ( ひとつだけ作るのが理想的で す ) 。このオプジェクトにあらゆる機能を追加することができます。 次のコードを考えてみましよう。 / / 変更前 : グローバルが 5 個あります / / 警告 : アンチパターンです / / コンストラクタ function Parent() { } function Child() { } / / 変数
6.3 クラシカルなバターンその 1 : デフォルトバターン一 123 ここで kid. say ( ) を実行すると何が起きるでしよう ? オプジェクト ( 3 ) にはそのようなメソッドが ないので、プロトタイブ連鎖を経由して ( 2 ) が調べられます。オプジェクト ( 2 ) にもそのようなメソッ ドはないので、連鎖をたどって ( 1 ) が調べられて、 こでやっと見つかります。 say ( ) の内部には this. name への参照があるので、これを解決する必要があります。こで再び探索が始まります。 この場合、 this はオプジェクト ( 3 ) を指しますが、 こには name はありません。次にオプジェクト ーーには値 Adam を持つ name プロバティがあります。 ( 2 ) が調べられ 最後にもう一歩先を見てみましよう。次のようなコードがあったとします。 この場合、どのように連鎖をたどるのか、図 6-3 に示します。 kid. say(); / / "Patrick" kid. name = "Patrick" ・ var kid = new Chi1d(); say() ・・ Parent. prototype = Patrick 継承される点です。この固有のプロバティはひとつのインスタンスに固有であり再利用できないこ このパターンの欠点のひとつは、追加した固有のプロバティとプロトタイププロバティの両方が 6.3.2 バターンその 1 を使うときの欠点 が「光臨」し、一連の連鎖の探索で見つかります。 delete kid. name を使って新しいプロバティを削除すると、オプジェクト ( 2 ) の name プロバティ kid. name の探索と同じなので、オプジェクト ( 3 ) ですぐに見つかります。 ソッド say を探し、次に ( 2 ) から探し、最後の ( 1 ) で見つかります。しかし今回の this. name の探索は 有のプロバティが直接作成されます。 kid. say ( ) を実行するとき、ますオプジェクト ( 3 ) からこのメ kid. name を設定してもオプジェクト ( 2 ) の name プロバティは変化しません。オプジェクト ( 3 ) に固 図 6-3 継承しさらに子オブジェクトにプロトタイプを追加した後のプロトタイブ連鎖 name proto new Chi1d() proto name = Adam ・・・ new Parent()
6.3 / / 空の子インストラクタ function Chi1d(name) { } こで継承の魔法がかかります inherit(ChiId, Parent); クラシカルなバターンその 1 . デフォルトバター ン一 121 これで親と子のコンストラクタができました。親コンストラクタのプロトタイプにメソッド say() を追加し、継承の処理を行う inherit() という関数を呼び出しています。 JavaScript 言語で inhe ⅱ t ( ) 関数が提供されているわけではないので、自分で実装する必要があります。これを実装 する一般的なアプローチをいくつか見ていきましよう。 6.3 クラシカルなバターンその 1 : デフォルトバターン p 訂 ent ( ) コンストラクタを使ってオプジェクトを作成し、このオプジェクトを Child ( ) のプロト タイプに代入する手法は、最もよく使われます。再利用可能な inherit() 関数の最初の実装を示し ます。 function inherit(), P) { = new P(); C. prototype prototype プロバティは関数ではなくオプジェクトを指すようにすべきです。これは重要なこと なので覚えておいてください。コンストラクタ自体ではなく親コンストラクタを使って作成したイ ンスタンス ( オプジェクト ) を指すようにしなければなりません。これを実現するには new 演算子が 必要なので、 new に注意を払えということです。 あなたのアプリケーションで new ChiId ( ) を使ってオプジェクトを作成すると、次の例で示すよ うに parent() インスタンスの機能がプロトタイプを介して得られます。 var kid = new Child(); kid. say(); / / "Adam" 6.3.1 プロトタイブ連鎖をたどる このパターンを使うことで、 this に追加したインスタンス固有のプロバティ ( この例では name) とプロトタイプのプロバティおよびメソッド ( この例では say ( ) ) の両方が継承されます。 この継承パターンがプロトタイブ連鎖でどのように動作しているか見ていきましよう。説明をわ こではオプジェクトをメモリ上のある区画と考えてみます。この区画には かりやすくするため、 データや他の区画を指す参照を格納できます。 new Parent ( ) を使ってオプジェクトを作成するとき、 こには name プロバティに相当するデータが格納 このような区画 ( 図 6-1 の ( 2)) が作成されます。 されます。 ( たとえば (new parent ). say ( ) を使って ) say ( ) メソッドにアクセスしようとしても、 区画 ( 2 ) にはそのメソッドはありません。しかし隠されたリンク __proto ーがコンストラクタ関数
ーン モジュールバタ return i; return -1 ; isArray = function (a) { return ops. call(a) / / var の終わり / / バブリック API を開示する return { isArray: isArray, indexOf: inArray array string; 5.4 5.4.2 コンストラクタを作成するモジュール ー 103 前の例では MYAPP. utilities. array というオプジェクトを作成しましたが、コンストラクタ関数 を使ってオプジェクトを作成した方が便利な場合もあります。モジュールバターンを使うときでも これは可能です。唯一の違いは、モジュールがオプジェクトではなく関数を返すようにモジュール を即時関数で包む点です。 コンストラクタ関数 MYAPP. utilities. A Ⅱ ay を作成するモジュールバターンの例を以下に示しま す。 MYAPP. namespace('MYAPP. utilities. Array' ) ; MYAPP. utilities. Array = (function ( ) { / / 依存関係 var uobj = MYAPP. utilities. object, ulang = MYAPP. utilities. lang, / / プライベートのプロバティとメソッド . Constr; / / var の終わり / / 1 回きりの初期化手続き / / バブリック API - - コンストラクタ constr = function ( 0 ) { = this. t0Array(0); this . elements
6.5 return ー meaowww' クラシカルなパターンその 3 : プロトタイプを拝借して設定する一 127 Bird() { . W1ngs this. fly = true; CatWings() { function this function Bird. apply(this); Cat. apply(this); say 、ー n95 console. dir(jane); var jane = new CatWings(); この結果を図 6-5 に示します。プロバティが重複した場合、後勝ち方式で解決されます。 function() 2 4 図 6-5 CatWings オブジェクトを Firebug で調べる コンストラクタの新しいインスタンスを指すように設定します。 前の 2 つのパターンを組み合わせます。ますコンストラクタを拝借し、次に子のプロトタイプが プロトタイプを拝借して設定する 6.5 クラシカルなバターンその 3 : ターンが解決します。 say( ) メソッドにアクセスできるようにするには、どうすれば良いでしようか ? この課題を次のパ それでは、子がプロトタイププロバテイも継承できるようにするには、前の例にある kid が する危険はありません。 親の固有のメンバの本物のコピーが得られるのは利点です。子が親のプロバティを誤って上書き を追加するのに最適な場所です。 に、プロトタイプはインスタンスごとに再作成されないので、再利用可能なメソッドやプロバティ このパターンの欠点は明らかです。プロトタイプから何も継承されません。すでに説明したよう 6.4.3 コンストラクタ拝借バターンの利点と欠点
104 ー 1 + = 1 ) { 5 章オブジェクト作成のバターン / / バブリック API - - プロトタイプ constr. prototype = { constructor: MYAPP. utilities. Array, " 2 . 0 " verslon: t0Array: function (obj) { fo て (var i = 0 , a = 冂 , len = obj[i]; = 0bj. length; return a; / / 新しい名前空間に代入された / / コンストラクタを返す return Constr; var arr = new MYAPP. utilities. Array(obj); この新しいコンストラクタは次のように使います。 i く len; 5.4.3 グローバル変数をモジュールに取り込む ローバルシンポルの解決が高速化されます。取り込まれた変数はこの関数にローカルになるからで プジェクトそのものを渡します。グローバル変数を取り込むことで、即時関数の内部で行われるグ 数はどんな値でも構いませんが、通常はグローバル変数への参照を渡すか、あるいはグローバルオ モジュールバターンを変形すれば、モジュールを包む即時関数に引数を渡すことができます。引 ひとつのグローバル変数がアプリケーションのグローバルになることを前提にしています。名 サンドボックスパターンは、名前空間パターンが持つ以下のような欠点を解決します。 5.5 サンドボックスパターン }(MYAPP, this)); こでローカルになる 〃グローバル名前空間オブジェクトへの参照が / / グローバルオブジェクトへの参照と MYAPP. utilities. module = (function (app, global) { す。
ー 4 章関数 コールバック / ヾターン 関数を引数として渡します。 設定オブジェクト たくさんの引数をひとつの関数で制御するのに役立ちます。 関数を返す ある関数の戻り値を別の関数にします。 カリー化 既存の関数とその引数の部分リストをもとに新しい関数を作ります。 2. 初期化バターン : 初期化と設定作業 ( ウエプのページやアプリケーションでは頻繁に行われま す ) をよりきれいに実行するのに役立ちます。グローバル名前空間を一時的な変数で汚染しな い構成にします。以下のパターンが含まれます。 即時関数 定義されたら即座に実行されます。 即時オブジェクト初期化 初期化作業を無名オプジェクトで構成します。無名オプジェクトは即座に呼ばれるメソッ ドを提供します。 初期化時分岐 アプリケーションの動作中に何度も同じ分岐コードを実行するのではなく、初回に実行さ れるとき分岐コードを 1 回だけ実行します。 3. 性能バターン : コードの実行速度を向上させます。以下のパターンが含まれます。 メモ化 計算値が何度も繰り返し計算されないように関数のプロバティを使います。 自己定義関数 関数を新しい本体で上書きして、 2 回目以後の呼び出しで行う作業を減らします。 88