今のイベントループでのスタート地点。呼ばれないので、ここには到達しない。呼んで、「次のジョブ」を登録した(今更)。今のイベントループの終わり。呼ぶ処理を「次のジョブを登録する処理」よりも後(=次のイベントループ)に実行する必要がある。次のイベントループで、予約されたこの処理が始まる。登録済みの「次のジョブ」を連鎖的に呼ぶ。次のイベントループで実行するように予約。今のイベントループでのスタート地点。「次のジョブ」として、この処理が始まる。呼んで、次のジョブを登録した。今のイベントループの終わり。......という順番で処理が進むから、期待通りに動くようになる。どの処理が一体何をしているのか、どの関数が何を返しているのか、自分が呼んでいるメソッドは誰のメソッドなのか、といった事をちゃんと理解しておくと、この話がすっと頭に入ってくると思うんだけど、ただの構文として覚えてしまっていると、メールの送り主の人みたいに「何で動かないの......」って詰まってしまうんだと思う。呼ぶ」という所までを「構文」として暗記していたから、結果的にはちゃんと動いてくれてたという状態だったんだと思う。その後若手IT勉強会の中でコードリーディングをやってだんだん仕組みが分かってきて、「ああなるほど、こういうことだったんだ」と理解できるようになった。コールバック関数という形で後から登録できるようにする」ものだ、と僕は認識してる。「後から登録する」という都合上、この性質は非同期処理(関数を抜けた時点でまだ処理が始まっていないという種類の処理)と非常に相性がいい。非同期処理じゃないと使えないわけではない。使わざるを得なくて、それで初見の人に分かりにくくなってしまう。そこがもどかしい所だ。4から(だったっけ?)の新しい機能に基づいたアニメーション管理のための仕組みが期待した通りに動かなくなってて、あれこれ試してるうちにどうもmozRequestAnimationFrame()の使い方のうちDOMイベントベースでやる方のが動かなくなっててコールバック関数を使う方法でならうごくっぽいという事が分かったので、ツリー型タブ0.12.2011120101からはそのように変更した。requestAnimationFrameでの変更による物で、要するに「DOMイベントベースの方法は正式な仕様になりそうにないし誰も使ってないし、もう廃止してよくね?」ってことで、Geckoでしか使えなかったDOMイベントベースの方法は廃止されて、WebKit等でも利用できるのと同じ形式のAPIに統一されたんだそうだ。その当日にMDNのドキュメントを調べてて、その廃止された方の使い方が解説されていて、MDNにも書いてある使い方なのにおっかしーなーと首をひねってたんだけど、単にドキュメントの更新が間に合っていなかっただけだった。現実的には妥当な決定だと思うけど、なんか釈然としない。2011JSDeferredは非同期処理の制御に特化しててサイズも小さくて素敵な軽量ライブラリだ!と僕は思ってるんだけど、世の中を見回してみると「軽量ライブラリ」って言われてる物は3KB未満とかそういうのが結構あるようだし、jQueryも1.5.1のminified版は28KBって書いてあるし、そうなるとjsdeferred.jsのコメント付き版が0.3.4で19KBというのは「軽量」と呼ぶにはひょっとしてちょっとでかいのかな......という気がしてきた。MinifierとかPackerとかそういう風なやつでどれくらい小さくなるのか実験してみた。Compresser→6.33KB/packer/→4.31KB/packer/はそのままだと構文エラーで動かなくなってしまった。置換)したら一応エラーは出なくなった。/packer/の構文解析が貧弱なせいっぽいので、JSDeferredの側で問題になる所にあらかじめセミコロンを入れておけば、この問題は無くなるっぽい気がする。......と書いたからか、ちょよんごさんが対応してくれた。Compilerにかけてみた所確かに小さくなったのですが、シンボル類まで全部失われちゃってこれ単体だと他のスクリプトと組み合わせられないのが残念ですね。なったのでJavaScriptで解決した。つまみ食うのアルゴリズムを丸パクリした。阿呆ですんません......なんでそこで詰まったかっていうと、単純な26進数の変換ではうまくいかなかったからなんですよね。A==0、B==1という感じで26進数として扱うと、26==Zで27==BAになるんだけど、実際は繰り上がった後はBAじゃなくてAAにならなきゃいけない。そこの所を考慮した計算を多分簡単なループで実現できるとは思ったんだけど、頭が働かなくて無理だった。Toolkitの導入を考えてる。ライブラリとして抜き出したコードを公開しておきたいけど、ライブラリ自体の使い方を書いたページを準備するのが面倒だし、多分誰も見てくれなさそう。ソースコードの中にコメントとして使い方を埋め込んでおくと、見るべきファイルが1つで済むから良さそうだけど、どんな書式で書くと分かってもらいやすいかが問題だ。ツリー型タブのAPI紹介等ではXPIDLっぽい書き方にしてみてるけど、オレオレ表記なので分かってもらえない可能性があるという心配はずっとしている。ところで、Firefox自体のソースを見ていると以下のような書き方をしているのをよく見かける。{(略)これはJavaで標準的に使われているJavadocという「ソースコードの中に埋め込まれたコメントを自動的に収集してHTML形式でドキュメントを生成する」仕組みに基づいたもので、同じ形式でコメントを埋め込めるよう、Javadocの仕様に準拠した実装が言語ごとに存在しているようだ。Toolkit、C言語ならGTK-Docが一般的らしい。Compilerも対応しているらしい事実上の標準としてみんなが見慣れた形式なのであるならば、これに合わせて書くのがいいだろう。思ったので、とりあえずライブラリとして切り出して公開しているコードにJavadoc形式で使い方の解説を埋め込んでみることにした。Toolkitの使い方Javaをインストールする。Toolkitをダウンロードして展開しておく。Toolkitのjsrun.jarとかと同じ位置に置く。Commentを書く。@IT等を見ると例がある。jsdoc.batの起動オプションにJavaScriptのファイルを渡して起動する。jsdoc.batと同じ位置にjsdocという名前のフォルダができて、その中に生成されたHTMLファイルがあるので、index.htmlをブラウザで開いて眺めてニヨニヨする。Toolkitに渡してみてもドキュメントを出力してくれなかった。UxU用のテストケースまで走査するため、フォルダごと渡してまとめてドキュメントを生成させてみると、ファイル一覧の中にテストケースのファイルまで出てきてしまう。@exampleに例を書く時に、例の中に<とか>とかのHTML的にまずい文字が含まれていると、出力されるHTMLがぶっ壊れてしまう。Toolkitはそれ自体がJavaScriptで書かれている。Java上で動作するJavaScript実行環境のRhinoの上で動作している。)、ファイルの拡張子でフィルタリングを行っているらしいということが分かった。あと、テンプレートのファイルの方を編集すれば、例に埋め込んだコードのせいでHTMLがぶっ壊れてしまう問題は回避できるようだった。いうわけで当面の所はこんな変更を加えて使ってみることにした。以下はjsdoc_toolkit-2.3.2.zipに対する差分です。2010モック(Mock)とスタブ(Stub)の違いがよく分かってなかったんだけど、何が違うのか、そしてモックはどう使う物なのかということを、すとうさんに教えてもらって今更理解した。あとで会社のブログに書くつもりだけど、メモとして要点だけまとめておく。テストは基本的に、粒度の細かいブラックボックステストにした方がいい。内部に持ってる隠しプロパティの値が正しいかどうか?という風な実装べったりのテストは、実装の変更に非常に弱い。関数の返り値だけ見て検証できるような設計が、自動テストしやすい設計という意味で「良い設計」と言える。ブラックボックステストには限界がある。するだけではテストできない機能もある。いうブラックボックステストを行えるような設計に直すべき。処理待ちしてやりさえすればいいような場合、処理待ちのための機能を持ったテスティングフレームワークを使うと、単純な非同期処理なら簡単にブラックボックステスト化できる。色々な副作用を伴う機能や、状態遷移があるような機能をブラックボックステストできるようにしようと思うと、「内部で状態の遷移のログを取っておいて、最後にそのログの内容が期待通りになっているかどうかを検証する」という風な形にならざるを得ない。いうことをやろうと思うと、本番用のコードの中に「ログ取り用の処理」のような「実際に使う場面では無駄」なコードが増えていってしまう。テストしやすくするためにテスト対象の実装に手を入れるのはよくあることだし、そうやって手を入れた結果として関数が小さな単位に分割されていったり関数名と入出力の対応が分かりやすくなっていったりするのなら、それによって動作が安定するようになったりコードのメンテナンス性が高くなったりするから、いいことだ。「テストのためだけの実装」が増えていって、動作が不安定になったりコードのメンテナンス性が落ちたりするのでは本末転倒だ。ブラックボックステストにし続けるためのコストが、本来の実装に悪影響を及ぼすようなレベルになってしまったら、そろそろホワイトボックステストに移行していい頃合いだ。検証対象の機能の粒度が大きくなってくると、これはもう避けられない事と考えた方がいい。自分は今まで、とりあえずユニットテストに注力していて、ある意味脅迫観念的な勢いで、ブラックボックス度合いを高くする事を心がけてた。今まではそれでだいたい問題なかった。最近になって、ブラックボックステストにしようとすると無理があるというケースにぶち当たるようになった。1つの機能の中でコロコロと遷移する内部状態を、どうにかして検証したいというようなケースが出てきた。それですとうさんに相談したら、そういう時はモックを使えばいいと言われた。話を聞く限りだとモックというのはテスト対象の実装の中の処理の流れを追う物のようなので、それじゃブラックボックステストにならないじゃないかと思った。それをそのまま言ったら、確かにテストはできるだけブラックボックステストになってた方がいいけど、機能テストやインテグレーションテストのような粒度の大きな単位のテストでは、処理の中で起こる様々な出来事や副作用を色々モニタリングして、すべての処理が期待通りに動いているかどうかを検証しないといけないから、必然的にホワイトボックステストにならざるを得ないと言われた。それを聞いて、目の覚めるような思いをした。ブラックボックステストとホワイトボックステストの使い分けはそこが基準になるのか、と。今まで自分がホワイトボックステストを書かずに済んでいたのは、状態の遷移を伴うような機能を作る必要がなかったからだったんだ、テストをどうも書きにくいなあと思っていた機能は本当はホワイトボックステストにしたほうがいい物だったんだ、と。そんなわけでUxUにモックの機能を実装した。Mock」で検索するとJSMockとjqmockが上位に出てきたので、最初はそれらを参考にするように(メジャーな実装があるんだったらそれをそのまま取り入れるなりAPIを合わせるようにするのが望ましい)と言われたんだけど、ドットで繋げるメソッドチェインの記法がガンガン出てきて頭パンクした。下の方までスクロールするとMockObject.jsというのが出てきて、こっちはファイル全体で3KBに満たない小さなライブラリなので、まずはここから始めることにした。コードが短いから、読むのもそんなに苦にはならない。モックの概念を言葉で説明されてもさっぱりだったけど、一通りの処理の流れを見たら、「モックというのは一体何をやらなきゃいけないのか」「どういう振る舞いが期待されているのか」ということがよく分かった。MockObject.jsと同等の機能を一通り実装した後でもう一度JSMockの方を見たら、なるほどこれはこういう意味だったのかというのがやっと分かった。サンプルコードを見ても、モックという物の意味をそもそもよく知らない時点では、どこからどこまでがJSMockの部分なのかさっぱり分からなかったんだよね。いうことで、MockObject.jsに加えてJSMock互換のAPIも付け加えてみた。jqunitの方は......もう別言語だからシラネ。2010先日のブラウザー勉強会で吾郷さんにお会いした際に、JavaScriptには言語の仕様として「例外がどこから投げられたのか」を知る為の仕組みが無いので独自のフレームワークを作る時に困ったという話を伺った。オレ標準JavaScript勉強会で何話せばいいか困ってた所だったので、それをネタに発表させてもらう事にした。Editionでは、例外オブジェクトの機能としてはError.prototype.nameとError.prototype.messageしか定義されていなかった(あとはconstructorとかtoString()とかその程度)。1.5の頃はMozillaもError.prototype.stackはサポートしてなかった。それ以降にMozillaが独自に拡張した箇所ということのようだ。今回調べた限りではOperaもChromeもError.prototype.stackをサポートしていた(Operaの場合はこれに加えて、少しフォーマットが違うスタックトレースをError.prototype.stacktraceでも取得できる、ということをedvakfさんに教えていただいた)。メジャーなJavaScript実行環境でこれに対応してないのはIE(IE9PP3を含む)くらいのようだ......と思ったらSafari(Windows版)もサポートしていなかった。吾郷さんのそれやUxUのようにデバッグを支援するためのフレームワークでは、スタックトレースは欠かせない要素だ。将来のECMAScriptの仕様に取り込まれてくれればいいのになあ、と思う。勉強会ではせがわようすけさんに突っ込まれたけど、セキュリティのためにはなるべくこういう情報は出さない方がいいものらしい。自分はスタックトレースの何がまずいのかがよく分かっていない。会場では「例えばスタックトレースでスクリプトのURLの中にセッションIDが含まれていたらセッションハイジャックされてしまう危険性がある」という例を教えていただいたけれども、それでもまだピンと来ていなかった。ツッコミを受けたことで「なるほど、クロスドメインの制約を突破されてしまう」という事が問題なのだと分かった。いう疑問はまだ残っている。分かりやすい話として、通常のXMLHttpRequestやiframeでは、別ドメインのドキュメントを読み込む事ができない・読み込んだとしてもその内容にスクリプトからアクセスすることはできない。<http://www.google.co.jp>.と言われてエラーになる。'false');document.body.appendChild(script);という風にして、別ドメインのドキュメントの内容を部分的にとはいえ読み取れてしまう可能性がある、というわけだ。エラーメッセージのスクリプトファイル名からのクロスサイトデータ漏えいで実際に脆弱性になっていたことが確認されていて、既に修正されている。実際に現行バージョンのFirefox・Opera・Chromeで試してみたところ、1番目・2番目のtry-catchを使った例ではそもそも例外を例外として(そもそも、エラーが発生したのかどうかすら)捕捉できなかったので、今の所問題にはならない。今は問題にならなくても、今後登場するJavaScriptの実装ではこういう場合でも情報を取れるようになっている可能性はあるので、将来的に脆弱性の原因になるかもしれないという警告は確かにアリだとは思う。いう所までは何とか理解できた。ブラウザのレベルでは、クロスドメインやクロスオリジンになる時だけは例外を出さないとか詳細な情報を出さないとか、そういう対応の仕方はあるだろうけれども、「ECMAScript」の仕様でそこに言及するのは変だろうなあ。いうややこしい事情を勘案すると、もう丸ごと全部仕様からドロップしてしまえという判断になるんかなあ。hogehogeを見て、10分でコーディング|プログラミングに自信があるやつこい!!に挑戦してみました。コーディング(と検証)だけで6分くらいかかりました。エレガントでもなんでもありません。"123123123"));クラス名がどうとか書いてあったから馬鹿正直にそれに従ってしまったし。いかん、人数よりカード枚数が少ないときの考慮が足りてなかった。2009前書きPHPとかerbのようなテンプレートをJavaScriptで。いう話に書いたやつの続き。大切な事なので3回言います。%>オーケー?こういう文字列をテンプレートとして解釈したい場面は多分よくあると思う。この例だと、todayという変数をどっか外部から与えて値を埋め込むことになる。この変数をどうやって渡したらええねん、と。解1:グローバル変数を使うグローバル変数をがんがんに使って構わないのであれば、先にグローバル変数としてtodayを定義しておけばいい。eval()で実行されるコードの中からも普通に参照できる。アドオンのコードのように、個々のグローバル関数やグローバル変数の寿命が長い事が予想されるスクリプトでは、この手は使えない。解2:テンプレート内に書くコード片にthis.を付けるparseTemplate()の仕様としてはあくまで「書かれたコード片は第2引数で渡されたオブジェクトをthisとして実行されます」という風にしておいて、テンプレート風に書かれている方の文字列内のJavaScriptコード片では必ずthis.todayという風に書くようにする、という方法もある。いちいちthis.を付けるのは面倒だし、そもそも僕がこのような記法を知ったきっかけであるERBでは、self.なんて書く必要がなかった。同じような物は同じように使えた方がいい。この方法も使いたくない。解3:varで宣言し直す先のエントリに当初書いていたコードでは、parseTemplate()の第2引数で渡したオブジェクト(ハッシュ)のプロパティを走査してそれをvarで変数として宣言し直す、ということをしてた。外の名前空間をなるべく汚さないための苦肉の策だ。これだとエラーになりそうな場合がけっこう考えられる。いうエラーの元になる名前のプロパティを除外して、安全な物だけをvarで宣言する、ということをやりたかった。言い換えると、そのプロパティの名前を構成している文字列がJavaScriptの識別子として妥当かどうかを判別したかった。いうことでやっと、このエントリの本題に入る。