詳解jQuery構(gòu)造器的實(shí)現(xiàn)
2009-01-13發(fā)布的1.3版
- init: function( selector, context ) {
- // Make sure that a selection was provided
- selector = selector || document;
- // 處理節(jié)點(diǎn)參數(shù),直接添加屬性到新實(shí)例上
- if ( selector.nodeType ) {
- this[0] = selector;
- this.length = 1;
- this.context = selector;
- return this;
- }
- // 處理字符串參數(shù)
- if ( typeof selector === "string" ) {
- // 判定是否為HTML片斷還是ID
- var match = quickExpr.exec( selector );
- if ( match && (match[1] || !context) ) {
- // 如果是HTML片斷,轉(zhuǎn)換一個由節(jié)點(diǎn)構(gòu)造的數(shù)組
- if ( match[1] )
- selector = jQuery.clean( [ match[1] ], context );
- // 如果是ID,則查找此元素,如果找到放進(jìn)空數(shù)組中
- else {
- var elem = document.getElementById( match[3] );
- // Make sure an element was located
- if ( elem ){
- // 處理 IE and Opera 混淆ID與NAME的bug
- if ( elem.id != match[3] )
- return jQuery().find( selector );
- var ret = jQuery( elem );
- ret.context = document;
- ret.selector = selector;
- return ret;
- }
- selector = [];
- }
- } else
- //使用Sizzle處理其他CSS表達(dá)式,生成實(shí)例并返回
- return jQuery( context ).find( selector );
- // 處理函數(shù)參數(shù),直接domReady
- } else if ( jQuery.isFunction( selector ) )
- return jQuery( document ).ready( selector );
- //處理jQuery對象參數(shù),簡單地將其兩個屬性賦給新實(shí)例
- if ( selector.selector && selector.context ) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
- //將上面得到節(jié)點(diǎn)數(shù)組,用setArray方法把它們變成實(shí)例的元素
- return this.setArray(jQuery.makeArray(selector));
- },
2009-02-19發(fā)布的1.32版
- init: function( selector, context ) {
- // Make sure that a selection was provided
- selector = selector || document;
- // 處理節(jié)點(diǎn)參數(shù),直接添加屬性到新實(shí)例上
- if ( selector.nodeType ) {
- this[0] = selector;
- this.length = 1;
- this.context = selector;
- return this;
- }
- //處理字符串參數(shù)
- if ( typeof selector === "string" ) {
- //判定是否為HTML片斷還是ID
- var match = quickExpr.exec( selector );
- if ( match && (match[1] || !context) ) {
- // 如果是HTML片斷,轉(zhuǎn)換一個由節(jié)點(diǎn)構(gòu)造的數(shù)組
- if ( match[1] )
- selector = jQuery.clean( [ match[1] ], context );
- else {
- var elem = document.getElementById( match[3] );
- // 如果是ID,則查找此元素,如果找到放進(jìn)空數(shù)組中
- if ( elem && elem.id != match[3] )
- return jQuery().find( selector );
- //這里對1.3版做了些優(yōu)化,更簡潔
- var ret = jQuery( elem || [] );
- ret.context = document;
- ret.selector = selector;
- return ret;
- }
- } else
- //使用Sizzle處理其他CSS表達(dá)式,生成實(shí)例并返回
- return jQuery( context ).find( selector );
- // 處理函數(shù)參數(shù),進(jìn)行domReady操作
- } else if ( jQuery.isFunction( selector ) )
- return jQuery( document ).ready( selector );
- //處理jQuery對象參數(shù),簡單地將其兩個屬性賦給新實(shí)例
- if ( selector.selector && selector.context ) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
- //這里對1.3版做了些擴(kuò)展,允許傳珍上元素集合(HTMLCollection)與節(jié)點(diǎn)集合(NodeList),
- //元素數(shù)組可能是我們用字符串轉(zhuǎn)換過來的,也可以是用戶直接傳進(jìn)來的
- return this.setArray(jQuery.isArray( selector ) ? selector : jQuery.makeArray(selector));
- },
2010-01-13發(fā)布的1.4版
- init: function( selector, context ) {
- var match, elem, ret, doc;
- //處理空白字符串,null,undefined參數(shù)(新增),返回一個非常純凈的實(shí)例
- if ( !selector ) {
- return this;
- }
- // 處理節(jié)點(diǎn)參數(shù),直接添加屬性到新實(shí)例上
- if ( selector.nodeType ) {
- this.context = this[0] = selector;//寫法上優(yōu)化
- this.length = 1;
- return this;
- }
- //處理字符串參數(shù)
- if ( typeof selector === "string" ) {
- // 判定是否為HTML片斷還是ID
- match = quickExpr.exec( selector );
- if ( match && (match[1] || !context) ) {
- //如果是HTML片斷
- if ( match[1] ) {
- //取得文檔對象
- doc = (context ? context.ownerDocument || context : document);
- // 如果是單個標(biāo)簽,直接使用 document.createElement創(chuàng)建此節(jié)點(diǎn)并放入數(shù)組中
- ret = rsingleTag.exec( selector );
- if ( ret ) {
- //如果后面跟著一個純凈的JS對象,則為此節(jié)點(diǎn)添加相應(yīng)的屬性或樣式
- if ( jQuery.isPlainObject( context ) ) {
- selector = [ document.createElement( ret[1] ) ];
- jQuery.fn.attr.call( selector, context, true );
- } else {
- selector = [ doc.createElement( ret[1] ) ];
- }
- } else {
- //改由buildFragment來生成節(jié)點(diǎn)集合(NodeList)
- ret = buildFragment( [ match[1] ], [ doc ] );
- selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
- }
- } else {
- // 如果是ID,則查找此元素,如果找到放進(jìn)空數(shù)組中
- elem = document.getElementById( match[2] );
- if ( elem ) {
- // 處理 IE and Opera 混淆ID與NAME的bug
- if ( elem.id !== match[2] ) {
- return rootjQuery.find( selector );
- }
- //這里也做了一些優(yōu)化,原來是很傻地再生成一個jQuery實(shí)例
- this.length = 1;
- this[0] = elem;
- }
- this.context = document;
- this.selector = selector;
- return this;
- }
- // 如果字符是很簡單的標(biāo)簽選擇器,那基本沒有必要走Sizzle路線,直接getElementsByTagName,很好的優(yōu)化
- } else if ( !context && /^\w+$/.test( selector ) ) {
- this.selector = selector;
- this.context = document;
- selector = document.getElementsByTagName( selector );
- // 如果第二個參數(shù)不存在或者是jQuery對象,那么用它或rootjQuery調(diào)用find查找目標(biāo)節(jié)點(diǎn)(走Sizzle路線)
- } else if ( !context || context.jquery ) {
- return (context || rootjQuery).find( selector );
- // HANDLE: $(expr, context)
- // (which is just equivalent to: $(context).find(expr)
- } else {
- //如果第二個參數(shù)已指定為某元素節(jié)點(diǎn),轉(zhuǎn)為jQuery對象,走Sizzle路線
- return jQuery( context ).find( selector );
- }
- // 處理函數(shù)參數(shù),直接domReady
- } else if ( jQuery.isFunction( selector ) ) {
- return rootjQuery.ready( selector );
- }
- //處理jQuery對象參數(shù),簡單地將其兩個屬性賦給新實(shí)例
- if (selector.selector !== undefined) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
- //這里又做了些許修改,緣于makeArray可以接受第二個參數(shù)(可以是數(shù)組或類數(shù)組,這時相當(dāng)合并操作)
- return jQuery.isArray( selector ) ?
- this.setArray( selector ) ://內(nèi)部用push方法,迅速將一個普通對象變成類數(shù)組對象
- jQuery.makeArray( selector, this );
- },
接著是廣受歡迎的2010-02-13發(fā)布的1.42版
- init: function( selector, context ) {
- var match, elem, ret, doc;
- // 處理空白字符串,null,undefined參數(shù)
- if ( !selector ) {
- return this;
- }
- // 處理節(jié)點(diǎn)參數(shù)
- if ( selector.nodeType ) {
- this.context = this[0] = selector;
- this.length = 1;
- return this;
- }
- // 處理body參數(shù)(新增)
- if ( selector === "body" && !context ) {
- this.context = document;
- this[0] = document.body;
- this.selector = "body";
- this.length = 1;
- return this;
- }
- // 處理字符串參數(shù),分七種情形:
- //①單個標(biāo)簽,帶對象屬性包 ---> jQuery.merge
- //②單個標(biāo)簽,不帶對象屬性包 ---> attr + jQuery.merge
- //③復(fù)雜的HTML片斷 ---> buildFragment + jQuery.merge
- //④ID選擇器,與找到的元素的ID不同 ---> getElementById + Sizzle + pushStack
- //⑤ID選擇器,與找到的元素的ID相同 ---> getElementById + 簡單屬性添加
- //⑥標(biāo)簽選擇器 ---> getElementsByTagName + jQuery.merge
- //⑦其他CSS表達(dá)式 ---> Sizzle + pushStack
- if ( typeof selector === "string" ) {
- match = quickExpr.exec( selector );
- if ( match && (match[1] || !context) ) {
- if ( match[1] ) {
- doc = (context ? context.ownerDocument || context : document);
- ret = rsingleTag.exec( selector );
- if ( ret ) {
- if ( jQuery.isPlainObject( context ) ) {
- selector = [ document.createElement( ret[1] ) ];
- jQuery.fn.attr.call( selector, context, true );
- } else {
- selector = [ doc.createElement( ret[1] ) ];
- }
- } else {
- ret = buildFragment( [ match[1] ], [ doc ] );
- selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
- }
- return jQuery.merge( this, selector );
- } else {
- elem = document.getElementById( match[2] );
- if ( elem ) {
- if ( elem.id !== match[2] ) {
- return rootjQuery.find( selector );
- }
- this.length = 1;
- this[0] = elem;
- }
- this.context = document;
- this.selector = selector;
- return this;
- }
- } else if ( !context && /^\w+$/.test( selector ) ) {
- this.selector = selector;
- this.context = document;
- selector = document.getElementsByTagName( selector );
- return jQuery.merge( this, selector );
- } else if ( !context || context.jquery ) {
- return (context || rootjQuery).find( selector );
- } else {
- return jQuery( context ).find( selector );
- }
- // 處理函數(shù)參數(shù),直接domReady
- } else if ( jQuery.isFunction( selector ) ) {
- return rootjQuery.ready( selector );
- }
- //處理jQuery對象參數(shù)
- if (selector.selector !== undefined) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
- //無論是數(shù)組還是類數(shù)組(如NodeList),統(tǒng)統(tǒng)使用jQuery.makeArray來為實(shí)例添加新的元素
- return jQuery.makeArray( selector, this );
- },
另附上makeArray方法與merge方法,merge方法好神奇啊
- makeArray: function( array, results ) {
- var ret = results || [];
- if ( array != null ) {
- // The window, strings (and functions) also have 'length'
- // The extra typeof function check is to prevent crashes
- // in Safari 2 (See: #3039)
- if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
- push.call( ret, array );
- } else {
- jQuery.merge( ret, array );
- }
- }
- return ret;
- },
- merge: function( first, second ) {
- var i = first.length, j = 0;
- if ( typeof second.length === "number" ) {
- for ( var l = second.length; j < l; j++ ) {
- first[ i++ ] = second[ j ];
- }
- } else {
- while ( second[j] !== undefined ) {
- first[ i++ ] = second[ j++ ];
- }
- }
- first.length = i;
- return first;
- },
2011-01-23發(fā)布的1.5版,其init方法與1.42的變化不大:只有兩處做了改動:
- //1.42
- - ret = buildFragment( [ match[1] ], [ doc ] );
- - selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
- //1.5
- + ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
- + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
- //1.42
- - return jQuery( context ).find( selector );
- //1.5
- + return this.constructor( context ).find( selector );//目的就是為了不再生成新實(shí)例
2011-05-02發(fā)布的jquery1.6,變化不大,只是對HTML片斷進(jìn)行了更嚴(yán)密的判定:
- // Are we dealing with HTML string or an ID?
- if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
- // Assume that strings that start and end with <> are HTML and skip the regex check
- match = [ null, selector, null ];
- } else {
- match = quickExpr.exec( selector );
- }
總體來說,jQuery的構(gòu)造器已經(jīng)做得非常之***,基本上達(dá)到“改無可改”的地步了。但是要保證其高效運(yùn)作,我們還需要一點(diǎn)選擇器的知識與了解buildFragment方法的運(yùn)作,因?yàn)檫@兩個實(shí)在太常用了,但也是最耗性能的。
原文鏈接:http://www.cnblogs.com/rubylouvre/archive/2011/05/15/2046593.html
【編輯推薦】