JavaScriptの変数についての考察

最近、Code Complete第2版という本を読んでいて、「変数の使用(第10章)」がとても為になる内容だったので、会社のチームメンバーに少しそのことについて話したら、JavaScriptについて興味深い話をすることができた。

第10章の内容について、議論の対象となった部分を引用する。

10.3 変数の初期化のガイドライン

変数は最初に使用する場所の近くで初期化する

リスト10-2: 悪い初期化(Visual Basic)
' すべての変数を宣言する
Dim accountIndex As Integer
Dim total As Double
Dim done As Boolean

' すべての変数を初期化する
acountIndex = 0;
total = 0.0
done = False
...

' accountIndexを使用するコード
...

' totalを使用するコード
...

' doneを使用するコード
While Not done
...
…中略

リスト10-2の例では、done変数を宣言した後、done変数を使用するコードが実行されるまでに、done変数が変更される可能性がある。

…中略

もう1つの問題は、すべての初期化を1か所にまとめると、done変数は最後の方でしか使用されないにもかかわらず、すべての変数がルーチン全体で使用されるという印象を与えることだ。

…中略

これは、「関連する作業を1つにまとめる」という近接の法則の一例である。

なるほどなるほど。しかし僕は普段の業務でプログラミング言語らしきものはJavaScriptしか使わないので、JavaScriptに置き換えて考えよう。

var accountIndex = 0,
    total = 0,
    done = false;
// accountIndexを使用するコード
...

// totalを使用するコード
...

// doneを使用するコード
while(!done) {
    
}
...

これが本書で「悪い例」とされているコードをJavaScriptに置き換えたコードだ。しかし関数の先頭で var hoge = 0, fuga = false; のようにして変数をまとめて宣言(&初期化)するのはJavaScriptではよく見られるコードだ。あのjQueryですらそのような記法を多用している。
JavaScript: The Good Partsによると

ほとんどの言語では、変数は一般的に最初に利用される場所で定義するのが最も良い方法だ。しかしこれは、ブロックスコープを持たないJavaScriptでは好ましくない。すべての変数は、それぞれの関数の先頭で定義したほうが良い。

とある。そう、JavaScriptはブロックスコープを持たない({}でくくられた部分限定の変数スコープ)といういわゆる変態言語であり、変数のスコープを生成するのは関数ブロックのみだ。それが理由で、変数はまとめて関数の先頭で宣言するという記法がベストプラクティスとされている。

さてしかし、Code Complete第2版による「変数は最初に使用する場所の近くで初期化する」ほうがよいという理屈の続きはこうだ。

10.4.1 変数の参照はまとめて

変数を参照してから次に参照するまでのコードは、「脆弱性の窓」(無防備な時間帯)である。その窓では、新しいコードが追加されたり、何気なく変数が変更されたり、変数に含まれていなければならない値が忘れられてしまったりする。変数の参照は、常に近いところにまとめて局所化するのが望ましい。

…中略

これを測定する方法は、変数の「持続間隔」を計算することである。

…中略
リスト10-6: 1と0の持続間隔(Java)
a = 0;
b = 0;
c = 0;
b = a + 1;
b = b / c;

この場合、bの1つ目の参照と2つ目の参照の間にコードが1行あるので、その持続間隔は1である。bの2つ目の参照と3つ目の参照の間にはコードがないので、その持続間隔は1である。bの2つ目の参照と3つ目の参照の間にはコードがないので、その持続間隔は0である。

…中略

リスト10-6では、bの平均持続間隔は (1 + 0) / 2 = 0.5 である。変数の参照を近くにまとめると、コードの読み手がコードをセクションごとに読んでいけるようになる。

…中略

10.4.2 変数の「寿命」はできるだけ短く

変数の持続間隔に関連して、変数の「寿命」という概念がある。変数の寿命とは、変数が存続する期間内に存在するステートメントの合計である。

…中略

変数の持続間隔とは異なり、変数の寿命は、最初に参照されてから最後に参照されるまでの変数の使用回数を計算に入れない。変数が最初に1行目で参照され、最後に25行目で参照された場合、変数の寿命は25(ステートメント)である。

…中略

変数の持続間隔と同様に、変数の寿命もできるだけ短くする、つまりステートメントの数を少なくすることが目標となる。持続間隔と同様に、ステートメントの数を少なくすると、脆弱性の窓が小さくなるという利点がある。

…中略

寿命を短くするもう1つの利点は、コードを正確に把握できることである。変数に10行目で値を代入し、45行目まで使用しない場合、2つの参照の間に空いている空間は、変数がその間に使用されていることを暗示する。

…中略

変数の寿命が短いと、コードが読みやすくなる。読み手が一度に頭に入れなければならないコードの行数が少なければ少ないほど、コードは理解しやすい。

変数には「平均持続間隔」と「寿命」という概念があるという。そしてそれらが短くなった方がコードの読み手にとって読みやすいコードになるという。

去年、僕がとある案件で数千行に及ぶJavaScriptを書いた際、最も頭を悩ませたのはコードを頭に入れることだった。機能を追加・修正するために一度に頭に入れなければならないコードが多すぎたため、開発が進めば進む程、コードの修正は困難を極めた。

プログラミング初心者が誰しも一度はぶつかる壁なのかもしれない。コードの分割をはじめとする、コードの設計の重要性を肌で感じた瞬間だった。
だからこそ、上記の「読み手が一度に頭に入れなければならないコードの行数が少なければ…」というくだりに深く納得したのだった。

さて、ではJavaScriptにおいて「変数の寿命を短くする」ことと「関数の先頭ですべての変数を宣言する」ことは両立するのだろうか?これについて隣席の@keiskeyの意見はこうだった。

関数1つの長さ自体を短くしてしまうのがいいのでは。Google Closure Libraryのコードを見ていると、中身が2行しかないメソッドに長々とした名前が付いていたりする。そんな長い名前を付けるんだったら直接コードを書いてしまえばいいやん、と思うけど、「変数の寿命」のポリシーをもって書かれていると考えると合点がいく。

なるほど…と再び納得するとともに、「この話、共有してよかった」と思った。

例えば上述(リスト10-2)のコードを一つの関数だと考えるとこうなる。

function Account() {
    var accountIndex = 0,
        total = 0,
        done = false;
    // accountIndexを使用するコード
    ...

    // totalを使用するコード
    ...

    // doneを使用するコード
    while(!done) {
        
    }
    ...
}

この関数をクラス風に書き直し、機能分割するとこういう感じになる。

function Account() {
    // コンストラクタ
    ...
}
Account.prototype = {
    getAccountIndex: function() {
        var accountIndex = 0;
        // accountIndexを使用するコード
        ...
        return accountIndex;
    },
    getTotal: function() {
        var total = 0;
        // totalを使用するコード
        ...
        return total;
    },
    checkStatus: function() {
        var done = false;
        // doneを使用するコード
        while(!done) {
            ...
        }
    }
}

prototypeにぶら下げたメソッド1つ1つを短くまとめて上手に機能分割することが、限りなく正解に近いのではないかと思った次第。

僕はプログラマと呼ばれる職種ではないのだけれど、プログラミングに関わる人間として、こういった考察はとてもおもしろいと感じる。また何か同じような話があったら書いていきたい。

JSONがinvalidでもjQuery.getJSON()はエラーを吐かない

記事のタイトルで言いたいことは言い切っちゃているんですけれども。
jQuery 1.4.2 の $.getJSON() 、もしくは $.ajax() でリクエストしたJSONファイルがinvalidだった場合、エラーは吐きませんがコールバック関数が実行されません

[jQuery] getJSON callback not firing? – jQuery Forum

なんでinvalidなのかと調べたところ・・・、JSONでは文字列は必ずダブルクォートで囲まないといけないことが判りました。シングルクォートではダメです。それからオブジェクトリテラルの左のオペランドにも、ダブルクォート省略不可!あー、なんか初歩的なところでつまづいています。

A string is a collection of zero or more Unicode characters, wrapped in double quotes, using backslash escapes.

http://json.org/

何しろエラーが出ないので、何が悪いのかわかりにくいです>< これで2時間はハマってしまった・・・。。

iPhoneのiPodアプリ風見出しインターフェースをJSで作ってみた

昨日のSugamo.cssで発表したコネタをポストします。

デモ:iPhoneのiPodアプリ風のインターフェースを再現するJS
JSファイル:iPhoneのiPodアプリ風のインターフェースを再現するJS

これはiPhoneにプリインストールしてあるiPodアプリのインターフェースを再現するJavaScriptです。要jQuery。

スクリーンショット:iPhoneのiPod風のインターフェースを再現するJS

iPodアプリで曲目リストやアーティストのリストを閲覧する際、「あ行」「か行」「さ行」みたいに並んでいるリストをスクロールしていくと、「あ行」のリストを見ているときは「あ」という見出しが画面上部に固定され、現在見ているリストがどの見出しに属するものなのかが見て分かるようになっています。つまり見出しがスクロールアウトされず、スクロールに着いてくる形になっているわけです。おそらく、一つのリストがとても長い場合は特に効果を発揮するかと思います。地味ですが確実にユーザビリティは向上するだろうという、粋な効果です。さすがApple。
何を言っているかよく分からない、という人はとりあえずデモを見てください・・・。

Continue reading

LightBox系スクリプト 16選

Lightbox系スクリプトの情報が一覧表になっているThe Lightbox Clones Matrixという素晴らしいサイトがあります。ここから一通り見て回って、自分なりに使えそうなものをピックアップしてみました。ほとんど勢いでまとめたものなので、情報としての正確性は低いかもしれませんが、ご容赦を。半分は自分用ってノリです。

自分はLightbox系のスクリプトを選ぶ時、そのサイトに予め組み込まれている(組み込む予定の)JSライブラリで使えるかどうかを基準に選ぶことが多いです。なので、必要とするJSライブラリ別にカテゴライズして並べています。

ライブラリ不要

Highslide JS

Highslide JS

必要ライブラリ:
対応形式:
画像、画像ギャラリー、インライン、インラインフレーム、Ajax、Flash
対象指定:
class属性&onclick属性
ライセンス:
CCライセンス 表示-非営利 2.5 一般
非商用:無償、商用-1サイト:29ドル、商用-無制限:179ドル
備考:
表示コンテンツのドラッグ移動が可能。Inline/Ajax/Iframe表示時には右端ドラッグでサイズ変更が可能。

iBox

iBox

必要ライブラリ:
対応形式:
画像、Ajax、インライン、Youtube動画などのムービー
対象指定:
rel属性
ライセンス:
無償
備考:
アニメーションがなく軽量。使い勝手はThickboxみたいな感じ。

jQuery

FancyBox →オススメ

FancyBox

必要ライブラリ:
jQuery、jQuery Easing(オプション)
対応形式:
画像、画像ギャラリー、インライン(Flashなど含む)、Ajax、Iframe
対象指定:
jQueryセレクタ、rel属性(画像ギャラリー指定時)、class属性(インラインフレーム指定時)
ライセンス:
MITライセンス
備考:
jQuery Easingプラグインなしだと、ズームアニメーション時のイージング効果がなしになる。

FancyZoom(jQuery)

必要ライブラリ:
jQuery
対応形式:
画像、インライン(Flashなど含む)
対象指定:
jQueryセレクタ
ライセンス:
不明
備考:
ウィンドウのシャドウ部分などの透過pngパーツが、IE6では透過gifで代用されており見た目が落ちる。

jQuery lightBox plugin →オススメ

jQuery lightBox plugin

必要ライブラリ:
jQuery
対応形式:
画像、画像ギャラリー
対象指定:
jQueryセレクタ
ライセンス:
ライセンス 表示-継承 2.5 ブラジル
備考:
UIは本家Lightboxと全く同じ。裏の仕組みがjQueryで動くようにアレンジされたLightbox。jQueryセレクタの指定に複数要素が該当する場合、自動的に画像ギャラリーモードになる模様。本家Lightboxとは違って色々なパラメーターがあり、カスタマイズができる

prettyPhoto

prettyPhoto a jQuery lightbox clone

必要ライブラリ:
jQuery
対応形式:
画像、画像ギャラリー、Flash、RealMedia、インラインフレーム
対象指定:
rel属性、href属性のパラメータ、jQueryセレクタ、オプションでtitle属性
ライセンス:
CCライセンス 2.5
備考:
対応形式のメディアそれぞれを混合したギャラリー表示が可能。デフォルトのスタイルは黒いUI。

ThickBox

Thickbox

必要ライブラリ:
jQuery
対応形式:
画像、画像ギャラリー、インライン、インラインフレーム、Ajax
対象指定:
class属性、title属性、href属性のパラメータ
ライセンス:
MITライセンス
備考:
jQueryを使うLighbox系スクリプトの代表的な存在。多様な形式に対応。アニメーションはしないが、その分軽快に動作する。

Prototype.js + script.aculo.us

FancyZoom

必要ライブラリ:
Prototype.js、script.aculo.us
対応形式:
インライン
対象指定:
href属性
ライセンス:
不明
備考:
対象のインラインコンテンツのIDをhref属性で指定できる・・・だけ?ウィンドウのシャドウ部分などの透過pngパーツが、IE6では透過gifで代用されており見た目が落ちる。

Lightbox2

Lightbox2

必要ライブラリ:
Prototype.js、script.aculo.us
対応形式:
画像、画像ギャラリー
対象指定:
rel属性、title属性
ライセンス:
CCライセンス 表示 2.5 一般
備考:
元祖Lightbox。オプション設定などはないが、使い方は単純明快。

Lightview

Lightview

必要ライブラリ:
Prototype.js、script.aculo.us
対応形式:
画像、画像ギャラリー、Flash、QuickTime、Ajax、インラインフレーム、インライン
対象指定:
class属性、title属性、rel属性
ライセンス:
非商用の場合、CCライセンス:表示-非営利-改変禁止 3.0 Unported
商用の場合、CCライセンス:表示-改変禁止 3.0
有償。非商用(1ドメイン):3$、非商用(制限なし):$15、商用(1ドメイン):49$、商用(制限なし):$295
備考:
多機能Lightbox。有償だけあってクオリティは高いが、たくさんのオプションがあるので使いこなすのは骨が折れるかも。

ModalBox

ModalBox

必要ライブラリ:
Prototype.js、script.aculo.us
対応形式:
プレーンHTML(文字列)、DOMノード
対象指定:
href属性、title属性、onclick属性
ライセンス:
不明
備考:
画面上部からニョロっとオーバーレイボックスが出現する。導入説明が親切。

MooTools

ImageZoom

ImageZoom

必要ライブラリ:
MooTools
対応形式:
画像、画像ギャラリー
対象指定:
rel属性、オプションでtitle属性
ライセンス:
CCライセンス 表示-継承 3.0 Unported
備考:
対応形式が画像のみと少ないが、ズームするアニメーションがお洒落。拡大状態を複数開けるのが個性的。

phatfusion →オススメ

phatfusion

必要ライブラリ:
MooTools v1.11
対応形式:
画像、Flash、QuickTime、WindowsMedia、RealMedia、MP3、Ajax、インラインフレーム、インラインHTML
対象指定:
スクリプト、rel属性、title属性
ライセンス:
MITライセンス
備考:
対応形式のメディアそれぞれを混合したギャラリー表示が可能。デフォルトのスタイルだとUIが黒い。この辺はスクリプト呼び出し時のオプションでカスタマイズ可能。多機能でクオリティ高し。

SexyLightBox

SexyLightbox

必要ライブラリ:
MooTools
対応形式:
画像、画像ギャラリー
対象指定:
スクリプト、class属性、rel属性、title属性
ライセンス:
MITライセンス
備考:
画面上部からビヨーンと登場するという、変わったアニメーションが特徴的。公式サイトの字が読めない。.meというドメインがモンテネグロのドメインなので、モンテネグロのサイト・・・?

Slimbox

Slimbox

必要ライブラリ:
MooTools 1.2
対応形式:
対象指定:
スクリプト、rel属性、title属性
ライセンス:
MITライセンス
備考:
UIはLightboxと全く同じ。Slimというだけあってライトウェイトであることがウリらしい。スクリプトの書き方次第でダイレクトにSlimboxを呼び出すことも可能。jQueryバージョンもあるとのこと(http://www.digitalia.be/software/slimbox2)。

Smoothbox, Thickbox for mootools

Smoothbox, Thickbox for MooTools

必要ライブラリ:
MooTools
対応形式:
画像、画像ギャラリー、インライン、インラインフレーム、Ajax
対象指定:
class属性、title属性、href属性のパラメータ
ライセンス:
MITライセンス
備考:
ThickboxのMooTool版。UIはThickBoxと同じ。IE8には非対応だった(オーバーレイ部分が真っ黒に)。

YUI

YUI Based Lightbox

YUI Lightbox

必要ライブラリ:
YUI
対応形式:
画像、画像ギャラリー
対象指定:
スクリプト、id属性(img要素)
ライセンス:
CCライセンス 表示-継承 3.0 Unported
備考:
コントロールパネルを表示されたり、画像のキャプションを入れるボックスを表示されたり。画像表示領域と、キャプションはドラッグで移動可能。

その他

Shadowbox.js

Shadowbox.js

必要ライブラリ:
基本的には不要(※)
※JSライブラリ(Adapter)を先に読み込ませると自動的に感知して、それと一緒に動作するようになる(らしい)。Adapterとして使えるのは Prototype、jQuery、MooTools (requires 1.2 Core)、Dojo Toolkit、Yahoo! User Interface Library (requires yahoo-dom-event.js)、Ext (requires ext-core.js)。
対応形式:
画像、画像ギャラリー、イメージマップ、Flash、各種ムービー、インラインフレーム、インライン、フォーム等
対象指定:
スクリプト、rel属性、title属性
ライセンス:
Shadowbox.js License
→有償。Single Developer: $20.00、Multiple Developer: $50.00
備考:
超高機能。アニメーションは控えめで上質感を感じさせる。スクリプト呼び出し時に引数として様々なオプションを指定可能。

Continue reading

LineNumberWriterを0.4にバージョンアップ

1年以上放置していたので今更感がありますが、LineNumberWriterをバージョンアップしました。今回から、変換対象のコード内でタグが使えるようになります。例えば以下のように、strong要素やspan要素などを挿入することが可能です。(※)

<strong>で囲んでみました
<span style="color:#F33;">で囲んでみました

※もちろん(X)HTMLの文法的にcode要素に含められない内容(ブロック要素など)は含めちゃダメですよ。

今度Sugamo.cssという企画に参加することにしたので、何かプライベートワークをやっていかなきゃいけないような気になり、とりあえずコレのバージョンアップでも、と着手しました。
実は前からタグが使えるようにしたくてちょくちょくいじってはいたんですが、IEの独自仕様でまた色々とつまづいてしまい、挫折しちゃってたんです。でもまぁ最近、良いアイデアが浮かんだのでどうにか形になった、という。

ダウンロードしようなどという奇特な方はこちらからどうぞ。