// 用obj2 obj3...扩展obj1,$.extend和$("").extend()都可以使用 // 可以合并数组 // 深度合并:obj中有同名对象属性,则继续对对象属性就行合并 jQuery.extend = jQuery.fn.extend = function() { // copy reference to target object var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; // Handle a deep copy situation // 如果target为boolean类型,则为深度合并 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 // 如果只有一个参数则扩展jQuery本身(jQuery jQuery.fn) 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 object literal values or arrays // 深度合并 if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) { var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src : jQuery.isArray(copy) ? [] : {}; // 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添加静态属性或方法 jQuery.extend({ // 避免 $ jQuery变量冲突,deep为true时也释放jQuery这意味着jQuery库就无法使用 noConflict: function( deep ) { window.$ = _$; if ( deep ) { window.jQuery = _jQuery; } return jQuery; }, // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // Handle when the DOM is ready // 处理DOM ready,执行readyList中的所有函数 ready: function() { // Make sure that the DOM is not already loaded // DOM未加载完才执行下面 if ( !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, 13 ); } // Remember that the DOM is ready jQuery.isReady = true; // If there are functions bound, to execute // 执行绑定的函数 if ( readyList ) { // Execute all of them var fn, i = 0; while ( (fn = readyList[ i++ ]) ) { fn.call( document, jQuery ); } // Reset the list of functions readyList = null; } // Trigger any bound ready events if ( jQuery.fn.triggerHandler ) { jQuery( document ).triggerHandler( "ready" ); } } }, bindReady: function() { // 保证事件只绑定一次 if ( readyBound ) { return; } readyBound = true; // Catch cases where $(document).ready() is called after the // browser event has already occurred. if ( document.readyState === "complete" ) { return jQuery.ready(); } // 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 // 确保jQuery.ready执行 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 // 确保jQuery.ready执行 window.attachEvent( "onload", jQuery.ready ); // If IE and not a frame // continually check to see if the document is ready var toplevel = false; // frameElement是父文档中生成 window 的 frame 或 iframe 对象 // top页面 frameElement=null try { toplevel = window.frameElement == null; } catch(e) {} //只有在top页面才检测 if ( document.documentElement.doScroll && toplevel ) { // 如果页面可以滚动,说明DOM已ready 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). // 从1.3开始不支持DOM方法和alert,IE返回false,其它返回true isFunction: function( obj ) { return toString.call(obj) === "[object Function]"; }, isArray: function( obj ) { return toString.call(obj) === "[object Array]"; }, // 判断是否简单对象(用 { } 或 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 || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { return false; } // Not own constructor property must be Object // 有constructor属性就认为不是原生Object // {}.constructor.prototype没有isPrototypeOf方法 if ( obj.constructor && !hasOwnProperty.call(obj, "constructor") && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { 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 || hasOwnProperty.call( obj, key ); }, // 是否空对象 isEmptyObject: function( obj ) { for ( var name in obj ) { return false; } return true; }, // 空方法,无任何操作 noop: function() {}, // Evalulates a script in a global context // 在全局执行脚本片段,没有使用eval方法 globalEval: function( data ) { if ( data && rnotwhite.test(data) ) { // Inspired by code by Andrea Giammarchi // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; //scriptEval特性检测 if ( jQuery.support.scriptEval ) { script.appendChild( document.createTextNode( data ) ); } else { script.text = data; } // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709). head.insertBefore( script, head.firstChild ); head.removeChild( script ); } }, // 判断elem的tagName是否等于name nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); }, // args is for internal usage only // 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 ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} } } return object; }, trim: function( text ) { return (text || "").replace( rtrim, "" ); }, // results is for internal usage only makeArray: function( array, results ) { var ret = results || []; if ( array != null ) { // The window, strings (and functions) also have 'length' // window string function都有length属性,window function有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; }, inArray: function( elem, array ) { if ( array.indexOf ) { return array.indexOf( elem ); } for ( var i = 0, length = array.length; i < length; i++ ) { if ( array[ i ] === elem ) { return i; } } return -1; }, // 合并数组 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; }, // 按照callback过滤elems grep: function( elems, callback, inv ) { var ret = []; // Go through the array, only saving the items // that pass the validator function for ( var i = 0, length = elems.length; i < length; i++ ) { if ( !inv !== !callback( elems[ i ], i ) ) { ret.push( elems[ i ] ); } } return ret; }, // arg is for internal usage only // 把数组按callback映射成新数组 map: function( elems, callback, arg ) { var ret = [], value; // Go through the array, translating each of the items to their // new value (or values). for ( var i = 0, length = elems.length; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } return ret.concat.apply( [], ret ); }, // A global GUID counter for objects // objects计数器 guid: 1, // 1.4新增,代理函数,解决this指针问题 proxy: function( fn, proxy, thisObject ) { if ( arguments.length === 2 ) { if ( typeof proxy === "string" ) { thisObject = fn; fn = thisObject[ proxy ]; proxy = undefined; } else if ( proxy && !jQuery.isFunction( proxy ) ) { thisObject = proxy; proxy = undefined; } } if ( !proxy && fn ) { proxy = function() { return fn.apply( thisObject || this, arguments ); }; } // Set the guid of unique handler to the same of original handler, so it can be removed if ( fn ) { proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; } // So proxy can be declared as an argument return proxy; }, // Use of jQuery.browser is frowned upon(皱眉头表示不满). 不推荐使用jQuery.browser,推荐使用特性检测 // More details: http://docs.jquery.com/Utilities/jQuery.browser uaMatch: function( ua ) { var ret = { browser: "" }; ua = ua.toLowerCase(); if ( /webkit/.test( ua ) ) { ret = { browser: "webkit", version: /webkit[// ]([/w.]+)/ }; } else if ( /opera/.test( ua ) ) { ret = { browser: "opera", version: /version/.test( ua ) ? /version[// ]([/w.]+)/ : /opera[// ]([/w.]+)/ }; } else if ( /msie/.test( ua ) ) { ret = { browser: "msie", version: /msie ([/w.]+)/ }; } else if ( /mozilla/.test( ua ) && !/compatible/.test( ua ) ) { ret = { browser: "mozilla", version: /rv:([/w.]+)/ }; } ret.version = (ret.version && ret.version.exec( ua ) || [0, "0"])[1]; return ret; }, browser: {} });