オープンソース フレームワークについて読んだとき、最も学びたいのは、設計のアイデアと実装テクニックです。
それほどナンセンスではありません。jQuery 分析は長年にわたって悪影響を及ぼしてきました。私はずっと前に読んだことがあります。
しかし、ここ数年はモバイル端末で作業しており、常に zepto を使用していました。最近、時間をかけてもう一度 jquery をスキャンしました。
スクリプトに従ってソースコードを翻訳するわけではありませんので、実際の経験に基づいて読んでください。
github 上の最新のものは、AMD 仕様に加わった jquery-master です。公式の最新 2.0.3 を参照します。
全体構造
jQuery フレームワークの中核は、HTML ドキュメントの要素を照合し、それらの要素に対して操作を実行することです。
例:
上記の記述から少なくとも 2 つの問題が見つかります
1. jQuery オブジェクトの構築方法
2. jQueryメソッドの呼び出し方法
分析 1: jQuery の新規不要の構築
JavaScript は関数型言語であり、関数はクラスを実装できます。クラスはオブジェクト指向プログラミングの最も基本的な概念です。
var a = new aQuery();
a.name();
これは従来の使用方法であり、jQuery がこのように動作しないことは明らかです。
jQuery は、jQuery 表示のインスタンス化に new 演算子を使用しないか、その関数
を直接呼び出します。jQueryの書き方に従う
これを実現するには、jQuery をクラスとみなし、$() がクラスのインスタンスを返す必要があります
コードを変更します:
new aQuery() を使用すると、インスタンスが返されますが、無限ループという明らかな問題が依然として存在します。
では、正しいインスタンスを返すにはどうすればよいでしょうか?
JavaScript のインスタンス this はプロトタイプにのみ関連します
その後、jQuery クラスをファクトリ メソッドとして使用してインスタンスを作成し、このメソッドを jQuery.prototye プロトタイプに配置できます
aQuery() の実行時に返されるインスタンス:
明らかに、aQuery() は aQuery クラスのインスタンスを返すため、init の this は実際には aQuery クラスのインスタンスを指します
問題は、init の this が aQuery クラスを指していることです。init 関数がコンストラクターとしても使用されている場合、内部の this をどのように処理すればよいでしょうか。
aQuery().age //18
この場合、これは aQuery クラスのみを指しているため、何か問題が発生します。そのため、独立したスコープを設計する必要があります
JQuery フレームワークの個別スコープ処理
明らかに、インスタンス init 関数を通じて、これを分離し、相互作用の混乱を避けるために、毎回新しい init インスタンス オブジェクトが構築されます
したがって、これらは同じオブジェクトではないため、新たな問題が発生するはずです
例:
//Uncaught TypeError: オブジェクト [object Object] にはメソッド 'name' がありません
console.log(aQuery().name())
エラーがスローされ、このメソッドが見つからないため、new の init が jquery クラスの this から分離されていることは明らかです
jQuery クラス プロトタイプのプロパティとメソッドにアクセスするにはどうすればよいですか?
スコープを分離するだけでなく、jQuery プロトタイプ オブジェクトのスコープを使用するにはどうすればよいですか?
返されたインスタンス内の jQuery プロトタイプ オブジェクトにもアクセスできますか?実装の重要なポイント
プロトタイプを渡すことで問題を解決し、jQuery プロトタイプを jQuery.prototype.init.prototype に渡します
言い換えると、jQuery のプロトタイプ オブジェクトは、init コンストラクターのプロトタイプ オブジェクトをオーバーライドします
参照によって渡されるため、この循環参照のパフォーマンスの問題を心配する必要はありません
Baidu は、簡単かつ直接的に理解できるよう、ネチズンから画像を借用しました:
fn について説明します。実際、この fn には特別な意味はなく、単なる jQuery.prototype への参照です
分析 2: チェーンコール
DOM チェーン呼び出しの処理:
1. JS コードを保存します。
2. 同じオブジェクトが返されるため、コードの効率が向上します
プロトタイプ メソッドを拡張してこれを返すだけで、クロスブラウザー チェーン呼び出しを実現できます。
JS で単純なファクトリ パターンを使用して、同じ DOM オブジェクトに対するすべての操作に対して同じインスタンスを指定します。
この原則は非常にシンプルです
コードを分解すると、チェーンを実現するための基本的な条件はインスタンス this の存在であり、それは同じであることがわかります。
したがって、連鎖メソッドでこれにアクセスするだけで済みます。これは、現在のインスタンスの this を返すため、独自のプロトタイプにアクセスできます
利点: コードの量を節約し、コードの効率を向上させ、コードの見た目がよりエレガントになります
最悪の点は、すべてのオブジェクト メソッドがオブジェクト自体を返すことです。これは戻り値がないことを意味しており、これはどの環境にも適さない可能性があります。
JavaScript はノンブロッキング言語であるため、ブロッキングはしませんが、ブロックすることはできません。そのため、イベントによって駆動され、プロセスをブロックする必要がある一部の操作を非同期で完了する必要があります。この処理は単なる同期チェーンです。非同期チェーン jquery Promise は 1.5 から導入されており、jQuery.Deferred については後で説明します。
分析 3: プラグイン インターフェイス
jQuery の主なフレームワークは次のとおりですが、一般的なデザイナーの習慣によれば、jQuery または jQuery プロトタイプに属性メソッドを追加する場合、および開発者にメソッド拡張を提供する場合は、カプセル化の観点から見ると、インターフェイスは正しいものであり、フレンドリーなユーザー インターフェイスを直接変更するのではなく、文字通りに理解できます。
jQuery は独自の拡張プロパティをサポートし、オブジェクトにメソッドを追加するための外部インターフェイス jQuery.fn.extend() を提供します。
jQuery ソース コードからわかるように、jQuery.extend と jQuery.fn.extend は実際には同じメソッドを指す異なる参照です
jQuery.fn.extend は、jQuery.fn
jQuery.extend = jQuery.fn.extend = function(){...}; これは連続しています、つまり、同じ関数を指している 2 つの点ですが、どのようにして異なる関数を実現できるのでしょうか?これがこの力だ!
前に説明したように、Fn と jQuery は実際には 2 つの異なるオブジェクトです。
jQuery.extend が呼び出されると、これは jQuery オブジェクト (jQuery は関数でありオブジェクトです!) を指すため、ここでの拡張は jQuery 上にあります。
jQuery.fn.extend が呼び出されるとき、これは fn オブジェクトを指し、jQuery.fn と jQuery.prototype は同じオブジェクトを指します。fn を拡張すると、jQuery.prototype プロトタイプ オブジェクトが拡張されます。
ここで追加するのがプロトタイプメソッド、つまりオブジェクトメソッドです。そこで、jQuery APIでは上記2つの拡張機能を提供しています。
// Handle a deep copy situation
if ( typeof target === "boolean" ) { // 如果第一个参数为true,即 jQuery.extend( true, obj1, obj2 ); 的情况
deep = target; // 此时target是true
target = arguments[1] || {}; // target改为 obj1
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) { // 处理奇怪的情况,比如 jQuery.extend( 'hello' , {nick: 'casper})~~
target = {};
}
// extend jQuery itself if only one argument is passed
if ( length === i ) { // 处理这种情况 jQuery.extend(obj),或 jQuery.fn.extend( obj )
target = this; // jQuery.extend时,this指的是jQuery;jQuery.fn.extend时,this指的是jQuery.fn
--i;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) { // 比如 jQuery.extend( obj1, obj2, obj3, ojb4 ),options则为 obj2、obj3...
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) { // 防止自引用,不赘述
continue;
}
// Recurse if we're merging plain objects or arrays
// 如果是深拷贝,且被拷贝的属性值本身是个对象
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) { // 被拷贝的属性值是个数组
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else { 被拷贝的属性值是个plainObject,比如{ nick: 'casper' }
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// 元のオブジェクトは決して移動せず、クローンを作成してください
Target [name] = jquery.extEnd (deep, clone, copy) // 再帰 ~
}
}
}
}
// 変更されたオブジェクトを返します
return target;
概要:
プロトタイプ ポインターのポインティングを変更することで、この新しいオブジェクトがプロトタイプのプロトタイプも指すようにします。 jQuery クラスのプロトタイプ
したがって、この方法で構築されたオブジェクトは、jQuery.fn プロトタイプで定義されたすべてのメソッドを継続します。