search phpbb-phpbb-FC2BLOG-Info-Edit Template-Post-Edit-Upload-LogOut
このエントリイでは、jQuery()コンストラクタによるインスタンスの初期化過程、つまりinit()メソッドについて概要解読を行います。ユーザーによってjQuery(a,c)関数が起動されると、jQuery.jsはインスタンスオブジェクトを作成し、その初期化を int() メソッドが担います。
まず該当箇所のコードを抜粋すると以下の通りです。
31:var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;
32:
33:jQuery.fn = jQuery.prototype = {
34: init: function(selector, context) {
35: // Make sure that a selection was provided
36: selector = selector || document;
37:
38: // Handle HTML strings
39: if ( typeof selector == "string" ) {
40: var m = quickExpr.exec(selector);
41: if ( m && (m[1] || !context) ) {
42: // HANDLE: $(html) -> $(array)
43: if ( m[1] )
44: selector = jQuery.clean( [ m[1] ], context );
45:
46: // HANDLE: $("#id")
47: else {
48: var tmp = document.getElementById( m[3] );
49: if ( tmp )
50: // Handle the case where IE and Opera return items
51: // by name instead of ID
52: if ( tmp.id != m[3] )
53: return jQuery().find( selector );
54: else {
55: this[0] = tmp;
56: this.length = 1;
57: return this;
58: }
59: else
60: selector = [];
61: }
62:
63: // HANDLE: $(expr)
64: } else
65: return new jQuery( context ).find( selector );
66:
67: // HANDLE: $(function)
68: // Shortcut for document ready
69: } else if ( jQuery.isFunction(selector) )
70: return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( selector );
71:
72: return this.setArray(
73: // HANDLE: $(array)
74: selector.constructor == Array && selector ||
75:
76: // HANDLE: $(arraylike)
77: // Watch for when an array-like object is passed as the selector
78: (selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) ||
79:
80: // HANDLE: $(*)
81: [ selector ] );
82: },
31行で定義されている変数 quickExpr は引数 selector に対する正規表現検索のための文字列で、その意味は次のようになります。
あるいは
のいずれか。
ここに「単語」とは「文字、数字あるいはアンダースコアのいずれかで構成されている文字列」であり、また部分一致文字列1の中に部分一致文字列2(何らかの文字か空白文字が1つ以上ある文字列)が設定されていますが、この部分文字列が以後のコードで呼び出される箇所はまだ「発見」していません。
重要な点なので敢えてここに記します。
RegExp.exec()メソッドは、正規表現による「汎用的で最も強力な」検索メソッドとされていますが(『Javascriptクイックリファランス Javascript1.5対応』pp.112〜113参照)、そのように言わしめる理由は部分一致文字列を取得できるからである、と確信します。
jQueryにおける利用法を基にそれを紐解けば、次のようになります。
検索結果が変数mに格納されますが、このmはexec()メソッドの仕様上配列となります。そして、当該配列には順に次の情報が格納されます。これらの正規表現を駆使した検索によって、"<・・・>"や"#idName"、".className" を抽出しています。
変数 m に格納される情報 m[0]──/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/ なる検索に一致した文字列 m[1]──/<(.|\s)+>/ なる検索に一致した文字列 m[2]──/.|\s/ なる検索に一致した文字列 m[3]──/\w+/ なる検索に一致した文字列
さて、33行以降497行まで、延々と jQuery() インスタンスの prototype プロパティの設定が行われますが、最初に置かれた init() メソッドが 20行 から呼び出されます。
呼び出された init() メソッドが最初に行うことは第一引数が与えられているかどうかのチェックであり、第一引数があればそれを selector に代入し、なければ document プロパティを selector に代入しています。(36行)
続く長いコードは if キーワードによって幾つかの階層に分岐されますが、その構造を視覚化すれば次のようになります。
init()
┌(1)selectorが文字列型の場合
│ ├(1-1)"<・・・>"の場合 ex."<div><p>Hello</p></div>"
│ ├(1-2)"#string"の場合 ex."#example1"
│ │ ├─(1-2-1)IEかOperaにおいてID名の代わりにnameが返された場合
│ │ └─(1-2-2)その他の一般的な場合
│ │ └─(1-2-3)指定したidName に対応する要素がなかった場合
│ └(1-3)その他の場合
│ ex."div > p"、"*"、"div, span, p.className"、"#idName > *"、
│ "#idName ~ div"、"label + input"、"tr:first"、":header"、
│ "div:has(p)"、"selector1, selector2, ・・・,selectorN"、
│ "tr:even "、"td:gt(index) "、"div:visible "、・・・・
├(2)selectorが関数型の場合
└(3)その他の場合………………………………………………72〜81行
return値
(1-1) HTMLの指定した要素の配列:selector=jQuery.clean(m[1],context)……44行
(1-2-1) 指定したidName値により食された要素の配列
(for IE,Opera Bug):jQuery().find(selector)……53行
(1-2-2) getElementByIdにより取得されたid名のHTML要素の配列:this……57行
(1-2-3) 空の配列:selector=[]……60行
(1-3) 新たなインスタンスが作成され、それによりcontext文字列から
selector文字列を検索した結果の配列……65行
(2) $(document).ready(function(){ /* Your code here*/ });
または $(document).load(function(){ /* Your code here*/ });……70行
(3) [selector](1-1及び1-2-3の場合を含む)
それには本家サイトの API Reference が手っ取り早いでしょう。こちら(API/1.2/Core - jQuery JavaScript Library)で例示を見ることが出来ます。
init()メソッドの挙動は結構複雑です。次々とjQuery固有のメソッドが連鎖的に呼び出されて目的が遂行されます。そこでjQuery.js のインクルード時から、init()メソッドが実際に動く迄の過程を追跡し、特に this がどの様に変化するか、返値は何か、に着目してその挙動をまとめてみました。
まず、jQuery.jsがインクルードされ、その後ユーザー指示による jQuery(a,c) 実行によって、インスタンスが生成される迄の動きです。
| タイミング | 前this | 履行行為 | 後this | 返値 |
|---|---|---|---|---|
| include時 | window | 変数設定、prototypeプロパティ設定 | window | なし |
| jQuery(a,c)実行 | window | new演算子によるインスタンス生成とjQuery(a,c)コンストラクタ呼び出し。インスタンス=this | newjQuery(a,c)によるインスタンス | (1)new jQuery(c).find(a)の結果値 (2)new jQuery(document)[jQuery.fn.ready ? "ready" :"load"](a)の結果値 (3)aに基づく配列、または[a] |
上表よりも更に深化させた分析も行ってみました。よろしければ、こちらの Javascript基礎の基礎(1) return 値は「何に」返されるのか?──jQuery解読(16)をご覧ください。 こちら は、表では静的で理解しにくい部分があるため、動きを文章化し、return先迄含めてまとめたものです。(2007/11/27記)
更にインスタンス生成後に行われる初期化過程を追跡してみると、次のようになるはずです。
| 前this | 履行行為 | 後this | 返値 |
|---|---|---|---|
| new jQuery(a,c)によるインスタンス | jQuery().find(a)実行 | jQuery()によるインスタンス | jQuery().find(a)の結果値 |
| new jQuery(a,c)によるインスタンス | document.getElementById(・・)の実行 | インスタンスに左記結果が代入され | それが返値となる |
| new jQuery(a,c)によるインスタンス | new jQuery(c).find(a)による新規インスタンスの生成 | new jQuery(c)による新規インスタンス | jQuery(c).find(a)の結果値 |
| new jQuery(a,c) によるインスタンス | new jQuery(document)[jQuery.fn.ready ? "ready" :"load"](a)による新規インスタンスの生成 | new jQuery(document) によるインスタンス | jQuery(document).ready(a) 又は jQuery(document).load(a)の実行結果値 |
| new jQuery(a,c)によるインスタンス | 配列[a]の生成 | インスタンスへの配列の代入 | その配列 |
一般にthisは、new演算子とコンストラクタから生成されたインスタンスオブジェクトを参照するか、関数を起動したオブジェクトを参照します。ですから、new演算子によって新たなインスタンスが定義されるその都度、thisの参照先は変化します。上表のとおり、thisの参照先はインスタンス作成の度に変化します。またそのインスタンスの挙動の中で起動されたメソッドによって、様々に変化します。
一方、変数jQueryの値、というよりも参照先は常にコンストラクタ関数オブジェクトのままで、それは固有のクラスプロパティとクラスメソッドを保持し続けます。
ところで、このようなコンストラクタ、インスタンス及びthisの振る舞いは、jQuery.jsの冒頭に配置され、二項演算子で分岐処理を施している僅か6行の次のコードによってもたらされています。
17:var jQuery = window.jQuery = function(selector, context) {
18: // If the context is a namespace object, return a new object
19: return this instanceof jQuery ?
20: this.init(selector, context) :
21: new jQuery(selector, context);
22:};
無名関数にコンストラクタの機能を持たせると同時に、そのコンストラクタから生成するインスタンスの初期化機能をも内包させる───こうしたコンストラクタ関数は一般に目にします。しかし、インスタンスの初期化結果をその生みの親となったコンストラクタ関数にリターンさせている点がこのコードの大きな特徴なのではないでしょうか。───僅か6行でこれだけのことを成し遂げているのですから、驚いてしまいます。
第一引数 selector の型が文字列型であれば(上分岐の(1)=39行)、その分岐(1)を更に分岐するために上述の ReExp.exec()が登場します。文字列 selectorを対象として正規表現検索文字列quickExpr に exec()メソッドが適用されます。(40行)
他方、selectorの型が文字列でない場合には、関数型であるかどうかチェックされ(上分岐の(2)=69行)、関数型であれば有名なコード $(document).ready(function(){ /* Your code here*/ });、または $(document).load(function(){ /* Your code here*/ });が呼び出されます。(70行)
最後に文字列型の場合の一部のケースや、関数型でもない場合及びその他の場合においては、 selector を値とする配列が返されます。(73-83行)
さて、selectorが文字列型の場合(39〜65行)の中身を見てみると次のように分岐されます。
このケースでは、41〜61行までのコードでjQuery(タグ付きタグ名)から配列を求めたり(42〜44行)、jQuery(id名称)から該当要素を抽出しています(46〜58行)。
前者のHTMLタグ名から配列を求めるコードは、jQuery.cleanメソッドを使って文字列の「清掃」を行います。このメソッドは765〜854行までの大変長いコードですが、中では更に別のjQuery.each()メソッドを利用しています。
一方後者のid名称によって対象要素を抽出するコードでは、225行以降で定義されているjQuery.find()メソッドによってIEやOperaのバグ対策を行った結果を返値とし、その他の場合にはdocument.getElementByIdによる検索結果を素直に返値としています。
このように、jQueryはあれこれの固有に定義した関数をサブルーチンとするコードに満ちあふれており、そのために複雑怪奇に見えます。なお、ここで呼び出されているjQueryの各種メソッド(cleanメソッド、eachメソッド及びfindメソッド)の解読は別の機会に行うことにします。
この場合には、new演算子が新たなインスタンスを生成し、所与の第二引数 context を唯一の引数とする jQuery(context)関数をコンストラクタとして機能させます。この関数処理が終わった時点で、当初の第一引数であるselectorを引数とするjQuery(content).find(selector)メソッドが起動し、ドキュメント全体または指定した範囲内において、DOMのプロパティやメソッドを多用して、selector文字列によるHTML要素の検索を行い、その結果が init() の返値として return され、最終的にユーザーが入力した jQuery(selector,context) に返されます。
多忙故に投稿に間が空いてしまいましたが、jQuery解読によるJavascript学習を先に進めたいと思います。
まず、jQuery1.2.1.jsがインクルードされた時にどの様に振る舞うのか、解明したいと思います。
13:// Map over jQuery in case of overwrite 14:if ( typeof jQuery != "undefined" ) 15: var _jQuery = jQuery;
ここでは、既にjQueryが定義済みの場合に、そのjQueryを変数_jQueryに代入し、既存のjQueryをいわば待避しています。このコードにより既存のjQueryがこの後のコードによって上書きされても、無名関数の呼び出しが終わる迄の間は、既に存在していたjQueryを_jQueryに保存することが出来ます。
なお、このような待避の必要性、つまりjQueryのscriptコードが読み込まれた時点で、既に変数jQueryが存在しているケースはどの様な場合があるのか判然としません。
しかも、この変数 _jQuery は Call オブジェクトの変数であって、決してグローバル変数ではありませんから、インクルードが終われば消滅してしまいます。つまり待避させた_jQuery を利用しようとすれば jQuery.js 内にそれを利用するコードを加えなければなりません。
その方法は1.2と1.2.1で全く異なっていますが、jQuery 関数が呼び出された時の this が jQuery のインスタンスでない場合にはそれを作成し、this がインスタンスであった場合には init() 関数を呼び出しています。但し、この jQuery インスタンスは jQuery インクルードに生成されるわけではありません。インクルード時には jQuery の定義文が解釈されるだけです。jQuery のインスタンスが生成されるのは、ユーザーによってjQuery(・・・)関数が呼び出された時です。
インクルード時には関数は解釈されるだけなので、ここでの主題からははずれますが、ここで、そのjQuery()呼び出し時の、1.2.1におけるインスタンスの作成方法のあらましを解読しておきます。
17:var jQuery = window.jQuery = function(selector, context) {
18: // If the context is a namespace object, return a new object
19: return this instanceof jQuery ?
20: this.init(selector, context) :
21: new jQuery(selector, context);
22:};
※ ここで使われている instanceof は、Javascript1.4以降で使用可能となった演算子で、「a instanceof A」の構文で、aがAのインスタンスであるかどうかを判定しています。
jQuery1.2迄は、this が window か、this.init が存在してない場合に、jQueryのインスタンスを生成し、そうでない場合には this.init() を呼び出してましたが、1.2.1による instanceof 演算子を使用する方法の方がスマートです。
このコードは次のことを行っています。
なお、このコードにおける「 return 値の連鎖 」の分析については、 Javascript基礎の基礎(1) return 値は「何に」返されるのか?──jQuery解読(16)をご覧ください。
関数リテラルで表現されたこのコードは、インスタンス作成と作成されたインスタンスの初期化を一気に行っており、非常に合理的だと思います。
24:// Map over the $ in case of overwrite 25:if ( typeof $ != "undefined" ) 26: var _$ = $;
$はprototype.jsなどでも使用されているGlobal変数ですから、jQuery.jsのインクルード時に既にそれが存在している可能性があります。そこでこのコードが必要となります。また当然jQuery()インスタンスの生成によっても定義されますから、直前のインスタンス生成による $ を待避することにもなります。
ここで重要なのは前者です。jQueryによる $ が他のフレームワーク等によって定義されたグローバル変数 $ を上書きしても支障がないように、_$ に既存の $ を待避させています。
但し、この変数 _$ は _jQuery同様にCall オブジェクトの変数であって、決してグローバル変数ではありませんから、インクルードが終われば消滅してしまいます。つまり待避させた_$を利用しようとすればjQuery.js内にそれを利用するコードを加えなければなりません。
業務繁忙故、ブログ投稿が出来ない日々が続いてしまいました。10日ぶりの投稿です。
jQueryはその全体がたった1つの無名関数で定義され、かつインクルード時にその無名関数が実行されるフレームワークです。それ以前は異なった定義方法でしたが、確かver1.1.4から無名関数による定義スタイルになりました。
そして重要な点は、インクルード時に実行される無名関数によって定義されるグローバル変数が僅か2つ──「jQuery」とそれへのショートカットである「$」──に留まることです。(cf. prototype.jsの場合にはインクルード直後に定義されるGlobal変数は26に達します。)
このGlobal変数を僅か2つしか定義しないことが、jQueryの大きな特徴の1つではないでしょうか。このGlobal変数の少なさは、任意のscriptから利用でき、かつ任意のフレームワークと共存して利用できるようにするための、後発フレームワーク故の工夫と言えますが、目的や理由はさておき、グローバルな名前空間を汚さない配慮は、フレームワークが殆どの場合において、他のスクリプトと共存利用されることを踏まえれば、当然の措置と言えます。
なお、グローバルな名前空間を出来るだけ汚染させないこの配慮は、『Javascript第5版』においてDavid Franagan氏が主張していることにほぼ整合します。
(jQueryではショートカット「$」を設けたことによって「最大1つ」が破られて2つになっていますが、これは必要なやむを得ない措置だと思われます。ショートカットの利便性は極めて高いからです。)
jQueryが定義するグローバル変数が実質的に1つであることは、jQueryを使う上でもユーザビリティに貢献しています。使いやすいのです。「書式」が統一的であることから、直感的にコマンドを記述することが出来ますし、覚えることも決して多くはありません。敷居が低いため容易に使い始めることが出来る訳です。
本日時点で最新バージョンである1.2.1は非圧縮版で2993行あります。しかし、その構造は極めて単純明快です。グローバル変数である(1)jQueryと$の定義、(2)prototypeプロパティの定義、(3)extend()関数による多数のプロパティ名とプロパティ値の定義(extend()を使わない固有のプロパティ名と値の定義がいくつか存在しているが)───この3つに収斂します。
こうした簡潔な構造が、extend()関数を利用したユーザーによる自在な独自拡張を許容し歓迎することになります。
多くの場合自作スクリプトとjQueryを併用するはずです。その時に重要となるのはインクルードするファイルサイズを出来るだけ小さくしたいはずですが、jQueryの場合この点を当然のように想定して、圧縮されたファイルを用意しています。
この配慮はユーザーからすれば大変有り難いことです。自ら圧縮する手間を省いてくれるからです。
まず、jQueryの全体的な構造と特徴について分析し、納得したいと思います。
第二に、その構造を理解した上で、includeされたjQueryがどの様に振る舞うのか、それをフォローアップしてみようと思います。つまり当該サイト(又は頁)起動時に読み込まれるjQueryの、読み込まれた直後の挙動を追跡してみたいと思います。
第三に、全体構造とも関わりますが、jQueryの骨格構造を為している、全体を包含する無名関数の定義と定義直後におけるその実行、並びにクラス定義とprototype設定を総覧したいです。
第四に、init()関数で何を行っているのか明らかにし、第五にprototypeで規定されている各種プロパティを総覧しようと思います。
第六には、引数として随所で活用されるselectorとcontextについて、あらゆるケースを想定し検討し、かつ体験してみようと思います。
第七に、extend()関数を利用して行われる様々なメソッド拡張機能について総覧したいです。
第八に、そして最後に、jQueryで巧みに活用されているreturn値について考えてみようと思います。
新管理画面が登場(FC2インフォメーション FC2ブログリニューアルのお知らせ)してから、約1ヶ月半が経過しようとしてます。
まだまだ不具合や仕様変更を要望したい箇所がありますが、そろそろ安定的に動くようになったようなので、Myブログの随所に設置してあるブログ利用上の各種ショートカットを新管理画面用に一斉に切り替えました。例えばブログ最上部にある小さな文字の各種ショートカット先を、全て新管理画面用のURIに変更しました。
これによりログイン時に旧管理画面からスタートしていた昨日までの使い方から、ログインを含めて全てを新管理画面で操作することになります。
これまでは主に書籍を通じてJavascriptの学習を進めてきたのですが、prototype.jsを知ってから、またjQuery.jsに没頭して以来、書籍よりも遙かに豊かなそれらの内容に魅せられ、またその難解さに翻弄されつつも、格闘を重ねる行為を通じて、次第に黎明を迎える喜びを感じてきました。
そして決断しつつあるのです。jQueryの解読を通じてJavascriptの神髄を学習したい、と!
まだ全部を解読できる見通しが立っていないので、ブログに書き始めることは遠慮しているのですが、或る程度解読できた時点から、おいおい解読&学習過程について綴ってみたい、と考えています。
jQueryは複数のコンポーネントから構成されているので、それぞれのブロック単位で解読を進めたいと思います。
まず取り組むべきは、まさにjQueryの核となっているcore.jsの解読を通じてJavascriptの神髄に少しでも達したいと思います。
いつになったらこのブログに書き始められるか、まだ見通しが立ちませんが、忙中閑を探しながらことを進めたいと思います。
90%近いシェアを握っているインターネットエクスプローラの描画エンジンを利用したタブbrowser。沢山のタブbrowserがあるが、多機能、カスタマイズフリー、スクリプト利用等で一日の長がある。Gekkoエンジンへの対応も行われ、IEからの自立独立の方向に向かっている。2005年7月にはIE7が登場する見通しの中で、今後の発展が望まれる。
多様なCSS作成支援機能を備えた、タグ入力式 HTML&CSS作成支援エディタ。スキンデザインもすっきりしている。テキストエディター上で作成するよりも確実で安全にタグ打ちが出来る。
文字コードを選べないのが欠点。
StyleNote同様のタグ入力式 HTML&CSS 作成支援エディタ。長年使用してきたが現在StyleNoteに乗り換えつつある。
クリップボード履歴情報を活用する為のソフト。画像まで履歴を取ってくれるのが嬉しい。このソフトを使わない日は絶対ない程に重宝し、愛用している。
起動中のウィンドウの「コピーできない」説明文などの文字列を取得し、コピー可能な文字データにするツール。何かと便利。
ストリーミングデータを保存することが出来るソフト。動画利用には不可欠なソフトだ。
無料ながらレイヤー機能を有し、スクリプトによる拡張も可能な、sleipnir作者が提供している優れもの画像編集ソフト。
画面キャプチャソフトと言えばこれに勝るものなし、ではないだろうか? 様々な取得方法を有しており、ブログ作成にもHomepage作成に不可欠だ。Jtrimと並んでWoodyBellsの作品。
複数ファイルの同時編集は出来ないが、透過pngも作れる画像編集ソフト。
(以下当該サイトから抜粋)初心者にも簡単に操作が出来るフォトレタッチソフトです。多くの加工機能で画像に様々な効果を与えることができます。非常に軽快に動作するため、ストレスなく操作できます。
Animation Gifファイルを作れる無料ソフト。
キャプチャソフト。画面内にサイト全体が表示しきれない場合でも、これを使えば全体をキャプチャすることが出来る。
画像処理。画像のフォーマット変換のみならず、色数やサイズ、圧縮率の変更まで一括処理できてしまう『BatchGOO!』は、大量の画像をまとめて処理したいときに大変便利なソフト。BMP, TIFF, JPEG, PCX, PNG の相互変換をはじめ、色数・サイズ・解像度の統一、JPEG圧縮率の調節など、ホームページ用の画像や携帯電話用の壁紙を揃えるのに抜群の相性を見せる。(Vectorの当該ソフト紹介頁より抜粋引用)
名前から直ぐに想像が付くように画像のサイズを測るためのソフトだ。Homepage作成には欠かせない。2カラム、3カラムのレイアウトを行う場合に大変重宝する。
ランチャーソフトは沢山あるが、中でもこれが一押しだ。2年以上使ってきたがその操作性には毎日満足している。これを使い始めてからデスクトップには一切のアイコンを表示することをやめてしまった。
AdobeReader7によって、起動時間が長すぎるという長年のユーザーの不満はある程度解消した。そのためこの高速化ソフトは存在価値が低下してしまったかもしれない。AdobeReader6迄はこのソフトによる起動高速化で恩恵を受けてきた。
IE専用が難点だが、様々なサイト内でIDやパスワードを入力するのに重宝するソフト。コンテキストメニューから簡単に起動できるのがGood! sleipnir等のIEの描画エンジンを利用しているブラウザでも使える。
利用しているパソコンの諸元値を取得するには、このソフトがベストだ。インストール済みソフトの一覧が取得できるのも嬉しい。
WMPは機能が豊富なだけ重い。RealPlayerも同様だ。そこでMedia Player Classicを使いたい。動作が軽快なだけではなく、対応しているファイル形式もすこぶる多く、これひとつで、wmvもrmも表示できてしまうのだから凄い! 数多あるMedia Playerの王様と言えるだろう。
自宅でPCを起動しているときには必ず起動しているメディアプレーヤー。何かと過剰なWinampよりも、起動も速くスキンはシンプルだ。
DivX, Xvid, Mov, Vob, Mpeg, Mpeg4, avi, wmv, dv, などの動画をDVD-Video形式に変換できるフリーソフト。クリックするとDVD関連ソフト紹介サイト=「DVDなToolたち」なるHomepageが開きます。