jquery1.7.2的源码分析(二)

jquery.extend

jQuery.extend = jQuery.fn.extend = function () {
var options, name, src, copy, copyIsArray, clone,
//target为传入的一个参数
    target = arguments[0] || {},
    i = 1,
//传入参数的个数
    length = arguments.length,
//首先默认为浅拷贝
    deep = false;
// Handle a deep copy situation
if (typeof target === "boolean") {
//当第一个参数是布尔值
deep = target;
//target第二个参数
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
//当target不是object并且不是function
if (typeof target !== "object" && !jQuery.isFunction(target)) {
target = {};
}
// extend jQuery itself if only one argument is passed
//当是一个参数的时候$.extend({ccc:function(){}})
if (length === i) {
//target为init对象
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;
};
};

jquery.extend的使用

jQuery.extend({
    noConflict: function( deep ) {
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }
        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }
        return jQuery;
    },
// Is the DOM ready to be used? Set to true once it occurs.
    isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
    readyWait: 1,
// Hold (or release) the ready event
    holdReady: function( hold ) {
        if ( hold ) {
            jQuery.readyWait++;
        } else {
            jQuery.ready( true );
        }
    },
// Handle when the DOM is ready
    ready: function( wait ) {
// Either a released hold or an DOMready/load event and not yet ready
//wait=true且jQuery.readyWait-1取非为true 或者wait为false jQuery.isReady为false
        if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
//当不存在body元素了,继续执行这个函数
            if ( !document.body ) {
                return setTimeout( jQuery.ready, 1 );
            }
// Remember that the DOM is ready
            jQuery.isReady = true;
// If a normal DOM Ready event fired, decrement, and wait if need be
            if ( wait !== true && --jQuery.readyWait > 0 ) {
                return;
            }
// If there are functions bound, to execute
            readyList.fireWith( document, [ jQuery ] );
// Trigger any bound ready events
            if ( jQuery.fn.trigger ) {
                jQuery( document ).trigger( "ready" ).off( "ready" );
            }
        }
    },
    bindReady: function() {
        console.log(readyList)
        if ( readyList ) {
            return;
        }
        readyList = jQuery.Callbacks( "once memory" );
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
        if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
            return setTimeout( jQuery.ready, 1 );
        }
// Mozilla, Opera and webkit nightlies currently support this event
        if ( document.addEventListener ) {
// Use the handy event callback
            document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
            window.addEventListener( "load", jQuery.ready, false );
// If IE event model is used
        } else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
            document.attachEvent( "onreadystatechange", DOMContentLoaded );
// A fallback to window.onload, that will always work
            window.attachEvent( "onload", jQuery.ready );
// If IE and not a frame
// continually check to see if the document is ready
            var toplevel = false;
            try {
                toplevel = window.frameElement == null;
            } catch(e) {}
            if ( document.documentElement.doScroll && toplevel ) {
                doScrollCheck();
            }
        }
    },
// See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
    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";
    },
//纯对象的判断
    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
            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.
        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;
    },
    error: function( msg ) {
        throw new Error( msg );
    },
    parseJSON: function( data ) {
//字符串转化为json
        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 );
    },
// Cross-browser xml parsing
    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;
    },
    noop: function() {},
// Evaluates a script in a global context
// Workarounds based on findings by Jim Driscoll
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
    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 );
        }
    },
// Convert dashed to camelCase; used by the css and data modules
// Microsoft forgot to hump their vendor prefix (#9572)
    camelCase: function( string ) {
        return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
    },
//例如当string为“-ms-aaaa” 得到的是msAaa;
//rmsPrefix = /^-ms-/,第一个replace结束后得到 ms-aaa
//rdashAlpha = /-([a-z]|[0-9])/ig,第二个replace 匹配了-a fcamelCase(all,letter,index)
//all为-a,letter为$1,为a,index=2 开始匹配的位置,从0开始
    nodeName: function( elem, name ) {
        return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
    },
// args is for internal usage only
    each: function( object, callback, args ) {
        var name, i = 0,
//有长度
                length = object.length,
//json对象的遍历
                isObj = length === undefined || jQuery.isFunction( object );
//args为真
        if ( args ) {
            if ( isObj ) {
                for ( name in object ) {
//举个例子:function add(b){console.log(b);}
//$.each({'aaa':{ccc:1,'bbb':222}},add,['aaa']);
//传递一个直接的参数,进行操作
                    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 ) {
//$.each([1,3,5,6],function(i,v){})
                    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;
    },
// Use native String.trim function wherever possible
    trim: trim ?
            function( text ) {
                return text == null ?
                        "" :
                        trim.call( text );
            } :
// Otherwise use our own trimming functionality
            function( text ) {
                return text == null ?
                        "" :
                        text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
            },
// 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;
    },
    inArray: function( elem, array, i ) {
        var len;
        if ( array ) {
            if ( indexOf ) {
                return indexOf.call( array, elem, i );
            }
//首先执行 i < 0 ? Math.max( 0, len + i ) : i
//如果不存在就从0开始,否则从当i<0时,len+i开始,>0直接从开始
            len = array.length;
            i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
            for ( ; i < len; i++ ) {
//返回所在位置
                if ( i in array && array[ i ] === elem ) {
                    return i;
                }
            }
        }
        return -1;
    },
    merge: function( first, second ) {
//jQuery.merge( ret, array );
//传入的是init,和一个array
        console.log(first);
        console.log(second)
        var i = first.length,
                j = 0;
//如果array是一个数组,在init的基础上再增加输入
        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;
    },
    grep: function( elems, callback, inv ) {
//grep() 方法是按照某种条件来过滤数组
        var ret = [], retVal;
        inv = !!inv;
// Go through the array, only saving the items
// that pass the validator function
//举个例子 var ccc=$.grep([1,2,4,'bb','cc'],function(v,i){return $.type(v)=='number'})
//console.log(ccc);[1, 2, 4]
//当最后参数设置为true是,到得到['bb','cc']
        for ( var i = 0, length = elems.length; i < length; i++ ) {
            retVal = !!callback( elems[ i ], i );
            if ( inv !== retVal ) {
                ret.push( elems[ i ] );
            }
        }
        return ret;
    },
// arg is for internal usage only
    map: function( elems, callback, arg ) {
        var value, key, ret = [],
                i = 0,
                length = elems.length,
// jquery objects are treated as arrays
//jquery对象也被当做是数组,如$('div')
                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 );
    },
// A global GUID counter for objects
    guid: 1,
// 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;
    },
// Mutifunctional method to get and set values to a collection
// The value/s can optionally be executed if it's a function
    access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
        var exec,
                bulk = key == null,
                i = 0,
                length = elems.length;
// Sets many values
        if ( key && typeof key === "object" ) {
            for ( i in key ) {
                jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
            }
            chainable = 1;
// Sets one value
        } else if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
            exec = pass === undefined && jQuery.isFunction( value );
            if ( bulk ) {
// Bulk operations only iterate when executing function values
                if ( exec ) {
                    exec = fn;
                    fn = function( elem, key, value ) {
                        return exec.call( jQuery( elem ), value );
                    };
// Otherwise they run against the entire set
                } else {
                    fn.call( elems, value );
                    fn = null;
                }
            }
            if ( fn ) {
                for (; i < length; i++ ) {
                    fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
                }
            }
            chainable = 1;
        }
        return chainable ?
                elems :
// Gets
                bulk ?
                        fn.call( elems ) :
                        length ? fn( elems[0], key ) : emptyGet;
    },
    now: function() {
//返回当前的时间
        return ( new Date() ).getTime();
    },
// Use of jQuery.browser is frowned upon.
// More details: http://docs.jquery.com/Utilities/jQuery.browser
    uaMatch: function( ua ) {
        ua = ua.toLowerCase();
        var match = rwebkit.exec( ua ) ||
                ropera.exec( ua ) ||
                rmsie.exec( ua ) ||
                ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
                [];
        return { browser: match[1] || "", version: match[2] || "0" };
    },
    sub: function() {
        function jQuerySub( selector, context ) {
            return new jQuerySub.fn.init( selector, context );
        }
        jQuery.extend( true, jQuerySub, this );
        jQuerySub.superclass = this;
        jQuerySub.fn = jQuerySub.prototype = this();
        jQuerySub.fn.constructor = jQuerySub;
        jQuerySub.sub = this.sub;
        jQuerySub.fn.init = function init( selector, context ) {
            if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
                context = jQuerySub( context );
            }
            return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
        };
        jQuerySub.fn.init.prototype = jQuerySub.fn;
        var rootjQuerySub = jQuerySub(document);
        return jQuerySub;
    },
    browser: {}
});
// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
    class2type[ "[object " + name + "]" ] = name.toLowerCase()
});
browserMatch = jQuery.uaMatch( userAgent );
if ( browserMatch.browser ) {
    jQuery.browser[ browserMatch.browser ] = true;
    jQuery.browser.version = browserMatch.version;
}
// Deprecated, use jQuery.browser.webkit instead
if ( jQuery.browser.webkit ) {
    jQuery.browser.safari = true;
}
原文地址:https://www.cnblogs.com/heyinwangchuan/p/6253773.html