首页 > 代码库 > jQuery源码学习笔记:扩展工具函数

jQuery源码学习笔记:扩展工具函数

// 扩展工具函数
jQuery.extend({
    // http://www.w3school.com.cn/jquery/core_noconflict.asp
    // 释放$的 jQuery 控制权
    // 许多 JavaScript 库使用 $ 作为函数或变量名,jQuery 也一样。
    // 在 jQuery 中,$ 仅仅是 jQuery 的别名,因此即使不使用 $ 也能保证所有功能性。
    // 假如我们需要使用 jQuery 之外的另一 JavaScript 库,我们可以通过调用 $.noConflict() 向该库返回控制权。
   
    // 通过向该方法传递参数 true,我们可以将 $ 和 jQuery 的控制权都交还给另一JavaScript库。
    noConflict: function( deep ) {
       // 交出$的控制权
       if ( window.$ === jQuery ) {
           window.$ = _$;
       }
       // 交出jQuery的控制权
       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
    // 一个计数器,用于跟踪在ready事件出发前的等待次数
    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
    // 文档加载完毕句柄
    // http://www.cnblogs.com/fjzhou/archive/2011/05/31/jquery-source-4.html
    ready: function( wait ) {
       // Either a released hold or an DOMready/load event and not yet ready
       //
       if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
           // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
           // 确保document.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.resolveWith( document, [ jQuery ] );
   
           // Trigger any bound ready events
           if ( jQuery.fn.trigger ) {
              jQuery( document ).trigger( "ready" ).unbind( "ready" );
           }
       }
    },
   
    // 初始化readyList事件处理函数队列
    // 兼容不同浏览对绑定事件的区别
    bindReady: function() {
       if ( readyList ) {
           return;
       }
   
       readyList = jQuery._Deferred();
   
       // 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.onload回调函数
           window.addEventListener( "load", jQuery.ready, false );
   
       // If IE event model is used
       } else if ( document.attachEvent ) {
           // ensure firing before onl oad,
           // maybe late but safe also for iframes
           // 确保在onload之前触发onreadystatechange,可能慢一些但是对iframes更安全
           document.attachEvent( "onreadystatechange", DOMContentLoaded );
   
           // A fallback to window.onload, that will always work
           // 注册window.onload回调函数
           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";
    },
   
    // 是否数组
    // 如果浏览器有内置的 Array.isArray 实现,就使用浏览器自身的实现方式,
    // 否则将对象转为String,看是否为"[object Array]"。
    isArray: Array.isArray || function( obj ) {
       return jQuery.type(obj) === "array";
    },
   
    // A crude way of determining if an object is a window
    // 简单的判断(判断setInterval属性)是否window对象
    isWindow: function( obj ) {
       return obj && typeof obj === "object" && "setInterval" in obj;
    },
   
    // 是否是保留字NaN
    isNaN: function( obj ) {
       // 等于null 或 不是数字 或调用window.isNaN判断
       return obj == null || !rdigit.test( obj ) || isNaN( obj );
    },
    // 获取对象的类型
    type: function( obj ) {
       // 通过核心API创建一个对象,不需要new关键字
       // 普通函数不行
       // 调用Object.prototype.toString方法,生成 "[object Xxx]"格式的字符串
       // class2type[ "[object " + name + "]" ] = name.toLowerCase();
       return obj == null ?
           String( obj ) :
           class2type[ toString.call(obj) ] || "object";
    },
   
    // 检查obj是否是一个纯粹的对象(通过"{}" 或 "new Object"创建的对象)
    // console.info( $.isPlainObject( {} ) ); // true
    // console.info( $.isPlainObject( '' ) ); // false
    // console.info( $.isPlainObject( document.location ) ); // true
    // console.info( $.isPlainObject( document ) ); // false
    // console.info( $.isPlainObject( new Date() ) ); // false
    // console.info( $.isPlainObject( ) ); // false
   
    // isPlainObject分析与重构 http://www.jb51.net/article/25047.htm
    // 对jQuery.isPlainObject()的理解 http://www.cnblogs.com/phpmix/articles/1733599.html
    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
       // 必须是一个对象
       // 因为在IE8中会抛出非法指针异常,必须检查constructor属性
       // DOM节点和window对象,返回false
      
       // obj不存在 或 非object类型 或 DOM节点 或 widnow对象,直接返回false
       // 测试以下三中可能的情况:
       // jQuery.type(obj) !== "object" 类型不是object,忽略
       // obj.nodeType 认为DOM节点不是纯对象
       // jQuery.isWindow( obj ) 认为window不是纯对象
       if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
           return false;
       }
   
       // Not own constructor property must be Object
       // 测试constructor属性
       // 具有构造函数constructor,却不是自身的属性(即通过prototype继承的),
       if ( obj.constructor &&
           !hasOwn.call(obj, "constructor") &&
           !hasOwn.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 ) {}
       // key === undefined及不存在任何属性,认为是简单的纯对象
       // hasOwn.call( obj, key ) 属性key不为空,且属性key的对象自身的(即不是通过prototype继承的)
       return key === undefined || hasOwn.call( obj, key );
    },
    // 是否空对象
    isEmptyObject: function( obj ) {
       for ( var name in obj ) {
           return false;
       }
       return true;
    },
    // 抛出一个异常
    error: function( msg ) {
       throw msg;
    },
    // 解析JSON
    // parseJSON把一个字符串变成JSON对象。
    // 我们一般使用的是eval。parseJSON封装了这个操作,但是eval被当作了最后手段。
    // 因为最新JavaScript标准中加入了JSON序列化和反序列化的API。
    // 如果浏览器支持这个标准,则这两个API是在JS引擎中用Native Code实现的,效率肯定比eval高很多。
    // 目前来看,Chrome和Firefox4都支持这个API。
    parseJSON: function( data ) {
       if ( typeof data !== "string" || !data ) {
           return null;
       }
   
       // Make sure leading/trailing whitespace is removed (IE can't handle it)
       data = http://www.mamicode.com/jQuery.trim( data );>