Kanasan.JS jQuery コードリーディング#2に参加しました

9月26日にKanasan.js主催で開催された「jQuery コードリーディング#2」に参加してきました。

複数人でペースを合わせてコードを読んでいくのは初めてだったので、最初は勝手がつかめなくて戸惑いましたが、慣れるとかなり面白かったです。一人で読んでいたら何気なく読み飛ばしてしまうような箇所でも、他の人の質問を受けて改めて読み返すと意外な発見があったりして、なかなか新鮮な体験でした。自分も途中からはほとんど独り言をつぶやくようなつもりで発言しまくっていました。

ただコードを読む前に、まずjquery.comのAPI Referenceを読むべきだったかなと思いました。たとえよく使うメソッドであっても、意外と知らない機能が隠れていたりするもので、そうなるとコードの意図を推測する時間が余計にかかります。例として jQuery.map が挙げられます。このメソッドは、JavaScriptの Array.prototype.map と違ってLispの mapcan みたいな機能を兼ねています。すなわち null, undefined は無視され、配列は concat されます。

$.map([1,2,3,4], function(x){ if(x % 2 == 0) return [x, x]; });
// => [2,2,4,4]

この動作を頭に入れておけば、最後の concat.apply は配列を一次元flattenするためのイディオムであることもすぐに見当がつきます。

ほとんど使ったことがないメソッドについては、この”意図を知る”作業は決定的に重要になります。今回の範囲で言うと私は queue, dequeue を全く使ったことがなかったので、かなり苦労しました。「これらはアニメーション(エフェクト)の基礎になっているメソッドだ」と別の方が指摘してくれたお陰でだいぶ助かりましたが、最初からAPIリファレンスを読んでいれば、もっと手っ取り早く正体がつかめたはずです。

実際にはこのqueque/dequeueは、非同期で実行される操作(関数)をあらかじめキューに溜めておいて、実行順序を保証するための仕組みです。アニメーション以外の例では、例えば jQuery.getScript によるスクリプトを読み込みを非同期かつ安全に実行するために使用できます。

/**
 * - LibraryA,
 * - LibraryA に依存する LibraryB
 * - LibraryA と LibraryB を使用する処理
 * を順番に実行する.
 * 読み込みが失敗したら後続の処理は実行しない
 */
 
// script-loader という名前の queue を使用する
jQuery.fn.loaderQueue = function(func) {
    return this.queue('script-loader', func);
}
jQuery.fn.loaderDequeue = function() {
    return this.dequeue('script-loader');
}
 
// スクリプト読み込みが成功したら dequeue する $.getScript
function dequeuingGetScript(url) {
    return function(next) {
        jQuery.getScript(url, function(data, status) {
                next();
            });
    };
}
 
jQuery(document)
    .loaderQueue(dequeuingGetScript('libraryA.js'))
    .loaderQueue(dequeuingGetScript('libraryB.js'))
    .loaderQueue(function(){ alert(LibraryA.func() + LibraryB.func()); })
    .loaderDequeue();

ただ実装の中ではエフェクト用の処理(fx queue)を決め打ちで特別扱いしているので、メソッドを分割した方が良さそうだよね、という意見が会場で出ました。

……

その他諸々、会場での話題や気づいた点など。

  • ローカル関数 access は非常に複雑かつ名前も分かりにくすぎる。特に第6引数 pass が何のためにあるのかわからない。一応
    $('#sss').attr({ key:function(){  }}, true)
    という形で使用すれば機能することはわかったが、何に使うか不明。
  • jQuery.support.hrefNormalized の名前が意味と逆転している。
  • expando って何?=>実行時に動的に追加されるプロパティのこと。
  • ローカル関数 eventSupported は特定のイベントがbubbleするかどうかのチェックに使われている。IEではsubmit/changeはbubbleしない。jQuery 1.4からはjQueryがエミュレートしてくれるようになった。
  • jQuery.data まわりのコードで jQuery.expando とローカル変数 expando の参照が入り交じっている。ケアレスミス?
  • 変数名 jQuery.cache は変。何かをキャッシュしているわけではなく、単なるデータストア。

あと懇親会ではバイクで行ったせいでお酒が飲めず、大変残念な思いをしたので、次に参加するときには絶対にバスか電車で行こうと思いました。

Leave a Reply