今回は、470行目~769行目までを解説します。
slice()
sliceメソッドは、Arrayオブジェクトのsliceを実行しています。わざわざ別のメソッドを定義しているのは、pushStackを使って現在の状態をスタックに格納するためです。
map()
mapメソッドは、配列の各要素に関数を適用します。Array.mapはJavaScript 1.6から実装されていますが、1175行目で独自に実装されています。ここでもスタックに格納しています。
andSelf()
andSelfメソッドは、直前に選択されていた要素を現在選択されている要素に加えて返します。
domManip()
domManipメソッドは、第2回で紹介したappend(),prepend(),before()などのメソッドから利用される内部処理用の関数です。
まず、485行目では、選択されている要素が複数あるかどうかを判定しています。次に489行目で初回ループ時のみcleanメソッドを実行し、argsとして渡された処理対象のエレメントをelems変数に格納します。また、もしreverseが真ならばelemsの各要素を逆順にしておきます。
次に497行目ですが、table要素を操作する際にtbody要素がなければ生成して、obj変数に格納します。これは、Internet ExplorerでtbodyがないとうまくDOM操作が動かないための対処です。
次に500行目からは、要素に対してcallback関数を適用する部分なのですが、script要素が含まれていたら変数に待避しておきます。そして、最後に520行目でまとめてscriptの内容を評価します。
initプロトタイプ定義
後にインスタンス化するときのために、jQuery.prototype.initメソッドのプロトタイプにjQueryオブジェクトのプロトタイプを設定しています。これによって、ここまで説明してきたjQuery.prototypeに定義された様々なメソッドやプロパティを継承しています。
evalScript()
528行目は、evalScript関数の定義です。先ほどのdomManipメソッドの中で登場しましたが、DOM操作の際にscriptタグを後から実行するためのものです。529行目でsrc属性が指定されていればajaxメソッドを使って外部スクリプトを読み込み、実行します。そうでなければ、直接スクリプトを評価して実行します。
そして、親要素が存在するときは渡された要素を削除します。
extend()
extendメソッドは、jQueryオブジェクトを拡張するメソッドです。代表的な使い方は、jQuery.extend(target, object1, [objectN])で、targetのプロパティをobjectNのプロパティで上書きします。複数のObjectを指定した場合は、右に指定したObjectのプロパティが優先されます。jQuery.fnオブジェクトを拡張して独自のプラグインを作成する場合にもこのメソッドを利用します。
実際に中でどのような処理が行われているかですが、548行目にあるように第1引数にBooleanが渡された時は動作が少し変わってきます。この場合は、第1引数をdeep変数に格納し、第2引数以降をスライドして処理対象とします。
560行目は、引数が1つだけの場合の処理です。この場合は、jQueryオブジェクトへの拡張だと判断してtargetにjQueryオブジェクトを設定します。
そして、565行目からが拡張処理を行う部分になります。引数の数だけ繰り返し処理を行いますが、値がnullやundefinedの場合には何もしません(567行目)。そして、ディープコピー(deep==true)の場合で、DOMツリーのNodeエレメントではないオブジェクトならば、再帰的にextend()を呼び出して対象のオブジェクトをマージしていきます。また、571行目は循環参照により、無限ループが発生するのを抑えるための処理のようです。
変数定義
4つの変数を定義しています。expandoは、現在のjQueryオブジェクトにユニークなIDを割り当てます。uuidとwindowDataは要素にユニークIDを割り振るために使われます。excludeは、CSSプロパティで単位pxを付加するかどうかの判定に利用されます。
noConflict()
ここからは、jQuery.…()で呼び出されるメソッドの定義になります。
標準では「$」にjQueryオブジェクトのショートカットが割り当てられます。このまま他のprototype.jsなどのライブラリと一緒に使うと競合してしまいますが、noConflict()をコールすることでこれを回避できます。595行目において、24行目で待避しておいた_$を$に戻しています。
また、hoge = noConflict(true)と引数付きでコールすることで、598行目にあるようにjQueryオブジェクト自体も完全に元に戻すことができます。
isFunction()
isFunctionメソッドは、関数オブジェクトであるかどうかを判定するためのメソッドです。prototype.jsにも同名のメソッドがあるのですが、こちらは次のように非常に単純です。
どうしてこんな複雑なことをしているのか不思議に思うかもしれませんが、答えはテストコード(test/unit/core.jsの136行目以降)を見ると分かります。実はブラウザごとに挙動が違っていて、より正確を期すためにこのような処理になっています。たったの2行でブラウザごとの差異を吸収しているわけです。
isXMLDoc()
610行目のisXMLDocメソッドでは、引数elemがXMLドキュメントもしくはその一部であるかどうかをチェックします。document.bodyが存在するかどうかで判断しているようです。
globalEval()
globalEvalメソッドも内部的に使用するメソッドで、グローバルスコープでスクリプトを評価するものですが、中身は意外と単純です。渡されたdataから余計な空白を取り除き、623行目でscriptエレメントを生成しています。ブラウザがInternet Exploerの場合はtextプロパティにスクリプトを設定、それ以外の場合はテキストノードを作成して子要素を追加しています。そして、最後にheadエレメントにscriptを追加するという流れになっています。
nodeName()
nodeNameメソッドは、第1引数で渡されたエレメントのnodeNmaeと第2引数の文字列が同じかどうかを判定して返します。
cache()
一時的にキャッシュとして利用するためのプロパティです。jQuery.data()とjQuery.removeData()から利用されます。
data()
dataメソッドは、要素のユニークなIDを返します。もしidが定義されていなければ、588行目で定義されたuuidをインクリメントして利用します。
また、主にイベント処理で利用するのですが、要素に関連付けられた一時データをcache属性に格納しておくことができます。それが653行目以降の処理で、引数nameのみが指定されていたら値を取得し、引数dataも指定されていたら値を設定します。
removeData()
removeDataメソッドは、expandoおよびdataメソッドで要素に関連付けられたデータを削除します。特に難しい部分はありませんが、Internet Explorerの場合のみ注意が必要でdeleteの代わりにremoveAttributeメソッドを利用しています。
each()
一般的なイテレータの機能を提供するeachメソッドです。
引数のargs配列があるかないかでcall()とapply()を、またArrayかそれ以外のObjectかでループ処理を使い分けています。コメントやJohnのコミットログを見る限りでは、call()を使った方が速いということでこのようにしているようです。
prop()
prop()は、内部的に使用するメソッドです。引数valueとして関数オブジェクトが渡された場合は、引数elemに対してその関数を実行します。また、引数valueに数値が渡され、CSSメソッドからの呼び出しの場合に単位"px"を付加します。
className()
classNameは、内部的に使用されるためのものです。
className.addメソッドは、addClass()から利用されます。748行目で複数のクラス名が指定されていて、nodeType=1つまりElementの場合で既にそのクラスが割り当てられていなければ、空白に続いてそのクラスを追加します。
また、className.removeメソッドはremoveClass()から利用され、addと同様にそのクラス名を取り除きます。
最後にclassName.hasメソッドはそのクラス名が既に指定されているかを判別するためのメソッドになります。1111行目で定義されているinArray()メソッドを利用しています。