jQuery1.11.1源码初步解析

jQuery是web程序员的必备js库,估计90%以上的web项目都会用到它。今天周末,心血来潮,打算仔细瞧瞧其庐山真面目。记得以前也对其分析过一次,半途而废了,也没有记录下来。呵呵。废话少说,直接开始。高手请绕行,勿喷,有错误请指正。谢谢。 
用eclipse打开jquery-1.11.1.js,源代码如下所示: 

Java代码   收藏代码
  1. (function( global, factory ) {  
  2.   
  3.     if ( typeof module === "object" && typeof module.exports === "object" ) {  
  4.         // For CommonJS and CommonJS-like environments where a proper window is present,  
  5.         // execute the factory and get jQuery  
  6.         // For environments that do not inherently posses a window with a document  
  7.         // (such as Node.js), expose a jQuery-making factory as module.exports  
  8.         // This accentuates the need for the creation of a real window  
  9.         // e.g. var jQuery = require("jquery")(window);  
  10.         // See ticket #14549 for more info  
  11.         module.exports = global.document ?  
  12.             factory( global, true ) :  
  13.             function( w ) {  
  14.                 if ( !w.document ) {  
  15.                     throw new Error( "jQuery requires a window with a document" );  
  16.                 }  
  17.                 return factory( w );  
  18.             };  
  19.     } else {  
  20.         factory( global );  
  21.     }  
  22.   
  23. // Pass this if window is not defined yet  
  24. }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {  
  25.   
  26. // Can't do this because several apps including ASP.NET trace  
  27. // the stack via arguments.caller.callee and Firefox dies if  
  28. // you try to trace through "use strict" call chains. (#13335)  
  29. // Support: Firefox 18+  
  30. //  
  31.   
  32. var deletedIds = [];  
  33.   
  34. var slice = deletedIds.slice;  
  35. ... ...  


1. 在代码行的第一行的第一个左括号(的右边双击,定位到了代码的最后: 

Java代码   收藏代码
  1. ... ...  
  2. // Expose jQuery and $ identifiers, even in  
  3. // AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)  
  4. // and CommonJS for browser emulators (#13566)  
  5. if ( typeof noGlobal === strundefined ) {  
  6.     window.jQuery = window.$ = jQuery;  
  7. }  
  8. return jQuery;  
  9. }));  


所以整个jquery源代码可以简化为:( ... ... ); 
就是一个小括号包含了一些javascript的表达式在里面。 

2.我们再看看小括号里面是什么东西: 

Java代码   收藏代码
  1. (function( global, factory ) {  


在第一行的第一个大括号{右边双击,通过代码定位知道了,这是一个匿名函数调用,可以简化为如下所示: 

Java代码   收藏代码
  1. (function( global, factory ) {  
  2.    ... ...  
  3. }());  



3.下面是最重要的:那么上面那个匿名函数的参数是什么呢?其参数的源代码如下: 

Java代码   收藏代码
  1. // Pass this if window is not defined yet  
  2. }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {  


很显然,第一个参数很简单: 

Java代码   收藏代码
  1. typeof window !== "undefined" ? window : this  


就是全局对象window. 
那么第二个参数呢?我们看到了function( window, noGlobal ) {,通过在大括号的右边双击,定位又到了代码的最后。所以第二个参数可以简化成如下所示: 

Java代码   收藏代码
  1. function( window, noGlobal ) {  
  2. ... ...  
  3. if ( typeof noGlobal === strundefined ) {  
  4.     window.jQuery = window.$ = jQuery;  
  5. }  
  6. return jQuery;  
  7. }  


所以第二个参数是一个函数,注意这里和上面的匿名函数调用不一样,这里没有调用,就是一个函数的定义,所以第二个参数就是一个函数,或者说函数句柄,也就是我们时时刻刻使用的jQuery,或者$,因为window.jQuery = window.$ = jQuery;。所以jQuery和$是等价的。 
到了这里,那么整个代码就可以简化成如下: 

Java代码   收藏代码
  1. (function( global, factory ) {  
  2.    ... ...  
  3. }(window, jQuery));  


这里window传给形参global,jQuery传给形参factory。 

4. 我们把上面我们刚刚得到的简化结果在细化一下,通过阅读代码注释,知道 

Java代码   收藏代码
  1. if ( typeof module === "object" && typeof module.exports === "object" )  


上面这个判断是为了适应CommonJS的环境。如果没有CommonJS环境,那么代码就是下面这个样子: 

Java代码   收藏代码
  1. (function( global, factory ) {  
  2.   factory( global );  
  3. }(window, jQuery));  


显然其实就等价于:jQuery(window); 
上面的分析,只是理解了jquery中外边的一些枝节。最重要的是理解函数jQuery(window)的定义。 

5. 上面我们说到了函数function( window, noGlobal ){... ...}就是jQuery的函数定义。 
那么:jQuery(window);调用其实就是:jQuery(window, undefined); 
也就是: function(window, undefined)();调用。 
也就是说noGlobal 参数为的值为undefined.我们Ctrl+F在源代码中查找noGlobal 的有关情况: 

Java代码   收藏代码
  1. function( window, noGlobal ){  
  2. // General-purpose constants  
  3. strundefined = typeof undefined,  
  4. ... ...  
  5.   
  6. if ( typeof noGlobal === strundefined ) {  
  7.     window.jQuery = window.$ = jQuery;  
  8. }  
  9. return jQuery;  
  10. }  


所以函数function( window, noGlobal )的第二个参数用了noGlobal的含义是,是否将jQuery和$等价,并且赋值给全局对象window。[color=darkred]也就是决定了是否向全局变量中引入jQuery和$这两个全局变量[/color]。 

所以如果在CommonJS环境中的话,并没有引入全局变量jQuery和$,而是仅仅将jQuery赋值给了module.exports: 

Java代码   收藏代码
  1. module.exports = global.document ? factory( global, true ) :function( w ) {};  


也就是:module.exports = jQuery; 注意这里没有 $ 等价于 jQuery 了。 
到了这里,jQuery源代码的外围枝节相关的代码已经分析完成。下面才是最核心的代码。 

6. 下面开始看function( window, noGlobal ){... ...}的源代码: 

Java代码   收藏代码
  1. function( window, noGlobal ) {  
  2.   
  3. var deletedIds = [];  
  4. var slice = deletedIds.slice;  
  5. var concat = deletedIds.concat;  
  6. var push = deletedIds.push;  
  7. var indexOf = deletedIds.indexOf;  
  8. var class2type = {};  
  9. var toString = class2type.toString;  
  10. var hasOwn = class2type.hasOwnProperty;  
  11. var support = {};  
  12. var  
  13.     version = "1.11.1",  
  14.     // Define a local copy of jQuery  
  15.     jQuery = function( selector, context ) {  
  16.         // The jQuery object is actually just the init constructor 'enhanced'  
  17.         // Need init if jQuery is called (just allow error to be thrown if not included)  
  18.         return new jQuery.fn.init( selector, context );  
  19.     },  
  20. ... ...  


最上面的几行代码应该是为将javascript中数组和对象的一些原生函数句柄赋值给jQuery 做准备的。 
上面的代码明显:jQuery = new jQuery.fn.init( selector, context ); 
所以关键是jQuery.fn.init( selector, context );的定义。 

在源代码中搜索:init 得到下面的代码: 

Java代码   收藏代码
  1. init = jQuery.fn.init = function( selector, context ) {  
  2.     var match, elem;  
  3.   
  4.     // HANDLE: $(""), $(null), $(undefined), $(false)  
  5.     if ( !selector ) {  
  6.         return this;  
  7.     }  
  8.   
  9.     // Handle HTML strings  
  10.     if ( typeof selector === "string" ) {  
  11.         if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {  
  12.             // Assume that strings that start and end with <> are HTML and skip the regex check  
  13.             match = [ null, selector, null ];  
  14.   
  15.         } else {  
  16.             match = rquickExpr.exec( selector );  
  17.         }  
  18.   
  19.         // Match html or make sure no context is specified for #id  
  20.         if ( match && (match[1] || !context) ) {  
  21.   
  22.             // HANDLE: $(html) -> $(array)  
  23.             if ( match[1] ) {  
  24.                 context = context instanceof jQuery ? context[0] : context;  
  25.   
  26.                 // scripts is true for back-compat  
  27.                 // Intentionally let the error be thrown if parseHTML is not present  
  28.                 jQuery.merge( this, jQuery.parseHTML(  
  29.                     match[1],  
  30.                     context && context.nodeType ? context.ownerDocument || context : document,  
  31.                     true  
  32.                 ) );  
  33.   
  34.                 // HANDLE: $(html, props)  
  35.                 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {  
  36.                     for ( match in context ) {  
  37.                         // Properties of context are called as methods if possible  
  38.                         if ( jQuery.isFunction( this[ match ] ) ) {  
  39.                             this[ match ]( context[ match ] );  
  40.   
  41.                         // ...and otherwise set as attributes  
  42.                         } else {  
  43.                             this.attr( match, context[ match ] );  
  44.                         }  
  45.                     }  
  46.                 }  
  47.   
  48.                 return this;  
  49.   
  50.             // HANDLE: $(#id)  
  51.             } else {  
  52.                 elem = document.getElementById( match[2] );  
  53.   
  54.                 // Check parentNode to catch when Blackberry 4.6 returns  
  55.                 // nodes that are no longer in the document #6963  
  56.                 if ( elem && elem.parentNode ) {  
  57.                     // Handle the case where IE and Opera return items  
  58.                     // by name instead of ID  
  59.                     if ( elem.id !== match[2] ) {  
  60.                         return rootjQuery.find( selector );  
  61.                     }  
  62.   
  63.                     // Otherwise, we inject the element directly into the jQuery object  
  64.                     this.length = 1;  
  65.                     this[0] = elem;  
  66.                 }  
  67.   
  68.                 this.context = document;  
  69.                 this.selector = selector;  
  70.                 return this;  
  71.             }  
  72.   
  73.         // HANDLE: $(expr, $(...))  
  74.         } else if ( !context || context.jquery ) {  
  75.             return ( context || rootjQuery ).find( selector );  
  76.   
  77.         // HANDLE: $(expr, context)  
  78.         // (which is just equivalent to: $(context).find(expr)  
  79.         } else {  
  80.             return this.constructor( context ).find( selector );  
  81.         }  
  82.   
  83.     // HANDLE: $(DOMElement)  
  84.     } else if ( selector.nodeType ) {  
  85.         this.context = this[0] = selector;  
  86.         this.length = 1;  
  87.         return this;  
  88.   
  89.     // HANDLE: $(function)  
  90.     // Shortcut for document ready  
  91.     } else if ( jQuery.isFunction( selector ) ) {  
  92.         return typeof rootjQuery.ready !== "undefined" ?  
  93.             rootjQuery.ready( selector ) :  
  94.             // Execute immediately if ready is not present  
  95.             selector( jQuery );  
  96.     }  
  97.   
  98.     if ( selector.selector !== undefined ) {  
  99.         this.selector = selector.selector;  
  100.         this.context = selector.context;  
  101.     }  
  102.   
  103.     return jQuery.makeArray( selector, this );  
  104. };  


所以:jQuery = function( selector, context ) { ... ...}; 
也就是jQuery函数就是根据:选择子selector在对应的上下文context中进行查找,找到之后将其构造成一个jQuery对象返回,这样可以就可以使用这个返回的jQuery对象所具有的各种函数了。其实大多数情况下,我们一般没有传递context改jQuery函数,那么默认context就是document了。最后一行代码: 
return jQuery.makeArray( selector, this ); 
告诉我们,jquery是面向集合(数组)编程的,他返回的都是集合(数组)。 
上面这103行代码才是jquery的核心。 
对于上面核心代码的分析,下次在具体分析。

猜你喜欢

转载自xiaoxiaoher.iteye.com/blog/2378162
今日推荐