1. 程式人生 > >jquery 1.7.2原始碼解析(二)構造jquery物件

jquery 1.7.2原始碼解析(二)構造jquery物件

構造jquery物件

jQuery物件是一個陣列物件。

一)建構函式jQuery()

建構函式的7種用法:

 

1.jQuery(selector [, context ])

傳入字串引數:檢查該字串是選擇器表示式還是HTML程式碼。如果是選擇器表示式,則遍歷文件查詢匹配的DOM元素,

並建立一個包含這些DOM元素引用的jQuery物件。如果沒有匹配的DOM,則建立一個length屬性為0的空jQuery物件。

預設情況下對匹配元素的查詢從根元素document開始,但也可以傳入context引數限制查詢範圍。

如果選擇器表示式是簡單的'#id'且沒有context引數,則呼叫document.getElementById()查詢。

如果不是則呼叫jQuery的find()方法。

2.jQuery(html [, ownerDocument])、jQuery(html, props)

如果傳入的引數是html程式碼,則嘗試用html程式碼建立新的DOM元素,並建立包含這些DOM元素引用的jQuery物件。

如果html程式碼是單獨的標籤,使用document.createElement()建立DOM元素。如果是複雜的html程式碼,則使用innerHTML.

引數ownerDocument用於建立新DOM元素的文件物件,如果不傳入預設為當前文件物件。

如果html程式碼是單獨標籤,第二個引數還可以是props,props是一個包含了屬性、事件的普通物件,在呼叫了document.createElement()

方法建立了DOM元素後,引數props會被傳給jQuery的attr()方法,由該方法設定新DOM的屬性,事件。

$('<div/>', {
   "class": "test",
    text: "Click me!",
    click: function () {
        console.log("log");
    }
});

3.jQuery(element)、jQuery(elementArray)

如果傳入一個DOM元素或者DOM元素陣列,則封裝DOM元素到jQuery物件種,並返回該jQuery物件。

$('#aInput').click(function
() { $(this).val("value"); });

4.jQuery(object)

傳入一個普通的js物件,則把該物件封裝到jQuery物件中,並返回jQuery物件。

這個功能可以方便地在普通js物件上實現自定事件的繫結和觸發。

var person = {name: 'Tony', gender: 'man'};
var $person = $(person);
$person.on('custom', function () {
    //do sth
});

5.jQuery(callback)

如果傳入一個函式,則在document上繫結一個ready事件監聽函式,當DOM結構載入完成時執行。

注意:ready事件要早於load事件發生。

6.jQuery(jQuery object)

建立一個副本並返回。

二)總體結構

jQuery物件模組總體結構:

(function ( window, undefined ) {
    //構造jQuery物件
    var jQuery = (function () {
        var jQuery = function (selector,context) {
                return new jQuery.fn.init(selector, context, rootjQuery);
            },

            //一堆區域性變數宣告

         //jQuery.fn成為jQuery.prototype的簡寫
        //覆蓋jQuery原型的目的:方便繼承,順便也減少了建立每個jQuery例項所消耗的記憶體
        jQuery.fn = jQuery.prototype = {
            constructor: jQuery,
            init: function (selector, context, rootjQuery) {},
           //一堆原型屬性和方法
        };

        //用jQuery的原型物件覆蓋jQuery.fn.init.prototype的原型物件
        //使 new jQuery.fn.init()返回的例項也可以建構函式jQuery()的原型方法和屬性
        jQuery.fn.init.prototype = jQuery.fn;
        jQuery.extend = jQuery.fn.extend = function () {};
        jQuery.extend({
            //一堆靜態屬性和方法
        });
        return jQuery;
    })();

    //省略其他模組程式碼
    window.jQuery = window.$ = jQuery;
})(window);

三)jQuery.fn.init(selector, context, rootjQuery)

1.12個分支

 

 2.原始碼分析

 1)定義jQuery.fn.init(selector, context, rootjQuery):

jQuery.fn = jQuery.prototype = {
    constructor: jQuery,
    init: function( selector, context, rootjQuery ) {
        var match, elem, ret, doc;

建構函式接收三個引數:

selector:

context: 不傳入,或者傳入DOM元素,jQuery物件,普通js物件之一。

rootjQuery: 包含了document物件的jQuery物件。用於:

1.document.getElementById()查詢失敗

2.selector是選擇器表示式且未指定context

3.selector是函式

2)引數selector可以轉換為false

// Handle $(""), $(null), or $(undefined)
        if ( !selector ) {
            return this;
        }

3)引數selector是DOM元素

 

// Handle $(DOMElement)
        if ( selector.nodeType ) {
            this.context = this[0] = selector;
            this.length = 1;
            return this;
        }

 

4)引數selector是字串'body'

// The body element only exists once, optimize finding it
        if ( selector === "body" && !context && document.body ) {
            this.context = document;
            this[0] = document.body;
            this.selector = selector;
            this.length = 1;
            return this;
        }

5)引數selector是其他字串

先檢測selector是HTML程式碼還是#id

        // Handle HTML strings
        if ( typeof selector === "string" ) {
            // 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 );
            }

 

1.引數selector是單獨標籤

則呼叫document.createElement()建立DOM元素:

//如果是字串
                if (typeof selector === 'string') {
                    //如果是HTML字串
                    if (selector.charAt(0) === '<' && selector.charAt(length-1) === '>' && selector.length >= 3) {
                        match = [null, selector, null];
                    } else {
                        match = quickExpr.exec(selector);
                    }
                }

                if (match && (match[1] || !context)) {
                    //HANDLE: $(html) -> $(array)
                    if (match[1]) {
                        context = context instanceof jQuery ? context[0] : context;
                        doc = (context ? context.ownerDocument || context : document);

                        //如果只傳入了一個字串,並且是個簡單的html標籤
                        //就呼叫createElement,跳過剩餘部分。
                        ret = rsingleTag.exec( selector );
                        if (ret) {
                            //是否是純粹物件
                            if ( jQuery.isPlainObject( context ) ) {
                                selector = [document.createElement(ret[1])];
                                jQuery.fn.attr.call( selector, context, true );
                            } else {
                                selector = [document.createElement(ret[1])];
                            }

                        } else {
                            ret = jQuery.buildFragment( match[1], [doc] );

                        }

2.引數selector是複雜HTML程式碼

} else {
        ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
        selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
    }
    //將建立的DOM元素合併到當前jQuery物件中,並返回
    return jQuery.merge( this, selector );

 3.引數selector是"#id",且未指定引數context

                // HANDLE: $("#id")
                } else {
                    elem = document.getElementById( match[2] );
                    // Check parentNode to catch when Blackberry 4.6 returns
                    // nodes that are no longer in the document #6963
                    if ( elem && elem.parentNode ) {
                        // Handle the case where IE and Opera return items
                        // by name instead of ID
                        if ( elem.id !== match[2] ) {
                            return rootjQuery.find( selector );
                        }

                        // Otherwise, we inject the element directly into the jQuery object
                        this.length = 1;
                        this[0] = elem;
                    }

                    this.context = document;
                    this.selector = selector;
                    return this;
                }

4.引數selector是選擇器表示式

            // HANDLE: $(expr, $(...))
            } else if ( !context || context.jquery ) {
                return ( context || rootjQuery ).find( selector );

            // HANDLE: $(expr, context)
            // (which is just equivalent to: $(context).find(expr)
            } else {
                return this.constructor( context ).find( selector );
            }

5.引數selector是函式

// HANDLE: $(function)
        // Shortcut for document ready
        } else if ( jQuery.isFunction( selector ) ) {
            return rootjQuery.ready( selector );
        }

6.引數selector是jQuery物件

if ( selector.selector !== undefined ) {
            this.selector = selector.selector;
            this.context = selector.context;
        }

7.引數selector是其他任意值

如果selector是陣列或者偽陣列,則都新增到jQuery物件中,如果是JS

物件則作為第一個元素放入。該方法也可將6)中selector包含的全部元素引用複製到當前jQuery

 

return jQuery.makeArray( selector, this );

 

四)jQuery.buildFragment(args, nodes, scripts)

1.實現原理

該方法會先建立一個文件片段DocumentFragment,然後呼叫方法jQuery.clean(elems, context, fragment, scripts)

將HTML程式碼轉換為HTML元素,並存儲在建立的DocumentFragment中。

此外,如果HTML程式碼符合快取條件,該方法還會把轉換後的DOM快取起來,第三次轉換相同的HTML程式碼時直接從快取中讀取。

2.原始碼分析

1)引數:

args:陣列,含有待轉換為DOM元素的html程式碼

nodes:陣列,含有文件物件、jQuery物件或DOM元素,用於修正建立文件片段DocumentFragment的文件物件。

scripts:陣列,用於存放HTML程式碼中的script元素。

2)定義區域性變數,修正文件物件doc

 

*
* fragment:指向建立的DocumentFragment
* cacheable: 是否符合快取條件
* cacheresult:指向從快取物件,jQuery.fragments提取到的文件片段,其中包含了快取的DOM元素
* doc:建立文件片段的文件物件
* */
var fragment, cacheable, cacheresults, doc,
    first = args[ 0 ];

// nodes may contain either an explicit document object,
// a jQuery collection or context object.
// If nodes[0] contains a valid object to assign to doc
if ( nodes && nodes[0] ) {
    doc = nodes[0].ownerDocument || nodes[0];
}

// Ensure that an attr object doesn't incorrectly stand in as a document object
// Chrome and Firefox seem to allow this to occur and will throw exception
// Fixes #8950
if ( !doc.createDocumentFragment ) {
    doc = document;
}

 

3)其他操作

// Only cache "small" (1/2 KB) HTML strings that are associated with the main document
// Cloning options loses the selected state, so don't cache them
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
// Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
    first.charAt(0) === "<" && !rnocache.test( first ) &&
    (jQuery.support.checkClone || !rchecked.test( first )) &&
    (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {

    cacheable = true;

    cacheresults = jQuery.fragments[ first ];
    if ( cacheresults && cacheresults !== 1 ) {
        fragment = cacheresults;
    }
}
//轉換HTML程式碼為DOM元素
if ( !fragment ) {
    fragment = doc.createDocumentFragment();
    jQuery.clean( args, doc, fragment, scripts );
}
//把轉換後的DOM元素放入快取物件jQuery.fragments中
if ( cacheable ) {
    jQuery.fragments[ first ] = cacheresults ? fragment : 1;
}
//返回文件片段和返回狀態
return { fragment: fragment, cacheable: cacheable };
};

jQuery.fragments = {};

五)jQuery.clean(elems, context, fragment, scripts)

1.實現原理

該方法負責把HTML程式碼轉換為DOM元素,並提取其中的script元素。該方法會先建立一個臨時

的div元素,並將其插入一個安全的文件片段(能正確渲染HTML5元素的文件片段)中,

然後把HTML程式碼賦值給div元素的innerHTML屬性,最後解析div元素的子元素得到轉換後的DOM。

2.原始碼分析

1)引數:

elems:陣列,包含了待轉換的html程式碼

context:文件物件

fragment:文件片段,存放轉換後的DOM元素

scripts:陣列,存放轉換後的DOM元素中的script元素

2)修正文件物件

//ret用於存放轉換後的DOM元素
var
checkScriptType, script, j, ret = []; context = context || document; // !context.createElement fails in IE with an error but returns typeof 'object' if ( typeof context.createElement === "undefined" ) { context = context.ownerDocument || context[0] && context[0].ownerDocument || document; }

3)遍歷elems陣列

//這裡使用"!=",可以同時過濾undefined,null的情況,但不會過濾掉0
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
    //如果elem是數字,則轉換為字串型別
    if (typeof elem === "number") {
        elem += "";
    }

    //如果elem是0,此時已經被轉換為字串“0”
    if (!elem) {
        continue;
    }

1.建立文字節點:

如果字串不包含標籤、字元程式碼和數字程式碼,則呼叫原生方法document.createTextNode()方法建立文字節點。

if ( !rhtml.test( elem ) ) {
                    elem = context.createTextNode( elem );
                } else {
rhtml = /<|&#?\w+;/,

為什麼不能包含標籤、字元程式碼和數字程式碼?

因為document.createTextNode()方法對於傳給它的字串不會做轉義解析。

而瀏覽器innerHTML機制可以。

2.修正自關閉標籤

else {
                    // Fix "XHTML"-style tags in all browsers
                    elem = elem.replace(rxhtmlTag, "<$1></$2>");
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,

3.建立一個臨時的div

// Trim whitespace, otherwise indexOf won't work as expected
                    var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
//從wrapMap提取對應的父標籤,因為HTML語法要求這些標籤必須包含在父標籤中 wrap
= wrapMap[ tag ] || wrapMap._default,
//包裹的深度,稍後會根據該變數剝去父元素 depth
= wrap[0], div = context.createElement("div"),
rtagName = /<([\w:]+)/,
wrapMap = {
        option: [ 1, "<select multiple='multiple'>", "</select>" ],
        legend: [ 1, "<fieldset>", "</fieldset>" ],
        thead: [ 1, "<table>", "</table>" ],
        tr: [ 2, "<table><tbody>", "</tbody></table>" ],
        td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
        col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
        area: [ 1, "<map>", "</map>" ],
        _default: [ 0, "", "" ]
    },

4.把臨時div插入安全文件中去

                    // Append wrapper element to unknown element safe doc fragment
                    if ( context === document ) {
                        // Use the fragment we've already created for this document
                        safeFragment.appendChild( div );
                    } else {
                        // Use a fragment created with the owner document
                        createSafeFragment( context ).appendChild( div );
                    }
safeFragment = createSafeFragment( document );
function createSafeFragment( document ) {
    var list = nodeNames.split( "|" ),
    safeFrag = document.createDocumentFragment();

    if ( safeFrag.createElement ) {
        while ( list.length ) {
            safeFrag.createElement(
                list.pop()
            );
        }
    }
    return safeFrag;
}

5.轉換html程式碼為DOM元素

// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];

// Move to the right depth
while ( depth-- ) {
    div = div.lastChild;
}

6.移除IE6\7自動插入的空tbody元素

                    // Remove IE's autoinserted <tbody> from table fragments
                    if ( !jQuery.support.tbody ) {

                        // String was a <table>, *may* have spurious <tbody>
                        var hasBody = rtbody.test(elem),
                            tbody = tag === "table" && !hasBody ?
                                div.firstChild && div.firstChild.childNodes :

                                // String was a bare <thead> or <tfoot>
                                wrap[1] === "<table>" && !hasBody ?
                                    div.childNodes :
                                    [];

                        for ( j = tbody.length - 1; j >= 0 ; --j ) {
                            if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
                                tbody[ j ].parentNode.removeChild( tbody[ j ] );
                            }
                        }
                    }

7)插入IE6\7\8自動刪除的前導空白

// IE completely kills leading whitespace when innerHTML is used
                    if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
                        div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
                    }

8)取到轉換後的DOM元素集合

elem = div.childNodes;

9)修正IE7、8中複選框和單選框選中問題

10)合併轉換後的DOM元素

if ( elem.nodeType ) {
                ret.push( elem );
            } else {
                ret = jQuery.merge( ret, elem );
            }

4)傳入了fragment的情況

 

if ( fragment ) {
            checkScriptType = function( elem ) {
                return !elem.type || rscriptType.test( elem.type );
            };
            for ( i = 0; ret[i]; i++ ) {
                script = ret[i];
                if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) {
                    scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script );

                } else {
                    if ( script.nodeType === 1 ) {
                        var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType );

                        ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
                    }
                    fragment.appendChild( script );
                }
            }
        }

        return ret;

 六)jQuery.extend()、jQuery.fn.extend()

1.如何使用

用於合併兩個或者多個物件的屬性到第一個物件,語法:

jQuery.extend([deep], target, object1[, objectN]);

jQuery.fn.extend([deep], target, object1[, objectN]);

deep:布林值,是否進行遞迴合併,預設為不遞迴

target:目標物件

objectN:源物件,包含的待合併的屬性

如果僅提供一個物件,引數target將被忽略,當前的jQuery或者jQuery.fn被當作目標物件

通過這種方式在jQuery或者jQuery.fn上新增新的屬性或者方法。

2.原始碼分析

1)定義區域性變數

jQuery.extend = jQuery.fn.extend = function() {
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

options:指向某個源物件

name:指向某個源物件的某個屬性名

src:某個目標物件的某個屬性原始值

copy:某個源物件的某個屬性值

copyIsArray:指示變數copy是否是陣列

clone:深度複製時,原始值的修正值

target:指向目標物件

i:源物件的起始下標

length:表示引數的個數,用於修正變數target

deep:是否深度合併,預設為false

jQuery.extend = jQuery.fn.extend = function() {
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;
        target = arguments[1] || {};
        // 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) ) {
        target = {};
    }

    // extend jQuery itself if only one argument is passed
    if ( length === i ) {
        target = this;
        --i;
    }

    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {
            // 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 {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );

                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
};

七)原型屬性和方法

1. .selector、.jQuery、.length、.size()

1)selector:用於記錄jQuery查詢和過濾DOM元素時的選擇器表示式,可用於除錯。

2)jQuery:表示jQuery的版本號

3)length:表示當前jQuery物件中元素的個數

4)方法size():功能上等價於length

    // Start with an empty selector
    selector: "",

    // The current version of jQuery being used
    jquery: "1.7.2",

    // The default length of a jQuery object is 0
    length: 0,

    // The number of elements contained in the matched element set
    size: function() {
        return this.length;
    },

2..toArray()、.get([index])

1.toArray():將當前jQuery物件轉換為真正的陣列,轉換後的陣列包含了所有元素。

slice = Array.prototype.slice,
toArray: function() {
        return slice.call( this, 0 );
    },

2.get([index]):返回當前jQuery物件中指定位置的元素或包含了全部元素的陣列(無參)。

    get: function( num ) {
        return num == null ?

            // Return a 'clean' array
            this.toArray() :

            // Return just the object
            ( num < 0 ? this[ this.length + num ] : this[ num ] );
    },

3. .each( function(index, Elment) )、 jQuery.each( collection, callback( indexArray, valueOfValue ) )

1. .each( function(index, Elment) )

遍歷當前jQuery物件,並在每個元素上執行回撥函式。每當執行回撥函式

 函式執行時會傳遞當前迴圈次數作為引數,更重要的是回撥函式在當前

上下文環境中觸發,在回撥函式中返回false可以終止遍歷。

each: function( callback, args ) {
        return jQuery.each( this, callback, args );
    },

2.jQuery.each( collection, callback ( indexArray, valueOfValue ) )

通用的遍歷方法,用於無縫地遍歷物件和陣列,對於陣列和類陣列通過下標

遍歷,對於其他物件則通過屬性名遍歷。在遍歷過程中如果回撥函式返回false則結束遍歷。

 

/* *
 * object:待遍歷的物件或者陣列
 * callback: 回撥函式,會在陣列的每個元素或者物件的每個屬性上執行
 * args: 傳遞給回撥函式的引數陣列,可選,如果沒有傳入,則執行回撥
 * 函式時傳入兩個引數(下標或屬性名,元素或屬性值),如果傳入了引數
 * 則把該引數傳遞給回撥函式。
 *
 * */
each: function( object, callback, args ) {
    var name, i = 0,
        length = object.length,
        isObj = length === undefined || jQuery.isFunction( object );

    if ( args ) {
        if ( isObj ) {
            for ( name in object ) {
                if ( callback.apply( object[ name ], args ) === false ) {
                    break;
                }
            }
        } else {
            for ( ; i < length; ) {
                if ( callback.apply( object[ i++ ], args ) === false ) {
                    break;
                }
            }
        }

        // A special, fast, case for the most common use of each
    } else {
        if ( isObj ) {
            for ( name in object ) {
                if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
                    break;
                }
            }
        } else {
            for ( ; i < length; ) {
                if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
                    break;
                }
            }
        }
    }

    return object;
},

 

4. .map( callback(index, domElement) )、 jQuery.map( arrayOrObject, callback(value, indexOrKey) )

1) .map( callback(index, domElement) )

遍歷當前jQuery物件,在每個元素上執行回撥函式,並將回撥函式的返回值放入一個新的jQuery物件中。

map: function( callback ) {
        return this.pushStack( jQuery.map(this, function( elem, i ) {
            return callback.call( elem, i, elem );
        }));
    },

2) jQuery.map( arrayOrObject, callback(value, indexOrKey) )

對陣列的每個元素或者物件的每個屬性呼叫一個回撥函式,並將回撥函式的返回值放入一個新的陣列中。

執行回撥函式時傳入兩個引數:陣列元素或屬性值,元素下標或屬性名。關鍵字this指向全域性物件window。

//引數arg僅限於jQuery內部使用
map: function( elems, callback, arg ) {
    var value, key, ret = [],
        i = 0,
        length = elems.length,
        // jquery objects are treated as arrays
        isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;

    // Go through the array, translating each of the items to their
    if ( isArray ) {
        for ( ; i < length; i++ ) {
            value = callback( elems[ i ], i, arg );

            if ( value != null ) {
                ret[ ret.length ] = value;
            }
        }

        // Go through every key on the object,
    } else {
        for ( key in elems ) {
            value = callback( elems[ key ], key, arg );

            if ( value != null ) {
                ret[ ret.length ] = value;
            }
        }
    }

    // Flatten any nested arrays
    return ret.concat.apply( [], ret );
},

5. .pushStack( elements, name, arguments )

建立一個新的空jQuery物件,然後把DOM物件元素集合放入物件中,並保留對當前jQuery物件的引用。

//elems: 放入新jQuery物件的元素陣列
//name: 產生元素陣列的jQuery方法名
//修正原型屬性的.selector
pushStack: function( elems, name, selector ) {
    // Build a new jQuery matched element set
    var ret = this.constructor();

    if ( jQuery.isArray( elems ) ) {
        push.apply( ret, elems );

    } else {
        jQuery.merge( ret, elems );
    }

    // Add the old object onto the stack (as a reference)
    ret.prevObject = this;

    ret.context = this.context;

    if ( name === "find" ) {
        ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
    } else if ( name ) {
        ret.selector = this.selector + "." + name + "(" + selector + ")";
    }

    // Return the newly-formed element set
    return ret;
},

6.  .end()

 

end: function() {
        return this.prevObject || this.constructor(null);
    },

 

7. .eq(index)、 .first()、 .last()、 .slice(start[, end])

    eq: function( i ) {
        i = +i;
        return i === -1 ?
            this.slice( i ) :
            this.slice( i, i + 1 );
    },

    first: function() {
        return this.eq( 0 );
    },

    last: function() {
        return this.eq( -1 );
    },

    slice: function() {
        return this.pushStack( slice.apply( this, arguments ),
            "slice", slice.call(arguments).join(",") );
    },

八)靜態屬性和方法

1. jQuery.onConflit([removeAll])

用於釋放jQuery對全域性變數的控制權

// Map over jQuery in case of overwrite
    _jQuery = window.jQuery,

// Map over the $ in case of overwrite
    _$ = window.$,

 

noConflict: function( deep ) {
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }

        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }

        return jQuery;
    },

2.型別檢測:jQuery.isFunction(obj)、 jQuery.isArray(obj)、jQuery.isWindow(obj)、

jQuery.isNumeric(value)、jQuery.type(obj)、jQuery.isPlainObject(object)、jQuery.isEmptyObject(object)

 

isFunction: function( obj ) {
    return jQuery.type(obj) === "function";
},

isArray: Array.isArray || function( obj ) {
    return jQuery.type(obj) === "array";
},

    isWindow: function( obj ) {
    return obj != null && obj == obj.window;
},

isNumeric: function( obj ) {
    return !isNaN( parseFloat(obj) ) && isFinite( obj );
},

type: function( obj ) {
    return obj == null ?
        String( obj ) :
        class2type[ toString.call(obj) ] || "object";
},

//是否是以{}或者new Object()建立的
isPlainObject: function( obj ) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
        return false;
    }

    try {
        // Not own constructor property must be Object
        //如果obj沒有屬性constructor,則說明該物件必然是通過物件字面量{}建立
        if ( obj.constructor &&
            !hasOwn.call(obj, "constructor") &&
            !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
            return false;
        }
    } catch ( e ) {
        // IE8,9 Will throw exceptions on certain host objects #9897
        return false;
    }

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.
    //在for迴圈中會先列舉非繼承屬性,再列舉繼承屬性
    var key;
    for ( key in obj ) {}

    return key === undefined || hasOwn.call( obj, key );
},

isEmptyObject: function( obj ) {
    for ( var name in obj ) {
        return false;
    }
    return true;
},

 

3.解析JSON和XML:jQuery.parseJSON(data)、 jQuery.parseXML(data)

1.jQuery.parseJSON(data)

    parseJSON: function( data ) {
        if ( typeof data !== "string" || !data ) {
            return null;
        }

        // Make sure leading/trailing whitespace is removed (IE can't handle it)
        data = jQuery.trim( data );

        // Attempt to parse using the native JSON parser first
        if ( window.JSON && window.JSON.parse ) {
            return window.JSON.parse( data );
        }

        // Make sure the incoming data is actual JSON
        // Logic borrowed from http://json.org/json2.js
        if ( rvalidchars.test( data.replace( rvalidescape, "@" )
            .replace( rvalidtokens, "]" )
            .replace( rvalidbraces, "")) ) {

            return ( new Function( "return " + data ) )();

        }
        jQuery.error( "Invalid JSON: " + data );
    },

2.jQuery.parseXML(data)

    parseXML: function( data ) {
        if ( typeof data !== "string" || !data ) {
            return null;
        }
        var xml, tmp;
        try {
            if ( window.DOMParser ) { // Standard
                tmp = new DOMParser();
                xml = tmp.parseFromString( data , "text/xml" );
            } else { // IE
                xml = new ActiveXObject( "Microsoft.XMLDOM" );
                xml.async = "false";
                xml.loadXML( data );
            }
        } catch( e ) {
            xml = undefined;
        }
        if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
            jQuery.error( "Invalid XML: " + data );
        }
        return xml;
    },

4.jQuery.globalEval( code )

用於在全域性作用域中執行javascript程式碼

 

    globalEval: function( data ) {
        if ( data && rnotwhite.test( data ) ) {
            // We use execScript on Internet Explorer
            // We use an anonymous function so that context is window
            // rather than jQuery in Firefox
            ( window.execScript || function( data ) {
                window[ "eval" ].call( window, data );
            } )( data );
        }
    },

 

5.jQuery.camelCase(string)

轉換連字元形式的字串為駝峰式

6.jQuery.nodeName(elem, name)

用於檢查節點名稱是否與指定值相等,檢查時忽略大小寫

nodeName: function( elem, name ) {
        return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
    },

 7. 陣列操作方法

1.jQuery.makeArray(obj)

將類陣列物件轉換為真正的陣列

// results is for internal usage only
    makeArray: function( array, results ) {
        var ret = results || [];

        if ( array != null ) {
            // The window, strings (and functions) also have 'length'
            // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
            var type = jQuery.type( array );

            if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
                push.call( ret, array );
            } else {
                jQuery.merge( ret, array );
            }
        }

        return ret;
    },

2. jQuery.inArray(value, array[, fromIndex])

在陣列中查詢指定的元素並返回其下標,未找到則返回-1

/*
* elem: 要查詢的值
* array: 被遍歷的陣列
* i: 指定開始查詢的位置
* */
inArray: function( elem, array, i ) {
    var len;

    if ( array ) {
        if ( indexOf ) {
            return indexOf.call( array, elem, i );
        }

        len = array.length;
        i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;

        for ( ; i < len; i++ ) {
            // Skip accessing in sparse arrays
            if ( i in array && array[ i ] === elem ) {
                return i;
            }
        }
    }

    return -1;
}

3. jQuery.merge(first, second)

用於合併兩個陣列的元素到第一個陣列。

第一個引數也可以時類陣列物件(即必須有整型屬性length值)。

第二個引數可以是陣列,類陣列或者有連續整型屬性的物件。

    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;
    },

4.jQuery.grep(array, function(elemOfArray, indexInArray)[, invert])

用於查詢陣列中滿足過濾函式的元素,原陣列不受影響。如果invert沒有傳入,或者傳入false.

元素只有在過濾函式返回true時,才會被儲存在最終的結果中。如果傳入true情況相反。

    grep: function( elems, callback, inv ) {
        var ret = [], retVal;
        inv = !!inv;

        // Go through the array, only saving the items
        // that pass the validator function
        for ( var i = 0, length = elems.length; i < length; i++ ) {
            retVal = !!callback( elems[ i ], i );
            if ( inv !== retVal ) {
                ret.push( elems[ i ] );
            }
        }

        return ret;
    },

8. jQuery.proxy()

    // Bind a function to a context, optionally partially applying any
    // arguments.
    proxy: function( fn, context ) {
        if ( typeof context === "string" ) {
            var tmp = fn[ context ];
            context = fn;
            fn = tmp;
        }

        // Quick check to determine if target is callable, in the spec
        // this throws a TypeError, but we will just return undefined.
        if ( !jQuery.isFunction( fn ) ) {
            return undefined;
        }

        // Simulated bind
        var args = slice.call( arguments, 2 ),
            proxy = function() {
                return fn.apply( context, args.concat( slice.call( arguments ) ) );
            };

        // Set the guid of unique handler to the same of original handler, so it can be removed
        proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;

        return proxy;
    },

9. jQuery.access()

為集合中的元素設定一個或者多個屬性值,或者讀取第一個元素的屬性值。