Kanasan.JS Prototype.js CodeReading #62008年10月16日 21時42分

Kanasan.JS Prototype.js CodeReading #6 に行ってきました (当日のチャットログ参加者のブログ記事一覧)。今回は Prototype.js 1.6.0.2 の 3375 行目、フォームに関する部分から最後まで読みきりました。

Form.Methods.findFirstElement (3449 行目)

フォームコントロールを取得するメソッドですが、tabindex 属性でタブ移動順が指定されていた場合は、その順序がもっとも早いコントロールを返そうとしているようです。しかしその際、tabindex 属性の値が 0 のものを値が 1 のものより優先しているようで、実際のタブ移動順と得られる結果が一致しません。HTML 4 では tabindex 属性の値 0 は、tabindex 属性を指定しないのと同じ効果を持つはずなのですが。

フォーカスと選択範囲

多くのブラウザでは、フォームコントロールのフォーカスと選択範囲は別個に管理されています。ですので、フォーカスのないコントロールの選択範囲を変更することも可能です。もっとも、フォーカスがなければ選択範囲を変更してもそれを視認できないかもしれませんが。

Form.Element.Serializers (3569 行目)

シリアライザという名前からフォームコントロールの値を取得する関数群かと思いきや、その名に反してゲッタとしての役割も負っています。各関数は引数 element と value をとり、value が省略されていればゲッタ、指定されていればセッタとして振舞うようです。

Form.Element.Serializers.select (3590 行目)

select 要素には type プロパティがあり、その値は multiple 属性が指定されていれば "select-multiple"、指定されていなければ "select-one" となります。Prototype.js ではこのプロパティを見て、ゲッタの処理を selectOne メソッドと selectMany メソッドに振り分けています。

Event.cache (3733 行目)

通常、ある要素に登録されているイベントリスナを取得することはできません。Prototype.js では addEventListener メソッドなどでリスナを要素に登録する際、cache オブジェクトにもそのリスナを格納しておくことで、要素からリスナを取得できるようにしています。cache オブジェクトの構造は次のようになっています。

Event.cache = {
  "ある要素のイベント ID": {
    "click": [
      click イベントに登録されたあるリスナのラッパ関数,
      click イベントに登録された別のリスナのラッパ関数,
      ...
    ],
    "イベント名": [
      そのイベントに登録されたリスナのラッパ関数,
      ...
    ],
    ...
  },
  "別の要素のイベント ID": { ... },
  ...
};

ここで、要素のイベント ID とは Prototype.js が要素ごとに生成する ID です。また、ユーザーから渡されたリスナをそのまま登録するのではなく、イベントオブジェクトの動作を拡張するラッパ関数を作成して (3866 行目、Event.createWrapper)、そのラッパ関数を登録しています。

Event.Methods.pointer (3787 行目)、Event.Methods.stop (3799 行目)

pointer メソッドはマウスポインタの位置を取得するメソッドで、IE 以外では pageX / pageY プロパティの値をそのまま使っています。IEでは文書の表示モードが標準モードでも互換モードでもうまくいくよう、document.documentElement と document.body のプロパティを併用しています。stop メソッドはイベントの伝播をとめるとともに、そのイベントのデフォルトアクションをキャンセルします。

イベントの伝播やデフォルトアクション、文書の座標系については WEB+DB PRESS Vol. 46 の連載「JavaScript + ブラウザ探検」第 3 回、「イベントの攻略……DOM Events、IE イベントモデル、ボックスモデル」でも解説されているので、ぜひ購入しましょう。

destroyCache (3896 行目)

前述の cache オブジェクトに格納したラッパ関数を削除します。IE 6 のメモリリーク対策に使われているようです。しかし、これは cache オブジェクトからラッパ関数への参照を切るだけで、リスナの登録解除を行っていません。リスナが登録する要素への参照を含んでいた場合、リスナからその要素へ、その要素からラッパ関数へ、ラッパ関数からリスナへの参照は残ったままとなり、循環参照によるメモリリークを防げない気がします。

Event.fire (3954 行目)

クリックやキー入力といったユーザーの操作によらず、スクリプト自身からイベントを発生させるためのメソッドです。IE 以外のブラウザでは任意の名前のイベントを発生させられるのですが、IE はサポートするイベント以外の名前を受け付けないので、IE の独自拡張イベントであり、Web 上でほとんど使用されていないと思われる dataavailable イベントを代わりに使っています。

ページ構築時のイベント (3997 行目)

画像などの読み込みが完了していなくても、文書自体の読み込みが完了すれば Prototype.js 独自の dom:loaded イベントが発生するようになっています。Firefox などでサポートされている DOMContentLoaded イベントと同様のものです。また、このイベントが発生した後には document.loaded プロパティの値が true になります。

document.observe('dom:loaded', function (event) {
  alert('文書の構築が完了しました。');
});

非推奨の機能 (4034 行目)

4034 行目以下に書かれている機能は非推奨のものです。Prototype.js の API ドキュメントを見れば非推奨の機能にはその旨が記されています。

Prototype.js 1.6.0.2 から 1.6.0.3 への変更点

余った時間でつい先日リリースされた Prototype.js 1.6.0.3 の変更点を見ていきました。ついでに Prototype.js のチェンジログや Emacs の diff-mode の存在も教えてもらいました。

__proto__ プロパティへのアクセス

従来 obj.__proto__ としてアクセスしていた部分が obj['__proto__'] に書き換えられました。Caja という JavaScript のサブセットを用いた環境では、アンダースコアで終わるメンバ名はプライベートメンバのものと定められており、それとの互換性を取るためだそうです。

iterator 関数の this の束縛

以前から each メソッドなどの第 2 引数に this となる値を渡すことはできましたが、1.6.0.2 ではそれを使わずあらかじめ bind メソッドで this の値を指定していました。

// Prototype.js 1.6.0.2
iterator = iterator ? iterator.bind(context) : Prototype.K;
iterator(value, index);

しかし、1.6.0.3 では call メソッドを使って this の値を指定するようにしています。

// Prototype.js 1.6.0.3
iterator = iterator || Prototype.K;
iterator.call(context, value, index);

bind メソッドを使うと無名関数が生成され、呼び出しもその無名関数を経由することになります。call メソッドを直接使うことで無名関数をなくし、高速化を図ったのではないかと思います。

Selectors API を使った要素取得

CSS セレクタを用いた要素取得で、Selectors API が使えるときはそれを使うようになりました。使えなければ従来どおり XPath または DOM Core の機能を用いて要素を取得します。

Safari での bfcache の無効化

Safari では bfcache にまつわる不具合があるようで、unload イベントにダミーのイベントリスナを登録することで bfcache を無効化しようとしています。bfcache とは blazingly fast cache (驚くほど速いキャッシュ) あるいは back-forward cache の略で、Web ページをキャッシュする際、そのソース文字列だけでなく、そこから構築された DOM オブジェクトなどの状態もキャッシュする機構のことです。これにより、「戻る」「進む」動作によるページ遷移が大幅に高速化されます。この仕組みはもともと Opera で実装されており、Firefox 1.5 が同様の仕組みを採用した際に bfcache と名付けました。

雑感

約 1 年間続いた Prototype.js コードリーディングもついに最終回を迎えました。私は途中 1 回欠席したものの、ここまで読み続けられ、Prototype.js の考え方や大量のバッドノウハウに触れられたのは貴重な経験となりました。そして何より、Kanasan.JS を通してさまざまな方と知り合え、JavaScript に限らず行動範囲が大きく広がりました。このような場を支えてくださったスタッフ、参加者の方々、そしてそのきっかけを与えてくれた Kanasan さんに深く感謝します。

コメント

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※投稿には管理者が設定した質問に答える必要があります。

名前:
メールアドレス:
URL:
次の質問に答えてください:
「ハイパーテキストマークアップ言語」をアルファベット4文字でいうと?

コメント:

トラックバック

このエントリのトラックバックURL: http://nanto.asablo.jp/blog/2008/10/16/3823078/tb