26 ー 2 章コメント if (!from Ⅱ !to) { return receiver; } else { from to = supplier; recelver; Y. mix() メソッドでは定数を使って処理方法を決定します。 mode 引数はこれらの定数のどれ かと等しくなりますが、数値だけから定数の意味を理解するのは困難です。複雑な決定になる 場合が説明されているので、このコードはうまくコメントされています。 2.3.2 工ラーになりそうな箇所にコメントする if ( !needle Ⅱ !element Ⅱ !need1e[NODE TYPE] Ⅱ !e1ement[NODE TYPE]) { var ret = false; るかもしれません。次に示すのは YUI ライプラリの Y. DOM. contains() メソッドの例です。 なエラーの特殊な例になります。プラウザ特有の何かを行っていないコードは、エラーに見え かすための汚いコードを使わざるをえないことがあります。こうした対応は、実際には潜在的 JavaScript を使う開発者は、非効率で美しくないコード、あるいは古いプラウザで正しく動 2.3.3 ブラウザ特有のハック このコードを見た他の開発者が誤って「修正」してしまうのを避けられます。 う。この行の最後にあるコメントは、代入演算子を意図的に使っていることを示しているので、 子 = ではなく等号演算子 = を使うつもりだったのに、それをタイプミスしたのだと思うでしょ なコードに慣れていなくて、コメントがない状態でこの行を見たら、これはもともと代入演算 なプラクティスではないので、 lint ツールによって問題として報告されるでしよう。このよう このケースでは while ループの制御条件の中で代入演算子が使われています。これは標準的 return element; (!fn Ⅱ fn(element)) ) { if ( ( a11 Ⅱ element[TAG_NAME]) & & while (element &&(element = element[axis])) { / / NOTE: 代入している コメントを書いて誤解を避けるべきです。 YUI の例をもう 1 つ見てみましよう。 げる必要のある問題を生みます。他の開発者には間違いに見えるコードを書いているときには、 こうした「修正」は、実際には掘り下 ます。そのコードが問題の原因でなかった場合を除くと、 者が問題になりそうに見えるコードを見つけては指摘し、チームでそれを修正することがあり コメントすべきもう 1 つのケースは、エラーになりそうなコードがあるときです。ある開発
80 ー 7 章イベント処理 / / 5 章の addListener() addListener(eIement, "click", hand1eCIick); このコードは event オプジェクトから c1ientX と c1ientY という 2 つのプロバティを使うだ けです。これらのプロバティは、ページの要素をユーザに表示する前に位置づけるのに使われ ます。このコードはかなり簡潔で問題がないように見えますが、公開すべきものは限定すべき であるので、実際には悪いパターンがコードで使われています。 ス 2 ルール 1 : アプリケーションロジックを切り離す 前の例の第 1 の問題は、イベントハンドラがアプリケーションロジックを含む点です。アプ リケーションロジックは、ユーザのアクションよりもアプリケーションに関連する機能です。 前の例のアプリケーションロジックは、特定の場所にポップアップ表示を行うことです。この アクションはユーザが特定の要素をクリックしたときに発生しますが、いつもそうとはかぎり ません。 アプリケーションロジックをイベントハンドラから切り離しておくのが定石です。なぜなら、 将来別のアクションによって同じロジックを発生させる必要があるかもしれないからです。た とえば、ユーザが要素の上にカーソルを移動させたときやキーポードの特定のキーが押された とき、ポップアップ表示させるように後から決めるかもしれません。このとき、そのコードを 第 2 、第 3 のイベントハンドラに複製して、同じイベントハンドラで複数のイベントを処理す るようにするかもしれません。 イベントハンドラにアプリケーションロジックを持たせる場合のもう 1 つの欠点は、テスト にあります。テストにおいては、ある要素を誰かにクリックしてもらって反応を見るといった オーバーヘッドなしで、機能を直接実行開始させる必要があります。イベントハンドラの内部 にアプリケーションロジックがあると、テストするためにはそのイベントを発生させるしかあ りません。いくつかのテストフレームワークではイベントをシミュレートすることができると はいえ、それは通常は最善のテスト方法ではありません。簡単な関数呼び出しを使って機能を 実行開始できる方がいいです。 常にイベント処理のコードからアプリケーションロジックを切り離すべきです。前の例をリ ファクタリングする第 1 ステップとして、ポップアップ処理のコードを独自の関数に移動しま す。この関数は、アプリケーションに対して定義した 1 個のグローバルオプジェクトにあるの がいいでしよう。イベントハンドラも同じグローバルオプジェクト上にあるべきなので、結果 として次の 2 つのメソッドになります。 / / Better : アプリケーションロジックを切り離す var MyAppIication hand1eC1ick: function(event) { this. showPopup(event);
71 use strict" f00 = 10 ; 6.3 1 ーグローバルのアプローチー / / ReferenceError: f00 は未定義 このコードを strict モードがサポートされている環境 (lnternet Explorer 10 + 、 Firefox 4 + 、 safari 5.1 + 、 Opera 12 + 、 Chrome) で実行すると、 2 行目で ReferenceError が「 f00 is not defined 」というメッセージで投げられます。 strict モードを有効にすると JavaScript の振る舞いの多くが変化するので、古いコードで作 業しているときは注意深く使うべきです。比較的新しいコードについては、常に strict モード を使って、グローバル変数の誤った作成など、 strict モードが捕捉できるよくあるプログラミ ングのエラーを避けるのがベストです。 6.3 1- グローバルのアプローチ 「グローバルを一切導入せすに JavaScript を書くにはどうすればいい ? 」と考えていません か。いくつかの賢いパターンを使えば技術的には可能ですが、このアプローチは長期的に考え ると現実的でもメンテナンスしやすくもありません。 JavaScript をチームで開発するとき、通 常はさまざまなシナリオで複数のファイルがロードされることを意味します。この異質なコー ドの間でコミュニケーションを実現する唯一の方法は、コードのすべてがその存在に依存でき る何かを持たせることです。できるだけ小さなグローバルの足跡を持たせて、 1 つのグローバ ルオプジェクトだけを作成するように同意するのがベストなアプローチです。 ・ CIosure ライプラリでは goog というグローバルオプジェクトが定義されています。 ・ Do 扣では d0j0 というグローバルオプジェクトが定義されています。 性のためだけに追加されました。 後者はあるべージで $ を使う他のライプラリ (prototype. js など ) と一緒に使う場合の互換 ・ jQuery では実際には、 $ と jQuery の 2 つのグローバルオプジェクトが定義されています。 ・ YUI では YUI というグローバルオプジェクトが定義されています。 1 ーグローバルのアプローチは、人気のあるすべての訂 avaScript ライプラリで使われていま す。 this. page this. title = title; function B00k(title) { る 1 つのオプジェクトが欲しいと仮定してみます。そのコードは次のようになるでしよう。 つのグローバルのプロバティとしてグローバルになるのです。たとえば、本書の各章を表現す トに押し込むのです。そうすれば、グローバルを複数作成する代わりに、それぞれの機能は 1 とがなさそうな名前 ) でグローバルを 1 つ作成し、機能をすべて 1 つのグローバルオプジェク 1 ーグローバルのアプローチの背後にある考えは、一意な名前 ( ネイテイプ API で使われるこ
12.5 何を使うべきか ? ていたので、 Mozilla による Firefox 3.6 でのこのメソッドの削除は、 MooTools の古いバージョ ンを実行しているユーザは皆、この削除の影響を被るコードを持っていることを意味しました。 この状況から、 MooTooIs はこの課題を「解決」した最新バージョンへのアップグレードをユー ザに呼びかけました。 ューザ工ージェント文字列をベースにしたプラウザ判定を徹底的に検証してきました。 れは Firefox 3.6 が示したように、潜在的な課題があるため、 JavaScript ライプラリの中 では標準的なプラクティスになりました。プラウザが互いに近づくに連れて、特徴を探し てそれぞれを区別するのは、ますます困難で危険になってきます。今後永久に、プラウザ 判定を使うのは、世界クラスの JavaScript フレームワークから期待されるプラウザ横断 的に一貫した体験をもたらすために、それを使わすに済ますことができない場合だけに限 定します。 12.5 何を使うべきか ? 特徴推定とプラウザ推定は、何としても避けるべきバッドプラクティスです。特徴検出はべ ストプラクティスであり、ほばあらゆるケースで必要となるのがこれでしよう。特徴検出を使 う前に、その特徴が実装されているかどうか知る必要があります。特徴間の関係を推定するの は、偽陽性や偽陰性になるので、試してはいけません。 こまでユーザ工ージェント判定を使うなとは言っていないのは、それが妥当なユースケー スがあると信じているからです。しかし、それが妥当なユースケースがそうたくさんあるとは 信じていません。ユーザ工ージェント検証を検討するために覚えておくべきことは、これが安 全なのは特定のプラウザの古いバージョンをターゲットにする場合だけということです。最新 のプラウザや将来のバージョンをターゲットにしてはいけません。 可能なかぎり特徴検出を使うことを勧めます。それが不可能なとき、ユーザ工ージェント判 定を代替として使います。プラウザ推定は決して使わないことです。なぜなら、メンテナンス 不能なコードで埋まり、プラウザの進化に合わせて不断に更新することが必要になるからです。
136 ー第Ⅲ部自動化 利点と欠点 JavaScript で自動化されたビルドシステムを使う場合、以下の利点があります。 ません。 ・あまり技術指向でない開発者は、ビルドシステムを使うときトラブルを経験するかもしれ プロダクションでのバグを特定するのが難しくなります。 プロダクションにデブロイされたコードは、編集中のコードと同じようには見えないので、 したらプラウザで再表示するだけという使われ方が増えます。 ん。ある開発者は、このステップに調整するとき多くのトラブルを経験します。何か変更 ・開発環境での変更の一方で、開発者はローカルビルドを実行する必要があるかもしれませ こうした自動化を使うことで、以下のようないくつかの欠点ももたらされます。 ・共通するタスクを簡単かっ迅速に再実行できます。 プロダクションサーバへの自動デブロイが容易です。 テストが自動化されるので、問題点が容易に特定できます。 きます。 デブロイ前に、ファイルの結合やミニファイなど、いくつかの方法で JavaScript を処理で ・静的分析を自動的に実行させて、エラーを見つけることができます。 スコントロールを設定することができます。 要がないので、サーバでの使用に最適化されているかどうか心配せずに、好きなようにソー ソースコントロールに保存しているコードをプロダクションで公開する際にミラーする必 の利点が実現されたら、それを好むようになります。 ローカルビルドを実行する必要があるという考えを嫌う開発者ですら、いったんこのシステム 著者の経験では、自動化によって得られる利点の方が欠点をはるかに上回ります。変更後に
91 案しました。 function isArray(va1ue) { return 0bject. prototype. t0String. call(value) = 8.3 プロバティの判定ー " [object Array] " ・ Kangax は、与えられた値でネイテイプの t0St ⅱ ng ( ) メソッドを呼び出すと、どのプラウザ でも標準の文字列が出力されるのを発見しました。配列の場合その文字列は " [ objectArra ⅵ " になるので、その配列がもともとどのフレームにあったかに関係なく、この呼び出し判定は機 能します。 Kangax のアプローチはすぐに一般的になり、いまではほとんどの JavaScript ライ プラリで実装されています。 このアプローチは、開発者が定義したオブジェクトとネイティブオブジェクトを 区別する判定にも有効です。このテク二ックを使うと、たとえばネイティブの JSON オブジェクトは [object JSON] を返します。 その頃から、 ECMAScript 5 では正式に JavaScript に Array. isArray() が導入されました。 このメソッドの目的は、値が配列であるかどうかを正確に判定することです。 Kangax の関数 を使うときと同様、 Array. isArray() はフレーム横断で渡された配列値でも機能します。この ため、いまでは多くの JavaScript ライプラリにこれと類似したメソッドが実装されています。 function isArray(va1ue) { "function") { if (typeof Array. isArray = return Array. isArray(value); } else { return Object. prototype. t0String. call(value) " [object Array] " ・ Array. isArray() メソッドは、 lnternet Explorer 9 + 、 Firefox 4 + 、 Safari 5 + 、 Opera 10.5 + 、 Chrome で実装されています。 圧 3 プロバティの判定 開発者が null( および undefined) を使うもう 1 つの典型的なケースは、 特定のプロバティが存在するかどうかを判定する場合です。 / / Bad : 偽値チェック if (0bject[propertyName]) { / / 何か実行 あるオプジェクトに
8.2 参照値の判定ー 87 null との単純な比較では、値が期待したものかどうかに関する十分な情報が得られません。 1 っ例外あります。期待する値の 1 つが実際に null である場合、直接 null と比較テストしても 問題ありません。この null との比較は、次のように = または ! = のどちらかを使って行わ れるべきです。 / / null との比較の場合、この方法で行う = document. getElementById("my-div"); var element if (element ! = = null) { element. cIassName "found" ・ 与えられた DOM 要素が見つからないとき、 document. getEIementById() が null を返すこと になります。このメソッドは null か DOM 要素を返します。 null は期待する結果の 1 つでは ないので、 ! = = を使ってテストするのは問題ありません。 か厳密不等価演算子 ( ! = = ) を使いましよう。 は効率の悪い方法です。 null をテストする必要がある場合、厳密等価演算子 ( = = ) typeof null を実行すると「 Object 」が返るので、これで null をテストするの 参照です。あらかじめ組み込まれている参照型には、ちょっと名前を挙げるだけでも、 0bject 、 参照値はオプジェクトとして知られています。 JavaScript では、プリミテイプ型でない値は 8.2 参照値の判定 クトの型は「 object 」としか返ってこないので、まったく役に立ちません。 Array 、 Date 、 Error があります。 typeof 演算子を参照に対して実行しても、次のようにオプジェ console. log(typeof { } ) ; console. log(typeof [ ]); console. log(typeof new Date()); console. log(typeof new RegExp()); / / "object" / / "object" / / "object" / / "object" オプジェクトに対して typeof を使用する場合のもう 1 つの欠点は、 値が null の場合も 「 object 」が返される点です。 console. log(typeof null) ; / / "object" 特定の参照型の値を判定するには、 instanceof 演算子を使うのが最善の方法です。 instanceof の基本構文は次の通りです。 value instanceof constructor いくつか例を見てみましよう。
74 ー 6 章クローバル変数 / 関数を作らない ここからのこの名前空間を使用開始できる Y0u礼10ba1. Books. MaintainableJavaScript. author = "Nich01as C. zakas" ・ * YourGIobal. Books に HighPerformanceJavaScript を追加する * Y0u礼10ba1. B00ks. Maintainab1eJavaScript は残っている YourG10baI. namespace("B00ks. HighPerformanceJavaScript"); / / 正しい参照 console. 10g(YourGlobaI. B00ks. Maintainab1eJavaScript. author); / ここからメソッド呼び出しに続けて新しいプロバティを追加できる Y0u礼10ba1. namespace("Books"). ANewBook = 1ーグローバルの上で namespace() メソッドを使うことで、開発者はその名前空間が存在する と自由に想定できます。この方法は、各ファイルごとにまず最初に namespace ( ) が呼び出され、 開発者が使用する名前空間が宣言されます。このアプローチはまた、名前空間を使う前にその 名前空間が存在するかどうかを検査するといった退屈きわまりない作業から開発者を解放しま す。 1 - グローバルのアプローチをチームがより効率的に活用できるようになります。 文字にします。これは設定項目の質問ですが、これらの選択肢を定義することで、 しよう。 YUI と同じように大文字で始めるか、それとも Dojo のようにすべて小 コードの他の部分を使うときは、名前空間に関連する規約を定義するようにしま 作る方法です。モジュールの概念は YU13 で形式化されました。次のような形になります。 YUI モジュールは、察しの通り、 YUIJavaScript ライプラリを使って新しいモジュールを 6.3.2.1 Y 団モジュール Definition : 非同期モジュール定義 ) モジュールの 2 つです。 つかあります。最も広く使われているのは、 YUI モジュールと AMD (Asynchronous Modu1e ECMAScript6 までに関しては ) ありませんが、モジュールを作成するためのパターンがいく モジュールは正式には JavaScript の部品ではありません。モジュールの構文は ( 少なくとも トを持っこともあります。 内部に、すべてのコードを記述します。モジュールは、名前と依存する他のモジュールのリス ません。その代わり、タスクの実行やインタフェースの公開を責任をもって行う単独の関数の ルです。モジュールとは、汎用の機能の断片であり、グローバル変数や名前空間を新規追加し 1 ーグローバルのアプローチを強化するもう 1 つの方法として開発者がよく使うのがモジュー 6.3.2 モジュール
3.6 for-in ル process(values[i]); 37 このループの本体は 2 回実行され、 3 回目はスキップされ、 4 回目のイテレーションは復活 します。このループは、邪魔されないかぎり、最後のイテレーションまで継続されます。 クロックフォードのコード規約は、 contin ue の使用を禁止しています。彼は cont inue を使 うコードは、条件を使えばもっと巧く書けると主張しています。たとえば、前の例は、次のよ うに書き換えることができます。 = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 ] , var values i, len; for (i=O, len=values. length; i く len; i + + ) { if (i ! = 2 ) { process(values[i]); クロックフォードはこのパターンの方が開発者は理解しやすく、エラーにもなりにくいと主 張しています。 D 可 0 では、 ( ontinue は b て eak と同様、使ってもよいと明示的に宣言されています。 本書では、 ( ontinue は可能なかぎり避けるとしても、それを完全に禁止する理由もない、とい う立場です。それを使うかどうかは、コードが読みやすくなるかどうかで決めるべきです。 JSLint は continue が使われていると警告します。 JSHint は continue が使われていても警 告しません。 3.6 fo 「 - in ループ fo ← in ループはオプジェクトのプロバティをイテレートする時に使われます。このループで は、制御の条件を定義する代わりに、システマチックにオプジェクトの各プロバティをイテレー トし、変数の中のプロバティ名を返します。 var prop; for (prop in object) { console. 10g("Property name is " + prop); console. log("Property value is " + object[prop]); for- in の問題は、オプジェクトのインスタンスプロバテイだけでなく、プロトタイプから継 承したプロバテイもすべて返すので、自前のオプジェクトのプロバティをイテレートすると、 期待に反した結果で終わるかもしれません。このため、 for - in ループは、インスタンスプロバ テイだけが得られるように has0wnp て operty ( ) を使ってフィルタするのがベストです。
8.3 / / 実行されない プロバティの判定ー 93 オプジェクトのインスタンスにプロバティが存在するかだけをチェックしたければ、 hasOwnProperty() メソッドを使いましよう。 JavaScript のオプジェクトはすべて 0bject を継 承するので、このメソッドを持っています。このメソッドはインスタンスに名前指定されたプ ロバティが存在する場合、 true を返します ( 指定されたプロバティがプロトタイプだけに存在 する場合、 false を返します )olnternet Explorer8 とそれ以前のバージョンでは、 DOM オプジェ クトは 0bject を継承していないので、このプロバティを持っていないことを覚えておきましょ う。このことは、 DOM オプジェクトの可能性があるオプジェクトで has0wnProperty() を使う 際には、ます has0wnP て operty ( ) の存在をチェックする必要があることを意味します ( そのオ プジェクトが DOM ではないことがわかっていれば、このチェックを省略できます ) 。 / / 実行される if ("hasOwnProperty" in object & & object. hasOwnProperty("related")) { / / 確実でないときは問題なし / / 実行される if (object. has0wnProperty("related")) { / / DOM オブジェクト以外には問題なし ましよう。そうすれば多くのバグが避けられるはすです。 プロバティの存在をチェックしたいときは、 in 演算子か has0wnProperty() を使って確認し る確認が必要なときだけにしています。 算子を使うようにしています。 has0wnProperty() を使うのは、インスタンスのプロバティであ lnternet Explorer8 とそれ以前のバージョンの問題があるので、著者は可能なかぎり in 演 のガイドラインに従います。 もちろん、値が null や undefined であるか特別にチェックしたいときは、 1 章