@kyanny's blog

My thoughts, my life. Views/opinions are my own.

Backbone.js のソースコードを読んだ

Backbone.js (annotated source code)ソースコードを読んだ。単なる趣味です。全体的に読みやすくて読んでて楽しかった。他人が読んで役に立つものでもないので、そういうのをお探しの方は Backbone.js Advent Calendar 2011 あたりをどうぞ。以下、読んで書いたブログ記事へのリンク集です。

Backbone.js Helpers 他

Backbone.js の Helpers とか、その他の細かい部分のコードを読んだ。ざっと眺めただけ。

http://documentcloud.github.com/backbone/docs/backbone.html

  • Initial Setup では、すでに Backbone がロードされてたら初期化を二重にやらないようにしていたり、 node.js など CommonJS の仕様に準拠した実行環境のときは exports まわりの仕様にあわせるなど、そんなことをやってる。あと underscore.js をロードしたり。 jQuery or Zepto の $ をとってきたり。
  • var new_backbone = Backbone.noConflict(); みたいにして、 window.Backbone はもともとロードされてたほうの Backbone オブジェクトに保ったまま新しくロードしたほうも使う、とかできる。
  • extend, inherits, ctor あたりはプロトタイプ継承ツリーを作ってる・・・んだと思うけど ctor の存在意義がいまいちよくわからない。constructorとprototype.constructorがわからなくなった - 宇宙野武士は元気にしているかを読んだら余計にわけがわからなくなった。 JavaScript りょく、というかプロトタイプベースのオブジェクト思考の理解が足りない。
  • あとは本当にユーティリティ関数。
  • 全体を (function(){ ... }).call(this); で囲って、無名関数を即時実行するイディオムなのはわかるんだけど、 call を使うのはなんでなんだっけ・・・どこかで (function(){})() との違いとかを比べてるブログ記事を見た覚えがあるけど忘れた。

Backbone.Hogehoge みたいなのにきれいにまとめられていて、あぶれた部分はとても少なかった。全体的に、整理されているし、名前の付け方も Ruby とか Rails っぽくておれには馴染みがあるし、コードもあんまり奇抜なところはなくて (while とかで破壊的に何かするやつは意味不明だったけど) 読みやすいコードだった。 jQuery とか、いまよりもっと勉強不足だった頃に眺めて最初のほうですでにつまづいてしまったけど、これはそれよりも読みやすくて、 JavaScript ライブラリのちょっとしたものを読むとっかかりとしてはいい教材だったと思う。これを読んで自信をつけてから大物に挑むのがいいなと思った。

Backbone.sync

Backbone.sync を読んだ。ざっと眺めただけ。これだけ小文字 (Sync ではなく sync) なのがなんか気になる。と思ったらこれは new するためのクラス(関数)じゃなくて単なる関数だからだった。

http://documentcloud.github.com/backbone/docs/backbone.html

  • リモートサーバの API を呼び出して、 Backbone.Model なインスタンスのデータを取得したり更新したりする永続化ストレージと通信するためのクラス。デフォルトでは Rails の resources ルーティングのルールを想定した作りになっていて、違う感じの API をバックエンドにする場合はこのクラスを上書きしろ、という感じ。
  • ちょっとした変更は全部書き換えなくていいように専用の変更用インターフェースがある。 emulateHTTP とか。
  • デフォルトではリクエストは JSON でおくる。 emulateJSON が有効だとフォーム送信の形式を使う。
  • 送信用パラメータとかリクエストヘッダとかを調整した上で、最後は $.ajax を呼び出して丸投げしている。なので、 jQuey なりそういうライブラリが別途必要。このへんは割り切ってるなーと思う。

要するに、単なるパラメータ調整用のラッパー関数なので、短いしやってることもすぐわかる感じだった。 xhr.setRequestHeader('X-HTTP-Method-Override', type); とかは、まぁこれ使えば覚えておく必要はないんだけど、これを使わないで自前でこういう風にリクエストをおくるときに、こういうリクエストヘッダの指定漏れとかでうまく動かなくてデバッグに苦労するとかよくあるので、見たことがあるだけでも記憶の底にひっかかっていて解決の糸口になったりするので、かけ捨ての保険みたいな意味で読んでおいてよかったと思う。

Backbone.View

Backbone.View を読んだ。ざっと眺めただけ。

http://documentcloud.github.com/backbone/docs/backbone.html

  • model: とか collection: とか tagName: とかは viewOptions というのに定義されてる
  • selectorDelegate というやつで、 jQuery とかの selector の検索範囲を特定の DOM 要素以下に限定するようなのを使っていて、 this.$ にそれで得られたオブジェクトをバインドしている
  • initialize, render は最初は空っぽな感じで、上書きして使うことが想定されている
  • make というのがあって、動的に DOM 要素を作って返す。テンプレートが必要ないような小さい要素の場合に使う、みたいなメモがあるけどどう嬉しいのかよくわからず
  • delegateEvents は Backbone.View 独特の "click .button" : 'save' みたいなイベントハンドラの宣言を受け取って、実際にイベントハンドラをバインドする。 new Backbone.View() したときに実行される。 for in でループして一個ずつやっている。 underscore.js の _.bind と jQuery などの $.bind が入り交じってて、 underscore.js のほうの理解があいまいなのでぼんやりしてしまう。 this を固定するためのものだっけ _.bind は。
  • _configure で viewOptions が Backbone.View のインスタンスのプロパティに生える。
  • _ensureElement で el: の値を初期化する。 Backbone.View のインスタンスが、実際どの DOM 要素と対応してるのかを示すプロパティ。自前で指定すればそれを使い、そうでなければ tagName とかから自動生成。これも new Backbone.View() したときに実行される。

delegateEvent で this.el に対してイベントハンドラをセットするあたりがこのクラスのキモで、あとは render() のインターフェースを揃えて、 DOM 操作がカオスにならないようにする、くらいの意義なのかな。薄い印象を受けた。眺めただけなので細かいコーディングレベルまで踏み込まずぼんやりしてるため、この行のテクニックが!みたいな感想が書けなくて薄いのはこのエントリのほうだ。

Backbone.History

Backbone.History を読んだ。ざっと眺めただけ。

http://documentcloud.github.com/backbone/docs/backbone.html

  • history.pushState or URL のハッシュ部分 (# 以下) のどっちかをよしなに使ってクロスブラウザで履歴管理を楽にやるためのラッパークラス
  • handlers は Backbone.Router を読んだときに出てきた、「URL がルーティングにマッチしたら呼び出されるコールバック関数」を入れておいたりするためのもの
  • getFragment のなかで、 pushState 利用モードのときは URL をいろいろ調整したうえで fragment にあたる部分を取り出す。 pushState じゃない場合は location.hash をみるだけ。
  • start は多重呼び出しされないようになってる。あと History API が利用できるかどうかなどもここでチェック。そして古い IE の場合は iframe を使うので this.iframe を初期化して navigate を呼ぶ。
  • pushState がある場合は popState もあるので checkUrl を popstate にバインド。 onhashchange があれば checkUrl をバインド。どっちもなければ setInterval で監視。
  • checkUrl で URL の変化を監視して変化してれば loadUrl を呼び出す
  • loadUrl のなかでルーティングにマッチするか調べて、コールバックを実行する
  • navigate のなかで変化後の URL を履歴に保存する。 pushState があればそれを使い、なければ iframe の location に保存する。

このクラスはけっこう動作のキモになるところっぽいけど、具体的に自分でコードを書いて使ってみないとピンとこない。あと、まず自分で pushState なり hash fragment なりを使って URL 遷移を管理するコードを書いてみて、その大変さとか面倒くささとかを体感しないとありがたみが薄くて感動が少ないのかもなーと思った。あと、本当はそれなりに面倒くさいJavaScriptとhistoryとAjaxのお話 - 愛と勇気と缶ビールなどを読むと History API まわりをクロスブラウザでちゃんと扱うのはなかなか難しそうで、 hist.js のソースを眺めて見比べると Backbone.History でちゃんともろもろカバーできてるのか?という疑問が。いちおう、適当な URL をブックマークしておいたり直接アクセスしてきても正しい感じに動作するように書かれてる気はするけど・・・。もしダメだった場合に hist.js なり代替のもっと高機能な何かと置き換えられるか?という点については、 Backbone.History はその他のクラスと全然依存してないので、問題なさそう。