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 コンストラクタ拝借バターンの利点と欠点
リテラルとコンストラクタ JavaScript におけるリテラル記法パターンは、簡潔で表現しやすいので、エラーになりにくいオ プジェクト定義ができます。この章では、オプジェクト、配列、正規表現などのリテラルについて 解説し、なぜ 0bject() や Array() といった組み込みのコンストラクタよりも望ましいのか説明し ます。データ転送フォーマットを定義する際に配列やオプジェクトのリテラルがどのように使われ るか説明するために、 JSON フォーマットを紹介します。この章では、カスタムのコンストラクタ についても説明します。 new を使ってコンストラクタを意図した通りの振る舞いにする方法を解説 します。 この章のメッセージ ( コンストラクタを使うのは避けて、かわりにリテラルを使いましよう ) を発 展させて、 Number() 、 String() 、 B001ean() といった組み込みのラッパーコンストラクタについて 解説します。これらをプリミテイプの数値、文字列、プール値と比較する方法を説明します。最後に、 これらとは異なる組み込みの Er て or ( ) コンストラクタの使い方を簡単に解説します。 3 」オプジェクトリテラル JavaScript ではオプジェクトをキーと値の組のハッシュテープル ( 他の言語でいう「連想配列」と 似たものです ) とみなすことができます。値はプリミテイプあるいは他のオプジェクトになります。 どちらの場合もプロバティと呼ばれます。値は関数にすることもできます。その場合メソッドと呼 ばれます。 JavaScript で作成したカスタムオプジェクト ( ユーサ定義のネイテイブオプジェクト ) はいつで も変更可能です。組み込みのネイテイブオプジェクトの場合、プロバティの多くも変更可能です。 ます空のオプジェクトから始めて、これに機能を追加していくことができます。このように必要に 応じてその都度オプジェクトを作る場合、オブジェクトリテラル記法が最適です。 次の例を考えてみましよう。 / / 空のオブジェクトから始める var dog = { } ;
幻 4 ー 8 章 D 〇 M とブラウザのパターン く ?php ンミュレートしてみましよう。 が読み込まれて実行されたことを意味します。 この実装をテストするために、 ondemand. jp. php というスクリプトを使ってネットワーク遅延を console. log(logthis); console. log(' loaded and executed ・ ) ; function extraFunction(10gthis) { sleep(l); header('Content-Type: application/javascript' ) ; 以下のコードで require() 関数をテストします。 require('ondemand. js. php', function ( ) { extraFunction('loaded from the parent page' ) ; document. b0dy. appendChild(document. createTextN0de('done! ・ ) ) ; このコードはコンソールに 2 行出力し、ページに done! と表示します。 jspatterns.com/b00k/8/ondemand.htmll こあります。 87.6 JavaScript を事前に読み込む この例のデモは http:// 遅延読み込みパターンとオンデマンドバターンは、現在のページで要求されるスクリプトを後か ら読み込みます。また現在のページでは必要ないけれども、後に続くべージで必要になるスクリプ トも読み込むことができます。このやり方だと、ユーザが次のページに移動したとき、スクリプト は事前に読み込まれているので、全体の体感速度が向上します。 事前読み込みは、動的スクリプトパターンを使えば簡単に実装できます。しかし、そのスクリプ トは構文解析され実行されることを意味します。構文解析は事前読み込みにかかる時間が長くなる だけですが、事前に読み込まれたスクリプトが次のページで動作する前提である場合、たとえば DOM ノードを探す動作を行う場合、その実行で JavaScript 工ラーが起きてしまいます。 構文解析と実行を行わずにスクリプトを読み込むことは可能です。 CSS と画像に対しても適用で URL を指定します。 他のプラウザの場合、 script 要素のかわりにく object> を使い、 new lmage() . src = "preloadme. js" ・ IE の場合、おなじみの画像ビーコンパターンでリクエストすることができます。 きます。 その data 属性にスクリプトの
8.7 読み込みのための戦略ー 213 のタブだけだとします。ユーザはそのタブをクリックしないかもしれません。 こでロードオンデマンドバターンの登場です。 require ( ) という関数またはメソッドを作成し ます。読み込むスクリプトのファイル名、そしてスクリプトが読み込まれたときに実行されるコー 的く sc ⅱ pt > 要素のパターンに従うだけです。スクリプトが読み込まれたタイミングの判断は、 このような関数をどう実装すれば良いでしようか。スクリプトをリクエストするのは単純で、動 functionDefinedInExtraJS(); , function ( ) { require("extra. js" require() 関数は次のように使います。 ルバック関数を引数にします。 ラウザごとに差異があるのでちょっとした仕掛けが必要です。 function require(file, callback) { = document. getElementsByTagName(' script ' ) [ 0 ] , = document. createElement(' script' ) ; var script neWJ S / / I E newjs. onreadystatechange = function ( ) { ・ loaded ・Ⅱ newjs. readyState = if (newjs. readyState = newjs. onreadystatechange = null; ・ complete ・ ) { callback(); / / IE 以外 newjs. onload = function ( ) { callback(); newjs. src = file; script. parentN0de. insertBefore(newjs, この実装の要点は以下の通りです。 script); ・ IE の場合、 readystatechange イベントを購読して、 readyState が loaded あるいは complete になっているか調べます。それ以外のプラウザの場合、このイベントは無視されます。 Firefox 、 Safari 、 Opera の場合、 onload プロバティで load イベントを購読します。 このアプローチは Safari 2 では動作しません。このプラウザに対応させるには、特別な変数 ( こ の変数を追加するスクリプトファイルで定義します ) が定義されているか調べる処理を一定間 隔おきに実行する必要があります。その変数が定義された状態になったら、新しいスクリプト
54 ー 3 章リテラルとコンストラクタ このように、正規表現リテラル記法は簡潔であり、クラス風コンストラクタで考えすにすむので、 正規表現リテラルを使う方が好ましいです。 補足しておくと、 RegExp ( ) コンストラクタを使うときには引用符はエスケープし、バックスラッ シュは二重にエスケープする必要があります。上の例で示すように バックスラッシュ 1 文字に 致させるにはバックスラッシュ 4 文字が必要になります。これによって正規表現パターンは長くな り、読むのも変更するのも難しくなります。正規表現は敷居が高いので、簡潔にできる場合はそう すべきですから、リテラル記法にこだわるのがベストです。 3.6.1 正規表現リテラルの構文 後にパターン修飾子を続けることができます。このときパターン修飾子の文字は引用符で囲みませ 正規表現リテラル記法では、正規表現パターンをスラッシュで囲みます。 2 番目のスラッシュの ん。 g 1 グローバル検索 マルチ ( 複数 ) 行 大文字と小文字を区別しない パターン修飾子の順序や組み合わせには制約がありません。 var re = / 正規表現パターン /gmi; String. prototype. replace() のようなパラメータに正規表現オプジェクトを受け取れるメソッド を呼ぶとき、正規表現リテラルを使えばコードがより簡潔になります。 "abc123XYZ". replace(/[a-z]/gi, var no letters console. 10g ( no letters); / / 123 正規表現パターンが事前にわからす、しかも正規表現データを実行時に作成する場合には、 new RegExp() を使った方が良いです。 正規表現リテラルとコンストラクタのもうひとつの違いは、リテラルの場合オプジェクトが作成 されるのは構文解析の段階で 1 回きりという点です。ループの中で同一の正規表現を作成する場合、 それ以前に作成されたオプジェクトが返されます。このときプロバティ (lastlndex など ) はすべて 初回に設定された値のままです。次の例では、二度とも同一のオプジェクトが返されます。 function getRE() { var re re. f00 return re; var reg re2 = getRE(), = getRE();
5.3 プライベートなプロバティとメソッドー 97 ” b10 ( k ヨ 4 320 ( 0 ー 0 「 p ロ ( 0 screen_height 5 ( 「に n width 図 5-2 プライベートオブジェクトが変更されてしまいました POLA では必要以上に権限を与えるべきではないとされています。この場合、 Gadget の利用者は ガジェットが箱に収まるかどうかに興味があるので、必要な情報は大きさだけです。そこで、すべ てを与えるかわりに、幅と高さだけを含む新しいオプジェクトを返す getDimensions() を作るので す。そうすれば getSpecs() を実装する必要はないでしよう。 これとは別に、データをすべて渡す必要があるときには、汎用のオプジェクト複製関数を使って、 specs オプジェクトのコピーを作成するアプローチもあります。 6 章で 2 つの関数を紹介します。 ひとつは extend ( ) で、これは与えられたオプジェクトの浅いコピーを作成します ( トップレベルの パラメータだけをコピーします ) 。もうひとつは extendDeep() で、プロバティとその入れ子になっ たプロバティを再帰的にコピーするので、深いコピーを作成します。 5.3.4 オブジェクトリテラルとプライバシー これまでの例ではコンストラクタを使ってプライバシーを実現しました。ではオプジェクトリテ ラルを使ってオプジェクトを作成したときはどうすれば良いでしよう ? その場合もプライベートメ ンバを作ることが可能でしようか ? これまで見てきたように、プライベートデータを関数で包めば実現できます。オプジェクトリテ ラルの場合は無名即時関数が作成するクロージャーを使うことができます。次の例を見てみましょ var myobj ; / / これがオブジェクトになります (function ( ) { / / プライベートメンバ var name / / バブリックな部分の実装 / / var がない点に注意 myobj / / 特権メソッド getName: function ( ) { return name; myobj. getName(); / / "my, 0h my" 同じ考え方で少し実装を変えたものを次に示します。
3.4 配列リテラルー 51 Array() コンストラクタには賢い使い方がいくつかあります。たとえば、文字列を繰り返 す場合、以下に示す例では 255 個の空白が続く文字列を返します ( なぜ 256 でないのか、 理由を考えてみてください ) 。 var white = new Array(256). join(' う文字列が返ります。これを利用すると次のようになります。 "[object Array]" という文字列が返ります。オプジェクトのコンテキストでは "[object Object]" とい で検査することができます。配列のコンテキストで toString の ( a11 ( ) メソッドを呼び出すと、 この新しいメソッドが利用できない環境の場合、 0bject. prototype. toString() メソッドを呼ん } ) ; / / false slice: function ( ) { } length: 1 , Array. isArray({ / / 検査をだましてみる / / 配列もどきのオブジェクトを使って Array. isArray( [ ]); / / true 配列のとき true を返します。 ECMAScript 5 では Array. isArray() という新しいメソッドが定義されています。これは引数が かのバージョンではフレームにまたがって使われた場合、この検査は正しく動きません。 るので、万全な対応ではありません。 instanceof A Ⅱ ay を使うやり方もありますが、 IE のいくつ は、配列でないオプジェクトが同じ名前のプロバテイやメソッドを持っていないことを前提してい length プロバテイや slice() などの配列メソッドの存在を調べたりします。しかしこのような検査 の値がほんとうに配列かどうかを知る必要がある状況はよくあります。配列かどうかを決めるには、 この振る舞いは ( 配列はオプジェクトなので ) 理にかなっていますが、あまり役に立ちません。そ console. log(typeof [ 1 , (]); / / "object" 配列を対象に typeof 演算子を使うと "object" が返ります。 3.4.3 配列かどうかの検査 if (typeof Array. isArray = "undefined") { Array. isArray = function (arg) { return 0bject. prototype. t0String. call(arg) " [object Array] " ・
8.7 var Obj = document. createElement('object' ) ; 0bj. data ー preloadme. js" document. body. appendChild(0bj); 読み込みのための戦略ー 215 この object が表示されるのを防ぐために、 width と height の属性を 0 に設定します。 preload() という汎用の関数かメソッドを作ることができます。また初期化時分岐パターン ( 4 章 ) を使えばプラウザごとの差異を吸収できます。 var preload; if (/*@cc on!@*/false) { / / 条件付きコメントを使って IE かどうか判別します preload = function (file) { new lmage(). src = file; } else { preload = function (file) { var Obj = document. createElement('object ・ ) , body = document. body; 0bj. width = 0 ; 0bj. height = 0 ; obj. data = file; b0dy. appendChild(0bj); この関数を使ってみましよう。 preload('my web worker. js ' ) ; からのスクリプトとしては使われす、再びダウンロードされるだけです。 るだけにすぎません。コンポーネントを画像として事前読み込みしても、次のページのキャッシュ も new lmage() に対応しているので、これは役に立ちません。画像のためのキャッシュを持ってい たとえば typeof lmage が " function " かどうかで判別することもできます。しかし、どのプラウザ この場合、プラウザの振る舞いに関する十分な情報を取得できないからです。このパターンの場合、 このパターンの欠点はユーザ工ージェントの判別が行われる点ですが、これは避けられません。 var isIE !false; / / true れます。旧からは次のように見えるわけです。 設定されますが、それ以外のブラウザでは ( コメントは無視されるので ) false に設定さ のようにすると、旧の場合、条件付きコメントの中に否定の ! があるので isIE は true に var isIE = /*@cc on!@*/false; 列を調べるよりも少し安全です。この文字列はユーザが簡単に変更できるからです。 条件付きコメントを使ったプラウサ判別そのものは、 navigator. userAgent にある文字
52 ー 3 章リテラルとコンストラクタ 3 JSON こまでの解説で配列とオプジェクトのリテラルには慣れたでしようから、いよいよ JSON につ いて説明します。 JSON (JavaScript Object Notation) はデータ転送フォーマットです。軽量で便 利なので、多くの言語で活用されていますが、とりわけ JavaScript では重宝されています。 JSON について新たに学ぶべきことは実際にはありません。配列とオプジェクトのリテラル記法 を組み合わせただけにすぎません。 JSON データは次のようになります。 {"name": some JSON とオプジェクトリテラルの構文の唯一の違いは、 JSON ではプロバティ名を引用符で囲む 必要がある点です。オプジェクトリテラルでは、引用符で囲む必要があるのはプロバティ名が識別 " Dave " } のように空白を含む場合な 子として妥当でない場合だけです。たとえば { " fi て st name": どです。 JSON データでは、関数や正規表現リテラルを使うことはできません。 3.5.1 JSON を使った処理 2 章で解説したように、 JSON データを eval() を使って闇雲に評価するのは、セキュリティの問 題があるので勧められません。 JSON. parse() メソッドを使うのが最善策です。これは ES5 以降言 語の一部になっていて、新式プラウザの JavaScript 工ンジンはネイテイプ対応しています。旧式 の JavaScript 工ンジンの場合、 JSON. org ライプラリ (http://www.json.org/json2.js) を使えば JSON オプジェクトとそのメソッドにアクセスできます。 / / 〕 SON データを入力 var jstr = ・ {"mykey": / / アンチパターン var data / / 推奨パターン = JSON. parse(jstr); var data + jstr + つ・ ) ; console. log(data. mykey); / / "my value" すでに訂 avaScript ライプラリをお使いでしたら、訂 SON を解析するユーティリティがライプラ リに組み込まれている可能性があるので、訂 SON. org ライプラリを追加する必要はないかもしれま せん。たとえば、 YU13 を使っていれば、次のような処理が可能です。 / / JSON データを入力 var jstr = '{"mykey": "my value"}'
2.9 コーティングの作法ー 25 白か ? ) から、会議やメーリングリストで多くの炎上が繰り広げられてきました。所属する組織が 採用する作法をあなたが提案する立場にあるのであれば、抵抗に遭うのを覚悟し、各人各様で頑固 な意見を聴く必要があるでしよう。重要なのは、確立するべき作法の詳細な内容よりも、むしろ、 それがどんな作法であれ、作法を確立し、それに一貫して従うことです。 2.9.1 インデント インデントのないコードは読めたものではありません。それよりもっと悪いのは、インデントに 一貫性のないコードです。一見すると作法に従っているように見えますが、読み進むにつれ混乱さ せられることでしよう。インデントの使い方を標準化するのは重要なのです。 タブを使ったインデントを好むユーザは、エデイタでタブを表示するときに何個分の空白にする か調整できるのを理由にあげます。空白を好む開発者は、通常空白 4 個のインデントを使うようです。 いすれにせよ、チームの全員が同じ作法に従っていれば問題ありません。本書のサンプルでは、空 白 4 個のインデントを使っています。これは JSLint のデフォルトでもあります。 何をインデントすべきでしようか ? ルールは単純、波括弧の中にあるものすべてです。対象とな るのは、関数の本体、ループ ( d0 、 while 、 f0 ム for-in) 、 if 文、 switch 文、オプジェクトリテラ ル表記におけるオプジェクトプロバティなどです。以下のコードにインデントの使用例を示します。 function outer(), b) { va r C lnner; if (a 〉 b) { inner = function ( ) { return { } else { inner = function ( ) { return { r: C 十 d return inner; 2.9.2 波括弧 波括弧は、それが省略できる場合であっても必す使うべきです。文法上は if や for の中に文が ひとっしかなければ、波括弧は要求されませんが、その場合でも必す使うべきです。それによって コードの一貫性が高まり、更新が容易になります。 for ループの中に文がひとっしかない場合を想像してみましよう。波括弧は省略できますし、構