jquery1.7.2的源码分析(二)

时间:2021-06-14 04:10:58

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