66 ー 5 章 U にでの疎結合 / / 使い方 addItem("/item/4" "Fourth item"); この簡単な例では、 Handlebars の真の柔軟性を紹介できません。 Handlebars では、簡単な プレースホルダの置換の他に、簡単なロジックとループ処理をテンプレートに組み込むことが できます。 項目ではなくリスト全体を表示したいけれども、表示すべき項目が実際に存在するときだけ 表示したいと想定しましよう。次のような Handlebars テンプレートを作ることができます。 {{#if items}} く ul > {{#each items}} く li > く a href="{{url}}">{{text}} く /a> く /li> {{/each}} く / ul > {{/if} { { # if } } プロックへルバは、 items 配列に少なくとも要素が 1 つ存在しないと、これを表示 しません。 { { # each } } プロックへルバは、この配列の各要素をイテレートします。テンプレー トを関数にコンパイルしたら、次の例で示すように、 items プロバティを持っオプジェクトを var result = template({ / / 2 項目を持つリストの HTML を返す items: [ ] var result = template({ / / 空の文字列を返す template に渡します。 items : [ text : url: text : url: Handlebars には、 "First item" "/item/l" "Second item" "/item/2" この他にもプロックへルバがあり、 いすれも JavaScript に強力なテンプ レート機能をもたらすように設計されています。
n ⅶとの比較を避ける JavaScript では、変数が適切な値で埋まっているかどうかを判定するために変数を null と 比較してテストするのは、いまだに問題含みのパターンです。次の例を見てみましよう。 var Contr011er process : function(items) { = null) { if (items ! = items. sort() ; items. forEach(function(item) { / / 何か実行 / / Bad この process() メソッドでは、 sort() と forEach() が使われていることから、 items が配列 であることが期待されています。このコードの意図は明らかです。 items 引数に配列として構 成されていないかぎり処理を継続してはいけません。このアプローチの問題は、 null と比較し ても実際に起こりえるエラーを防げない点です。 items の値が 1 や文字列など勝手なオプジェ クトである可能性があります。これらはいずれも null とは等しくありませんが、 s t ( ) の実 行で process() は落ちてしまいます。 変数を null と比較するだけでは、その変数の値を安全に処理できるかどうかを判定するの に十分な情報が得られません。幸いにして JavaScript では変数の真の値を判定するいくつか の方法が提供されています。 圧 1 プリミテイプ値の判定 JavaScript には、文字列、数値、プーリアン、 null 、 undefined という 5 つのプリミテイプ 型があります。ある値が文字列、数値、プーリアン、 undefined のいすれかであると期待して
42 ー 4 章変数、関数、演算子 / / Bad d0Something(); function d0Something() { a1ert("HeIIo world ! " ) ; JavaScript 工ンジンはコードを次のように解釈するので、 function doSomething() { a1ert("He110 world ! " ) ; d0Something(); このアプローチは機能します。 この振る舞いのため、 JavaScript 関数は常に使用するより前に宣言しておくのを推奨します。 この設計はクロックフォードのコード規約に登場します。クロックフォードは、次のように function doSomething() { } else { a1ert("Hi!"); function doSomething() { if (condition) { / / Bad 通りの振る舞いにはなりません。 さらに、関数宣言がプロック文の中に出現してはいけません。たとえば、このコードは期待 JSLint と JSHint はどちらも、関数を宣言する前に使用するとき、警告します。 d0Something(items[i]); for (i=O, len=items. length; i く len; i + + ) { / / 何か実行 function d0Something(item) { result = value + 10 ; = 10 , value var i, len, function d0SomethingWithItems(items) { ある関数の中で変数を宣言した直後にローカル関数を書くことを推奨しています。
5.5 JavaScript から HTML を隔離する一 63 本来ならば、必要以上にテンプレートテキストを JavaScript の内部に組み込みたくありません。 テンプレートを他のマークアップと同じ領域に定義され、 HTML ページの中に直接埋め込むこ とで匐 avaScript からアクセス可能なように作ります。これは 2 通りのやり方で実現できます。 第 1 は、テンプレートを HTML のコメントとして組み込むやり方です。コメントは、要素や テキストと同じ DOM ノードなので、クエリが可能であり、 JavaScript を使ってそのコンテン の JavaScript では、コメントからテンプレートテキストを参照しています。 このコメントは、使用されるリストの最初の子として、用途に適した内容で配置します。次 く / ul > く li 〉く a href="/item/3">Third item く /a> く /li> く li > く a href="/item/2">Second item く /a> く /li 〉 く li > く a href="/item/I">First item く /a> く /1i〉 く ul id="mylist"> く !--<li id="item%s"> く a href="%s">%s く /a> く /li 〉ー> ツを抽出することができます。 var mylist = document. getElementById("my1ist") , = mylist. firstChi1d. nodeValue; tempIateText テンプレートテキストを参照できたら、後はこれをフォーマットして DOM に挿入するだけ です。これはすべて以下の関数を使って実現します。 function addltem(url, text) { var mylist tempIateText result = document. getElementById("mylist"), = mylist. firstChild. n0deValue, = sprintf(tempIateText, url, text); mylist. insertAdjacentHTML("beforeend", result); / / 使い方 addItem("/item/4" "Fourth item"); このメソッドは与えられた情報でテンプレートテキストを処理し、 insertAdjacentHTML() を使って結果の HTML を注入します。このステップで HTML 文字列は DOM ノードになり、 く ul 〉の子として追加されます。 テンプレートを HTML ページに組み込む第 2 のやり方は、く sc ⅱ pt > 要素をカスタムの type プロバティを一緒に使います。プラウザはく script> 要素の中のコードをデフォルトで JavaScript であると想定しますが、プラウザが理解できない type を指定することで、 JavaScript でないものをプラウザに知らせることができます。
4.3 関数呼び出しにおける空白ー aIert("Yo!"); 43 このコードの実際の動作はプラウザごとに異なります。ほとんどのプラウザは条件を評価す ることなく、自動的に第 2 の宣言を採用します。 Firefox は condition を評価し、適切な関数 宣言を使います。これは ECMAScript 仕様ではグレーな領域なので、避けるべきです。関数宣 言は、条件文の外側でのみ使うべきです。このパターンは GoogIe では明確に禁止されています。 生 3 関数呼び出しにおける空白 ほば例外なく、関数呼び出しでは関数名と開き括弧の間には空白を置かないスタイルが推奨 されています。これによって、プロック文と区別できるようになります。 / / Good d0Something(item); / / Bad : プロック文のように見えるので d0Something (item); / / プロック文 while (item) { / / 何か実行 クロックフォードのコード規約はこれをはっきり強調しています。 D 可 0 、 SproutCore 、 GoogIe は、コードのサンプルを通してこのスタイルを暗に推奨しています。 jQuery では、次にように、開き括弧の後と閉じ括弧の前にさらに空白を置くことが明記され ています。 / / jQuery スタイル d0Something( item ) ; この意図は引数をより読みやすくするためです。 jQuery では、このスタイルの例外もいくつ か列挙されています。特にオプジェクトリテラル、配列リテラル、関数式、あるいは文字列が 単独引数として関数に渡される場合です。このため、以下の例はいずれも妥当と見なされます。 一般論として、 1 つ以上例外があるスタイルは、開発者を混乱させるので、良くありません。 doSomething("Hi! " ) ; doSomething([ item ]); d0Something({ item: item } ) ; dosomething(function() { } ) ; / / jQuery での例外
5.5 を知らせます。 JavaScript から HTML を隔離する 65 く script type="text/x-handlebars-template" id="list-item"> く li 〉く a href="{{url}}">{{text}} く /a> く /li> く /script> れを使ってテンプレートテキストを関数にコンパイルします。 り込む必要があります。このライプラリでは、 Hand1ebars というグローバル変数を作成し、 このテンプレートを使うためには、ますべージに Handlebars の JavaScript ライプラリを取 var script tempIateText template = document. getElementById("list-item"), script. text, = HandIebars. compile(script. text); 変数 template はここでは関数になります。これが実行されると、フォーマットされた文字 列が返ります。必要なことは、 name と u て 1 というプロバティを持つオプジェクトをこれに渡す だけです。 = template({ "Fourth item" " /item/4" var result text : url: 結果をフォーマットする部分では、引数を自動的に HTML 工スケープ処理することで、セキュ リティ問題を防ぎ、簡単なテキスト値でマークアップが壊れないようにしています。たとえば、 文字「 & 」は自動的に & amp ; にエスケープされます。 これらをまとめて 1 つの関数にします。 function addltem(url, text) { var mylist script templateText template div result; = document. getE1ementById("my1ist"), = document. getE1ementById("Iist-item"), script. text, = Handlebars. compile(script. text), = document. createE1ement("div") , = template({ result text: text, u て 1 : url div. innerHTML = result; list. appendChi1d(div. firstChild);
202 ー付録 A JavaScript スタイルガイド / / Bad: undefined リテラルを使用 if (variable = = undefined) { / / 何か実行 A. 4 演算子の間隔 二項演算子は、前後に空白を 1 個置いて、式を明確にする。演算子には、代入と論理演算子 も含まれる。 / / Good var found = (values[i] = / / Good = item); if (found & & (count > 10 ) ) { d0Something(); / / Good for (i = 0 ; i く count; process(i); / / Bad: 空白がない = (values[i]= var found / / Bad: 空白がない if (found&&(count>l の ) d0Something(); / / Bad: 空白がない ==item); fO て (i=0; i く count; i + + ) { process(i); A. 5 括弧の間隔 括弧を使うときには、開き括弧の直後と閉じ括弧の直前に空白を置く。 / / Good var found = (values[i] = / / Good = item);
64 ー 5 章 U にでの疎結合 く script type="text/x-my-template" id="list-item"> く li > く a href="%s">%s く /a> く /1i〉 く /script> く sc ⅱ pt > 要素の text プロバティを使えば、テンプレートテキストを参照できます。 var script tempIateText = document. getEIementById("1ist-item"), = script. text; addltem() 関数は、次のように変更します。 function addltem(url, text) { var mylist script tempIateText result div = document. getE1ementById("my1ist"), = document. getEIementById("1ist-item"), = script. text, = sprintf(tempIateText, url, text), = document. createEIement("div"); div. innerHTML = result. rep1ace(/AYs*/, mylist. appendChi1d(div. firstChiId); / / 使い方 addItem("/item/4" "Fourth item"); このバージョンの関数での変更点の 1 つは、テンプレートにあるかもしれない先頭の空白を 除去している点です。この余分な空白は、く sc ⅱ pt > タグを開いたとき、その行の上にテンプレー トがあるために発生します。テンプレートをそのままの形で注入すると、く div > の内部に空白 テキストのノードが作成され、く li > の代わりにテキストノードがリストに追加されてしまいま す。 5.5.3 代替案 3 : 複雑なクライアントサイドのテンプレート 前節で使ったテンプレートのフォーマットは、まったく簡単なもので、エスケープは一切行っ ていません。テンプレートをより堅牢にするためには、 Hand1ebars のようなソリューション を考慮したくなるかもしれません。 Handlebars は、プラウザでの JavaScript で動作するよう に設計された、完全にクライアントサイドのテンプレートシステムです。 HandIebars のテンプレートは、プレースホルダを示すために二重括弧を使います。前節の テンプレートの Handlebars バージョンは次のようになります。 く li > く a href="{{url}}">{{text}} く /a> く /li> Handlebars テンプレートのプレースホルダは、 JavaScript での値の名前に対応するように 名前が付けられています。 Handlebars では、く script> 要素に text/x-handlebars-template を 値とする type 属性を指定することで、 HTML ページにテンプレートが埋め込まれていること
の 数 関 fo て (i=0, len=items. length; i く len; i + + ) { d0Something(items[i] ) ; クロックフォードは、それよりさらに進めて、次のように関数の冒頭で var 文を 1 つだけ使 うのを推奨しています。 function doSomethingWithItems(items) { var i, len, value = 10 , result = value + 10 ; f0 て (i=0, len=items. length; i く len; i + + ) { doSomething(items[i]); D 可 0 では、変数が互いに関連するときだけ、 var 文を結合するのを許可しています。 va て文をすべて結合して、行ごとに変数を 1 っ初期化するのがお勧めです。初期化されてい ない変数は、次の例のように、最後に書くべきです。 function d0SomethingWithItems(items) { var value result len; = 10 , = value + 10 , for (i=0, len=items. length; i く len; i + + ) { doSomething(items[i]); ダウンロードがより高速になるように、 var 文を結合す 最低限、コードがより小さくなり、 るのをお勧めします。 42 関数の宣 変数宣言と同じように、関数宣言も、 JavaScript 工ンジンによって巻き上げられます。 その ため、コードの中で関数が宣言されるより前で関数を使うことが可能です。
62 ー 5 章 U にでの疎結合 表示することができます。 JavaScript ライプラリを使うと、 DOM 要素にリモートのマ プを読み込ませるプロセスが少しばかり容易になります。 YUI と jQuery のどちらも、似たよ うな API でこれを実現しています。 / / YUI function 10adDialog(name, oncomplete) { Y. one("#dlg-holder"). load("/js/dialog/" + name, oncomplete); / / jQuery function IoadDia10g(name, oncomplete) { $("#dlg-holder"). load("/js/dialog/" + name, oncomplete); ークアッ テンプレートのテキストを JavaScript に変換するのが、このプロセスの重要な部分です。 "/item/4" = sprintf(tempIateText, "Fourth item"); var result / / 使い方 return (i く args. length) ? args[i + + ] return text. replace(/%s/g, function() { var i=l, args=arguments; function sprintf(text) { 使います。 でこのプレースホルダを実データで置き換えることを意図しています。この関数は次のように は C 言語の sprintf() と同じフォーマットです ) 。 DOM に結果を注入する前に、 JavaScript このテンプレートには、テキストを挿入すべきプレースホルダとして %s があります ( これ く li > く a href="%s">%s く /a> く /1i〉 のようになるでしよう。 の付いたマークアップ部品です。たとえば、リストに項目を追加するためのテンプレートは次 クライアントサイドのテンプレートは、 JavaScript によって完成させる必要のあるスロット 5.5.2 代替案 2 : 簡単なクライアントサイドのテンプレート ん。 プのチャンクについては、クライアントサイドのテンプレートを検討したくなるかもしれませ DOM に保持するのは、性能上の理由から良いやり方ではありません。より小さなマークアッ する必要があるときにも役立ちます。たとえば、使用しない大量のマークアップをメモリや リモート呼び出しを使ってマークアップを注入するのは、ページに大量の HTML を注入