土曜日に開催されたRoppongi.JS#1に参加しました。
内容はjQueryのコードリーディングです。
WEB参加型のJS勉強会という事だったのですが、ちょっと関東での用事と被ったのもあって、移動時間的な問題もあって主催者であるHolyGrailの家に押しかけて参加しました。
急遽の事だったのに、快く受け入れてくれてほんとありがとう!
WEB参加型の醍醐味的な所は味わえてないのかもだけど、かなり楽しく参加させていただきましたよ!
以下、今回勉強した事のまとめ。
とりあえず開催前の告知
3/15(sat) - Roppongi.JS#1 jQuery Code Reading(1)のお知らせ - devlog.holy-grail.jp
Lingrのログなども読めるので、どんな話ししてたか気になる人はLingrをチェック!
※わかりやすく綺麗にまとめるには行数付きのコードをボックス内か何かに併記した方がいいんだろうが、めんどいので行数だけ書く。
(後日気力があったらコード付きに整形する
まず1行目と一番最後の3480行目
グローバルスコープを汚さないための処理。
無名関数で処理を括って、宣言をローカルスコープ内に留める処理はライブラリやGreasemonkyなどの共有されるコードを書く場合は必須ですね。
2~11はラインセンス表記
jQueryはMITとGPLのデュアルライセンス。
14,15は識別子名がかぶった時のための退避処理。
まぁ今更他にjQueryなんて名前付けるやつも居ないとは思うが。。。
少し飛ばして同じく23,24行目も退避処理。
prototype.jsでも使われるような
これらの退避処理はnoConflictメソッドを呼び出す事で元に戻せます。
「jQuery.noConflict()」を呼べば$が戻り。
「jQuery.noConflict(true)」を呼べばjQueryも戻る。
$もjQueryも戻したらどうやって使うんだとか思うけど、別にj=jQueryとかに入れといてもいいし、noConflictの戻り値を使う方法もある。
27行目
window.$ = jQuery;
jQueryを$で呼べるように。
17~20行目
前後になったけど、けっこう鍵。
var jQuery = window.jQuery = function( selector, context ) {
return new jQuery.prototype.init( selector, context );
};
jQuery()や$()で呼ばれるのはここで作られた関数。
内部的にはnew jQuery.prototype.initのラッパーなわけだが、これで明示的にnewしなくても関数呼びだしで使える。
しかし、ここをちゃんと理解するには549行目の
jQuery.prototype.init.prototype = jQuery.prototype;
も大事。
ここでprototypeを上書きする事によって、newされたjQuery.prototype.initのインスタンスからjQuery.prototypeのメソッドが使える。
これがあるおかげでjQuery()=new jQuery()=new jQuery.prototype.initを実現。
長いのでjQuery.prototype.initのリーディングは後回し。
104行目から。
jQueryに何故sizeとlengthが混在するのかという議論
単なるエイリアスとか、歴史的経緯(互換)とか、配列として扱うためとか。
116行目「get」
引数があればそのindexの要素を返す。
引数が無ければ、純粋な配列の形で要素群を返す。
(jQuery.makeArray
128行目「pushStack」
スタックに積む処理だが、具体的には返すインスタンスのprevObjectにthisを代入している。
これで、endメソッドなどを使って一つ前のインスタンスに戻す事が可能。
メソッドチェーンで強力な効果を発揮するわけだけど、使ってみないと実感沸かない。
142行目「setArray」
文字通り配列をセットする。
lengthをゼロでリセットしてから、Array.prototype.push.apply( this, elems );で強制結合。
lengthをゼロにしているのは、Array.pushが結合元のlengthを元に挿入位置を決定し、またその後のサイズもそれに準ずる。
size以外にlengthを利用する理由の一つかも。
applyを使ってるのは引数に配列として渡せるから。
Array.prototype.pushを使ってるのは高速化のためらしい。
引数としては配列のようなオブジェクトでも問題無い。
配列の場合lengthの書き換えで配列の実際のサイズも変更されるが、配列のようなオブジェクトではそうもいかないので、setArrayでサイズが小さくなっても、getメソッドで消えたはずデータを参照できる。
これは外部から強制的に取れるだけで、本来はlengthを通して取られるため残っても問題は無い。
154行目「each」
ここのeachはただのラッパー。
本体はextendされた732行目にある。
thisの要素とargsを引数にしてcallbac関数を呼び出す処理。
160行目「index」
渡された要素と一致するものをeachを使って検索し、同一のものがあればそのindexを返す。
一致するものが無かった場合-1を返す。
172行目「attr」
valueが存在すればseter、存在しなければgeterとして動作する。
第3引数のtypeが物議を醸したが、後述するcssでのみ使われる内部パラメータだった。
本来はextendされたattrに処理が移行するが、typeがある場合のみtypeが示すメソッドが呼び出される。
いまいちpropがわかってないので、そこのリーディングの時にまた読み直そう。
198行目「css」
さきほどのattrを呼ぶメソッド。
widthとheigthに負の値が入らないようにしている。
205行目「text」
引数が存在しobjectではない場合、this.empty()で自身を空にして、appendでセットする。
引数が存在しない場合、eachで回して子ノードのテキストを突っこんでいく。
「this.nodeType != 8」でコメントノードを排除し、「this.nodeType != 1」でエレメントノードだった場合は再起呼び出し。
223行目「wrapAll」
この18行のソースが最大の難関となった。
動作としては与えられたDom要素で既存のDom要素を囲ってしまうメソッド。
具体的な動作は以下。
<span id="id">aa</span>こーゆーHTMLソースがあって
$('#id').wrapAll("<div><p><a><s></s></a></p></div>");これを実行すると。
<div><p><a><s><span id="id">aa</span></s></a></p></div>こうなります。
内部的には、
jQuery( html, this[0].ownerDocument )
で受けとったHTMLソース(もしくはDOM要素)をjQueryインスタンス化
.clone()で既存のDom要素を引数にした時に破壊的にならないように。
.insertBefore( this[0] )位置を保持
.map(function(){ ~略~ })で各要素のfirstChildに掘り下げていって、一番深い階層に到達。
一番深い要素に
.append(this);
でthisを追加。
ここまで来るのに時間がかかった。
まず俺はmapメソッドの使い方すらわからなかったわですが、Lingrでの会話でやっと全体を理解できました。
いやはや勉強になる。
そしてここで時間です。
4時間半かけて240行!
ヒャーヽ|'p'|ノ
ってか37~101すっとばしてるので、176行ですね!
ヒャーヽ|'p'|ノ
とりあえずLingrのログを見た感じだと世界中(偏りあり)からの参加だったようで、距離を無視した勉強会の環境は既にインフラとしては十分ですね!
UstとLingrとSkypeのオープン通話の3つがキーですね。
メイン会場のUst。
そこに音声としてSkypeの音も一緒に入る。
Lingrに会議を統一。(ソース貼り易い
これでUstの録画さえしておけば、勉強会の全容が後から参照できて、勉強の教材としても残ると面白いかもね。
コードリーディングは続けていくにせよ、今回は確実に初心者向けからかけはなれていってたのが一番の反省点かな。
何かいいネタないかなー。
最後にLingrから引用
monjudoh:俺たちの戦いはこれからだ!!HolyGrail先生の次回作に御期待ください
コメントする