夏休みはタイ古式マッサージセミナーに参加してきました。agoです。
先週日曜日若手IT勉強会に参加させていただき、jQueryのコードリーディングを行ってきました。
そこでjQueryのコードを読むときの基礎知識に関して簡単にまとめてみたいと思います。
1 変数の複数同時宣言と代入
まず、JSでは変数の宣言は以下のような形式で行います。
var hoge;
この場合は単一の変数の宣言ですが、以下のような記述を行うと複数の変数を同時に宣言することが可能です。
var hoge, huga;
また、変数宣言と同時に値の代入を行うことも可能で、その場合以下のような記述になります。
var hoge = 'test';
さらに複数の変数宣言と、値の代入は同時に行うことが可能で、その場合以下のような記述になります。
var hoge = 'test1', huga = 'test2';
jQueryでもこの記述は多く使われており、最初の変数宣言も以下のような記述になっています。 (コメントを除いています)
var
window = this,
undefined,
_jQuery = window.jQuery,
_$ = window.$,
jQuery = window.jQuery = window.$ = function( selector, context ) {
return new jQuery.fn.init( selector, context );
},
quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
isSimple = /^.[^:#\[\.,]*$/;
ここでは、window, undefined, jQuery, $, jQuery, quickExpr, isSimpleのローカル変数が定義されています。 (windowはグローバルのwindow objectの参照、undefinedは未定義値の定義)
2 slice().sort()
jQuery.event周りのコードではよく以下のような記述が行われています。
var namespaces = type.split(".");
handler.type = namespaces.slice().sort().join(".");
namespacesは.split()の結果なのでArrayであることは保証されていますが、なぜか.sort()を行う前に.slice()を行っています。
これはJSの.sort()が破壊的であるためで、一旦.slice()を行うことにより配列のコピーを作成し、その結果に対して.sort()を実行するための記述です。
もしここで.slice()を行わない場合、.sort()段階でnamespaces自体が.sort()されてしまい、後でnamespacesを使用するときに元の順番を再現することができなくなってしまいます。
また、同じような処理として.slice()の代わりに.concat()を使用する場合もあります。
3 Sizzle
jQueryでは1.3系からjQueryと同じ作者によって開発されたSizzleというCSS selector engineが使用されており、コード的にjQueryと分離された形で埋め込まれています。
CSS selector engineというのは渡された文字列をCSS selectorとして分解し、その内容にあったelementsをdocument objectから.getElementsByTagName等を使用してelementsを返すパーサになります。
基本的にはJSなので読めないことはないのですが、CSS selectorを解釈するための長大な正規表現やソース内のコメントが少ないことから読むのは大変かもしれません。
4 thisがArray like
jQueryは基本的にthisがArray likeのobjectになっており、そのためたまにthis[0]のような記述がされていることがあります。 (2009/10/1 追記 Arrayではなく、正しくはArray likeのobjectであるとご指摘を受け修正いたしました。ありがとうございます)
また、thisに対してArray系のmethodが適応されることも多いので注意してください。
5 .extend()を使用したobject拡張
jQuery.extend()の基本的な機能はobjectの拡張ですが、単一の引数で呼び出された場合、jQuery object自体の拡張を行います。
jQuery.extend({ 'hoge' : 'huga' });
console.log(jQuery.hoge); // => huga
また、jQuery.fn.extend()も単一の引数で呼び出された場合、jQuery.fn object自体の拡張を行います。
jQuery.fn.extend({ 'hoge' : 'huga' });
console.log(jQuery().hoge); // => huga
ここで、jQuery.fn.extend()の拡張し対してjQuery()でアクセスしていますが、jQuery.fn.extend()への拡張はjQuery objectではなく、jQuery functionの呼び出し結果のobjectへの拡張になります。
jQueryの内部でもjQuery.method形式で呼び出すmethodとjQuery().method形式で呼び出すmethodでは同じ名前でも扱いが違うので注意してください。 ($.eachと$().eachは別物として定義されています)
6 jQuery.event
jQueryではブラウザの仕様から外れたeventの扱いが多く、実装のほとんどをjQueryの内部で行っています。 (namespace、eventのbubbling、eventへの独自引数、イベント設定時の初期化処理、カスタムイベント、live event、ブラウザ差異の補正)
コードを読むときは以下の点を注意して読んで見てください。
- namespaceは.区切りで複数の名前空間を指定できること($().bind('click.hoge.huga', function () {})が可能)、
- 追加されたeventはjQuery.data(elem, "events")に保存されていること、
- jQuery.data(elem, "events")内のイベントの呼び出しをjQuery.data(elem, "handle")経由で行っていること
ちなみにver 1.3.2ではnamespaceは複数の名前空間を同時に指定した場合、問題が発生する場合があるので注意してください。