18 ー 2 章必須バターン 第二の修正パターンは while ループを使います。 var myarray = [ ] , i = myarray. length; while (i--) { 24 fO 「 - in ループ 句を言ってきます。 これらの最適化が注目されるのは性能が重要な処理においてだけでしよう。 JSLint は i-- にも文 / / mya Ⅱ ay [ i ] に対して何か処理を行う var man / / オブジェクト 次の例を考えてみましよう。 ティを除外するために、メソッド has0wnProperty() を使うことです。 オプジェクトのプロバティを順番に処理するとき重要なのは、プロトタイブ連鎖から来たプロバ このため配列には for ループを使い、オプジェクトには fo ト in ループを使うのが好ましいです。 ラーになるかもしれません。また、プロバティのリストの順序は for- in では保証されていません。 ですが、お勧めできません。配列オプジェクトにカスタム機能が拡張されていた場合、論理上の工 ( JavaSc ⅱ pt では配列もオプジェクトなので ) for - in ループは配列に対しても使えなくはないの たループは列挙 (enumeration) とも呼ばれます。 for - in ループは配列以外のオプジェクトを繰り返し処理するときに使うべきです。 for-in を使っ Object. prototype. clone = function ( ) { } ; if (typeof Object. prototype. clone = "undefined") { / / すべてのオブジェクトにメソッドが追加される / / コードのどこかで heads: 1 legs: 2 , hands: 2 , ります。 表示されるのを避けたければ、 has0wnProperty() を呼んでこのプロトタイプを除外する必要があ の新しいメソッドに自動的にアクセスできるのです。 man を for- in で列挙するときに clone ( ) が を使って拡張されています。プロトタイブ連鎖は生きています。つまりすべてのオプジェクトはこ man を定義している箇所より前か後かで、 0bject のプロトタイプが clone() という便利なメソッド この例では man という簡単なオプジェクトがオプジェクトリテラルで定義されています。この
4.9 設定オブジェクトー 81 生 9 設定オプジェクト 設定オプジェクトパターンは、 API をきれいに提供する方法です。他のプログラマが使うライプ ラリやコードを作るときに向いています。 ソフトウェアは開発や保守にともなって要件も変わっていきます。最初はある要件が念頭にあっ たとしても、後から機能が追加されていくことは珍しくありません。 設定オプジェクトの欠点は次の通りです。 パラメータの追加や削除が簡単になります。 コードが読みやすく保守が楽になります。 オプションのパラメータを安全に省略できます。 パラメータの順序を覚える必要がありません。 設定オプジェクトの利点は次の通りです。 addPerson(conf); last: "Wayne" "Bruce" first : username: "batman" var conf = { この関数を以下のように使えるようにします。 addPerson(conf); 良いアプローチです。このパラメータを configu て ation にちなんで conf と名付けましよう。 多数のパラメータを渡すのは不便です。パラメータをひとつにまとめてオプジェクトにするのが "Wayne", new Date(), null, null, "batman"); addPerson("Bruce" なければならないし、パラメータの順序が狂わないように注意が必要です。 略できないことを覚えなければいけません。この関数を呼ぶとき、オプションのパラメータも渡さ この段階ですでにこの関数のシグネチャは少し長くなっています。ユーザの名前は必須であり省 function addPerson(first, last, dob, gender, address) {. のパラメータは省略できるようにリストの最後にしています ) 。 ことがわかりました。そこでこの関数を修正して、新しいパラメータを追加しました ( オプション 後になって、実際には誕生日も格納する必要があること、また性別と住所がオプションで必要な function addPerson(first, last) {. ムを受け取り、その人物をリストに追加します。 addPerson ( ) という関数を書いているとしましよう。この関数はファーストネームとラストネー
60 ー 4 章関数 / / アンチパターン / / あくまで説明のための書き方です var add = new Function('a, b' 'return a + b'); add(), 2 ) ; / / 3 が返る このコードでは、 add ( ) はコンストラクタによって作成されたわけですから、疑いの余地なく、 ひとつのオプジェクトです。ただし Function ( ) コンストラクタを使うのは良いやり方ではありま せん。 (eval ( ) と同様 ) コードが文字列として渡され評価されるからです。またコードを書くのも読 むのも不便です。引用符はエスケープする必要があり、また関数内のコードを読みやすくするため に適切にインデントするときも、余計な注意を払わければなりません。 関数でもうひとつ重要な特徴は、スコープを提供する点です。 JavaScript には波括弧を使ったロー カルスコープはありません。言い換えるとプロックはスコープを作らないのです。 JavaScript にあ るのは関数スコープだけです。関数の内部で var を使って定義された変数がローカル変数になり、 その関数の外部からは見えません。波括弧がローカルスコープを提供しないということは、 if の条 件部あるいは for や while のループの内部で var を使って変数を定義しても、その変数はそれらの 内部でローカルな変数にはならないのです。その変数は関数の中でのみローカルなのです。もし関 数で囲んでなければ、その変数はグローバル変数になります。 2 章で解説したように、グローバル の数を最小にするのは良い習慣ですから、変数のスコープをきっちり管理するには関数が必須の手 段になります。 4.1.1 用語の整理 関数を定義するときに使われるコードに関連する用語について解説しておきましよう。 ノヾターン について説明するときには、コードと同じくらい用語を正確かっ誤解のないように使うことが重要 だからです。 次のコードを考えてみましよう。 / / 名前付き関数式 var add = function add(), b) { return a 十 b; のコードで示す関数は、名前付き関数式を使っています。 の関数式にある名前 ( 2 番目の add ) を省略すると、名前なし関数式、いわゆる無名関数になり ここす / / 関数式、いわゆる無名関数 var add = function (), b) { return a 十 b; 「名前付き関数」は、省略可能な名前を定義している特殊な関数式ということになります。
38 ー 2 章必須パターン GoogIe の Closure CompiIer が使われています。ページの起動時間を短縮するのに役立ちます。 以下はミニファイされたコードの例 ( YU12Library の Event ユーティリティの一部 ) です。 YAHOO. util. CustomEvent=function(D,C,B,A){this. type=D;this. scope=Cllwindow;this. silent =B;this. signature=AllYAHOO. util. CustomEvent. LIST;this. subscribers=[];if(!this. silent){} var E=" YUICE0nSubscribe";if(D!==E){this. subscribeEvent=new YAHOO. util. CustomEvent(E,this,true);}. 、ニファイアは、空白、改行、コメントを除去するだけでなく、上記のコードのパラメータ D 、 c 、 B 、 A のように、変数名を短い名前に変更します ( そうしても安全なときだけ ) 。グローバル変数の名 前を変更するとコードが壊れるかもしれないので、 ニファイアが名前変更するのはローカル変数 だけです。できる限り口一カル変数を使うほうが良いのは、 こうした理由もあるのです。たとえば DOM の参照にグローバル変数を使うと、ひとつの関数の中に複数必要になるので、ローカル変数 に代入するのが良いプラクティスです。、ニファイによって、ダウンロードが高速になるだけでな く、変数名が高速に解決できるようになるので、実行時のコードも高速化されます。 補足しておくと、 Google Closure Compiler は ( アドバンスモードで ) グローバル変数もミニファ イしようとするので危険です。この余分なミニファイを活用するには、注意と自制が必要です。 プロダクトのコードをミニファイするのは重要です。ページの性能向上に役立っとはいえ、その 作業はミニファイアに任せるべきです。あらかじめミニファイされたコードを書くのは間違いです。 常に説明的な変数名、一貫した空白とインデント、コメントで書くべきです。あなたが書いたコー ドは人が読むものなので、保守する人がそれをすぐに理解できるようにしておき、ファイルサイズ を減らすのはミニファイア ( 機械 ) に任せましよう。 2.16 JSLint を実行する JSLint については 1 章ですでに紹介しましたが、この章でも何点かふれておきます。コードを JSLint にかけるのが良いプログラミングパターンであることは了解されているでしよう。 JSLint は何を見ているのでしようか ? この章で説明したいくつかのパターン ( 単独 va てパターン、 parselnt() の基数、常に波括弧が使われているか ) だけでなく、他にも以下のような違反がないか 見ています。 ・正規表現の中の不適切なエスケープ文字 oid 、 with 、 eval の使用 ・安全でない UTF 文字 定義される前に使われている変数 ・到達できないコード プペースのツールが提供されています。これをダウンロードすれば、 WSH (Window システムの一 いことに、多くのプラットフォームの JavaScript インタープリタでダウンロードできるようにウェ JSLint は JavaScript で書かれています (JSLint のチェックをほとんど合格したはすです ) 。嬉し
10 ー 2 章必須バターン コードの作成に費やすことができます。そのコードはそのときその場では動作しているでしようが、 アプリケーションが完成に近づくにつれ、レビューし、再検討し、微調整しなければいけないさま ざまな問題がコードに発生します。 こうした変化の結果として、最初にコードを書いたときには数時間しかかからなかったのが、そ コードが全面的に書き換えられる、あるいは他のアーキテクチャ、他の言語に移植される コードが転用される 新しい環境 ( たとえば新登場のプラウザ ) でアプリケーションを動作させる必要がある アプリケーションに新機能が追加される ・バグが発覚する 保守しやすいコードとは、以下のようなコードです。 ションの成功に極めて重要なのはこのためです。 のコードを読むのに何週もかかることになります。保守しやすいコードを作ることがアプリケー ドキュメントが整備されている 一人で書いたようなコードに見える ・見通しが良い ・一貫性がある ・読みやすい console. log(this. myglobal); / / " he110 " console. log(window["myglobal"]); / / " he110 " console. log(window. myglobal); / / " he110 " console. log(myglobal); / / " he110 " myglobal = " he110 " ; / / アンチパターン るグローバル変数にアクセスする方法を示します。 これは通常グローバルオプジェクト自体を指しています。以下のコード断片は、プラウザ環境にあ になります。プラウザには window というプロバティがグローバルオプジェクトに追加されています。 クトがあります。作成されたあらゆるグローバル変数はこのグローバルオプジェクトのプロバティ JavaScript のどんな環境であれ、関数の外部で this を使ってアクセスできるグローバルオプジェ 関数の外部で宣言されたか、あるいは宣言されることなく使われます。 おいてローカルであり、その関数の外部からは利用できません。一方、グローバル変数はあらゆる JavaScript は関数を使ってスコープを管理します。関数の内部で宣言された変数は、その関数に 22 グローバル変数の使用を最小にする JavaScript を書く際に考慮すべきこれらの点について、この章の後半で解説します。
2.1 1 getLast: function ( ) { コメントを書く 31 この例では getName() はパプリックメソッドを意図していて、安定した API の一部であるのに対 して、 getFirst() と getLast ( ) はプライベートメソッドを意図しています。どれも通常のパプリッ クメソッドではあるのですが、アンダースコアを前置きすることで、 person オプジェクトを使うと き、これらのメソッドは次のリリースでの動作は保証されていないこと、そのため直接使うべきで はないことを警告する効果があります。 JSLint はアンダースコアが前置きされていると苦情を言い ますが、オプション nomen : fa lse に設定すると無効にできます。 以下に p ⅱ vate の作法のバリエーションを示します。 アンダースコアが後置きされていればプライベートの意味 ( name 、 getE1ements ( ) など ) 。 アンダースコアを 1 個前置きしたら protected プロバティ、 2 個前置きしたら一 private プロ ノヾティ。 ・ Firefox では言語の一部ではないけれどもいくつかの内部プロバティが利用できます。これら proto ーやー parent ーのように、アンダースコア 2 個が前後に置かれて のプロバティは、 います。 2 Ⅲコメントを書く たとえあなた以外の誰も触らないとしても、コードにコメントを書くべきです。問題に深く集中 しているときは、コードが何をしているか十分把握できていますが、一週間後にコードに戻ってみ ると、それがどのように動作するのか正確に思い出しづらくなります。 自明なことに関してコメントする必要はありません。すべての変数にコメントをつけたり、すべ ての行にコメントをつけるのは、やりすぎです。ただし、関数とその引数と戻り値、さらには興味 深いアルゴリズムや変わったテクニックについてはドキュメントにしておく必要があります。コメ ントはそのコードの未来の読者にとって手がかりになると考えましよう。コメントと関数とプロバ ティ名を読むだけでコードが何をしているのか理解できる必要があります。たとえば、特別な処理 を実行する 5 、 6 行のコードがあったとします。そのコードの目的とその理由を説明した 1 行コメ ントがなければ、コードの詳細は読みとばされるはすです。 習慣にするのが難しいけれども最も重要なのは、コメントを最新に保つことです。古くなっ たコメントは誤解を生み、それならコメントがない方がよっぽどましな場合もあります。 次節で紹介しますが、コメントはドキュメントを自動生成するのに役立ちます。
4.1 月 . 示、 2 番目の add を省略して名前なし関数式にしても、その定義は影響されす、関数の呼び出し方も 同じです。唯一の違いは、この関数オプジェクトの name プロバティが空文字列になる点です。 name プロバティは ( ECMA 標準の一部ではなく ) 言語の拡張なのですが、多くの環境で利用可能です。 2 番目の add を残すと、プロバティ add. name の中身は文字列 "add" になります。 name プロバティ は Firebug などのデバッガを使うときや同じ関数を再帰的に呼び出すときに便利です。必要なけれ ば省略してかまいません。 最後に関数宣言について説明します。これは他の言語で使われる関数とほとんど同じに見えます。 function f00 ( ) { こに関数の本体を書きます 構文上は、名前付き関数式と関数宣言は似ています。関数式の結果を変数に代入しない場合はな おさらです ( この章の後半で説明するコールバックパターンでこの手法が使われます ) 。関数宣言と 名前付き関数式は、次の節で説明するように、その関数が現れる文脈でしか区別できない場合もあ ります。 両者の構文の違いは、最後がセミコロンで終わっているかどうかです。セミコロンは関数宣言で は必要ありませんが、関数式では必要です。自動的にセミコロンが挿入される仕組みであるとはい え、関数式では必ずセミコロンを使うべきです。 関数リテラルという用語もよく使われます。これは関数式や名前付き関数式を意味するよ 4.1.2 うです。曖昧さを避けるため、この用語は使わない方が良いでしよう。 宣言と式 : 名前と巻き上げ では関数宣言と関数式のどちらを使うべきでしようか ? 構文上の制約で宣言が使えないとき、 のジレンマは解決されます。次の例では、関数オプジェクトをパラメータとして渡しています。ま たオプジェクトリテラルの中でメソッドを定義しています。 / / これは関数式です / / 引数として関数 ca11Me に渡しています caIIMe(function ( ) { / / 名前なし関数式 / / 無名関数として知られています / / これは名前付き関数式 ca11Me(function me() { / / 名前付き関数式 / / 名前は " me "
7.9 オブザーバー 179 最後はゲームの開始処理と終了処理です。 / / 開始 mediator. setup(); window. onkeypress = mediator. keypress; / / 30 秒経過したらゲーム終了 setTimeout(function ( ) { WindOW. onkeypress alert( 'Game over! ・ ) ; } , 30000 ) ; = null; ス 9 オブザーバ オブサーバ ( Obse Ⅳ er ) パターンはクライアント側の JavaScript プログラミングでよく使われま す。プラウザイベント ( マウスオーバー、キープレスなど ) はこの例です。プラウザが起こしたイベ ントと比較するために、プログラムで作成したイベントをカスタムイベントと言います。このパター ンは、購読者 / 発行者パターンとも言います。 このパターンの狙いは疎結合を促進することです。あるオプジェクトが別なオプジェクトのメ ソッドを呼び出すかわりに、あるオプジェクトは別なオプジェクトの特別な機能を購読し、そのオ プジェクトから通知を受けます。この購読者は観察者とも言われます。一方、観察されているオプ ジェクトは発行者あるいはサプジェクトと呼ばれます。重要なイベントが発生したとき、発行者は すべての購読者に通知 ( 呼び出し ) します。ある形式のイベントオプジェクトでメッセージを渡すこ ともあります。 7.9.1 例その 1 : 雑誌の購 ハロノし このパターンの実装方法を理解するために、具体例を見てみましよう。発行者 pape ては日刊の新 聞と月刊の雑誌を発行します。購読者 joe はその通知を受け取ります。 paper オプジェクトはすべての購読者を格納する配列プロバティ subscribers を持つ必要があり ます。購読の動作はこの配列への追加にすぎません。イベントが発生すると paper は購読者のリス トをループで回り、購読者に通知します。この通知は購読者オプジェクトのメソッド呼び出しを意 味します。このため、購読の動作において、購読者はそのメソッドのひとつを paper の subscribe() メソッドに提供します。 paper は unsubscribe() も提供しています。これは購読者の配列から削除するメソッドです。 pape ての最後の重要なメソッドは publish() です。これは購読者のメソッドを呼び出します。まと めると、発行者オプジェクトには以下のメンバが必要です。
120 ー 6 章コード再利用パターン Java では次のように書きますが、 person adam = new person(); JavaScript では次のように書きます。 var adam = new person(); Java は強い型付けの言語なので、 adam の型が Person であることを宣言する必要があります。 の点を除けば、どちらの構文も同じに見えます。 JavaScript のコンストラクタ呼び出しを見ると、 あたかも person がクラスであるかのように見えますが、 Person はただの関数にすぎないことを忘 れないことが重要です。このように構文が似ているため、多くの開発者が JavaScript をクラスの 発想で思考し、クラスを仮定した概念や継承パターンを開発する結果になりました。こうした実装 を「クラシカルな」と形容し、クラスについて思考する必要のないそれ以外のパターンを「モダンな」 と呼ぶことにします。 あなたのプロジェクトで継承パターンを採用することになったら、選択肢は少なくありません。 クラスが関係しないことにチームが馴染めないのでなければ、あなたはモダンなパターンで頑張る べきです。 この章ではますクラシカルなパターンからはじめて、次にモダンなパターンを解説します。 6.2 クラシカルな継承を使ったとき期待される結果 クラシカルな継承の実装が目指すゴールは、コンストラクタ関数 Chi1d ( ) が別のコンストラクタ parent ( ) からプロバティを取得できるようにすることです。 語は別の意味に誤解されるかもしれないからです。 ラス」という用語は使わないようにしましよう。 JavaScript での開発においては、この用 第誤解の余地がありません。一般論として、チーム内でコミュニケーションを取るときは、「ク ます。「コンストラクタ関数」や「コンストラクタ」は長ったらしい用語ですが、正確で クラシカルなバターンについて説明しますが、「クラス」という用語は使わないようにし return thiS. name; parent. prototype. say = function ( ) { / / プロトタイプに機能を追加 this. name = name Ⅱ 'Adam' ・ function Parent(name) { / / 親コンストラクタ 次の例は Parent() と Child() という 2 つのコンストラクタを定義しています。
1 % ー 8 章 DOM とブラウザのパターン 第 1 部分では、イベントオプジェクトへのアクセスを獲得する必要があります。イベントオプ ジェクトには、イベントとイベントを発生させたページ要素に関する情報が含まれています。 このイベントオプジェクトはコールバックイベントハンドラに渡されますが、 onclick プロバ ティを使う場合、これが渡されないので、かわりにグローバルプロバティ window. event にア クセスします。 第 2 部分ではラベルを更新する実作業を行います。 第 3 部分ではイベントの伝搬をキャンセルします。この例では特に必須ではありませんが、 般にこの処理を行わないと、イベントがドキュメントルートあるいはウインドウオプジェクト にまで伝搬してしまいます ( 泡に喩えてバブルアップと言われます ) 。 こでも 2 通りの処理が 必要です。 W3C 標準のやり方 (stopPropagation()) と IE 用の別処理 (canceIBubbIe) を使います。 第 4 部分では、要請に応じて、デフォルトの動作を抑止します。いくつかのイベント ( リンク のクリック、フォームの送信 ) にはデフォルトの動作がありますが、 preventDefauIt() を使え 8.3.2 イベント委譲 ソッドを使ってイベントユーティリティを作成しているのは意味があります。 これを見てわかるように、対になった作業 ( IE 対策 ) が伴うので、 7 章で解説したフアサードのメ ば抑止できます ()E の場合は returnVaIue を false に設定します ) 。 それ以外のクリックはすべて無視します。 除外する必要があります。この例では、関心があるのはボタンのクリックだけないので、 div での 前の例で紹介した myHand1e て ( ) 関数を 1 カ所だけ変更すれば転用できます。関心がないクリックを ボタンごとにリスナを結びつけるかわりに、 div にリスナを結びつけて、クリックを覆います。 く /diV> く button>C1ick me three: 0 く /button> く button>Click me t00 : 0 く /button> く button>Click me: 0 く /button 〉 く div id="click-wrap"> 図 8-1 イベント委譲の例 : クリックするとラベルの数値がインクリメントされる 3 つのボタン Click me: 2 Click me t00 : 11 CIick me three: 7 以下のマークアップを対象に作業を行います。 jspatterns.com/b00k/8/click-delegate.html でデモを見ることができます。 div 内部にボタンが 3 個ある例を見てみましよう ( 図 8-1 ) 。このイベント委譲の例は http:// をひとっ結びつけるだけですみます。ボタンごとに結びつけるとリスナは 10 個になってしまいます。 べントリスナの数を減らせます。 div 要素の内部にボタンが 10 個ある場合、 div にイベントリスナ イベント委譲パターンはイベントの伝搬 ( バブリング ) に有効です。個々のノードに結びつけるイ