终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事。
在 一文,大致描述了 jQuery 源码整体大致架构,主要分为以下几个点:
A 通过自执行函数,在内部将 jQuery (jQuery 为一个函数,同时,在 JS 中,函数也为对象)以 window.jQuery=window.$ 引入,供用户直接便可使用 $、jQuery 调用。
B 以 C#、Java 为例,可知存在静态方法、对象方法区别,该原理类比 jQuery ,jQuery.xxx 相当于给为其增加静态方法,而 jQuery.fn.xxx 则可类比于相当于给其增加对象方法。
C 在 jQuery 源码内部,巧妙运用原型,直接上代码:
jQuery = function( selector, context ) { // 用户调用 jQuery 方法,即是以 jQuer.fn.init 为构造函数所创建的对象 return new jQuery.fn.init( selector, context, rootjQuery );};// 在 JS 中,所有函数对象默认将有 prototype 属性// 为 jQuery.prototype 另起一个简单的别名,即 jQuery.fn// 其中 jQuery.fn 内部包含 constructor 属性(重新将该函数指向 jQuery)、init 函数jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ){ ..... }, // Start with an empty selector selector: "", // The current version of jQuery being used jquery: "1.8.3", .....};// 关键一步,将 jQuery.fn.init 函数的 prototype属性 指向 jQuery.fn ,这一步相当重要jQuery.fn.init.prototype = jQuery.fn;JS 中通过原型实现继承,其中使用最简单的介绍,JS 中对象的 __proto__ 属性===该对象 constructor 的 prototype 属性, 在调用方法以及获取属性,首先在自身查找,然后,即沿着 __proto__ 属性查找(而其等于 该对象 constructor 的 prototype 属性), 如果还没找到,继续沿着对象的 __proto__ 的 __proto__ 一步一步往上查找,最终查找到 Object.prototype.__proto__ 为止,没有即返回 undefined.
在 jQuery 源码中,同时,还大量包括扩展 jQuery 对象自身属性方法,以及扩展 jQuery.fn (jQuery.prototype)属性方法,而该些扩展功能,即通过 jQuery.fn.extend=jQuery.extend=function(){} 实现,
具体分析,请查看下述代码部分.
jQuery.extend = jQuery.fn.extend = function () { // arguments 在 JS 可获取函数实际传入参数,其包含 length 属性 var src, copyIsArray, copy, name, options, clone, // 可获取 arguments 类数组首个实参 target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // 若传入首个实参为 boolean 值,则做相关逻辑处理 if ( typeof target === "boolean" ) { deep = target; // 将 target 重新赋值,如若首个实参为 boolean 值 target = arguments[i] || {}; // 同时,将 i 的值加 一,则为 i=2 i++; } // Handle case when target is a string or something (possible in deep copy) // 首个实参类型不等于 object 并且不等于 function 类型,则做相关逻辑处理 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed // 如若传入实参仅为一个,则做下方逻辑处理 if (i === length) { // 将 target 重新赋值,如若 jQuery.extend 则此时 this =jQuery, // 如若 jQuery.fn.extend ,则此时 this =jQuery.fn=jQuery.prototype target = this; // 同时,将 i 的值减一,则 i=0 i--; } // 如传入实参参数为 1 ,此时,扩展对象属性 this =jQuery,this=jQuery.fn=jQuery.prototype // 如传入实参参数个数不为1,此时,将分为首个实参为 boolean 值,以及相关情况 for ( ; i < length; i++ ) { // 首先,判断传入实际值是否为 undefined 、null if ( (options = arguments[ i ]) != null ) { // 再对传入实参值进行遍历操作,扩展对象属性 for (name in options) { // 参考上方逻辑,如实参个数为1,则 target指向 this // 如若实参个数不为1 ,则 target 指向 argument[0] // 如若实参个数为 bool 值,则 target 指向 argument [1] src = target[ name ]; copy = options[ name ]; // 判断需扩展对象与此时扩展属性进行比较,以防扩展对象与扩展属性相等,出现循环引用 if ( target === copy ) { continue; } // 如若首个参数值 boolean 值为 true 且扩展属性为 true 且 扩展属性为数组或者纯对象,进入深度复制逻辑处理 if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) { // 如若是数组
// jQuery.fn.extend(true, { Name: "jack", age: 20, {Physics:30}} ,{ Name: "jack", {Math:40,English:100,Chinese:76}}) // jQuery.fn.extend( { Name: "jack", age: 20, {Physics:30}} ,{ Name: "jack", {Math:40,English:100,Chinese:76}})
if (copyIsArray) { copyIsArray = false; // 若此时需扩展对象 target[name] 属性(数组)进行扩展 clone = src && jQuery.isArray(src) ? src : []; } // 如若扩展属性为纯对象 else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // 进行递归调用,进行属性扩展 target[ name ] = jQuery.extend( deep, clone, copy ); // 直接扩展对象属性 ,如果扩展属性不为 undefined ,target[name]=copy,直接扩展对象属性 } else if (copy !== undefined) { // jQuery.fn.extend({ Name: "jack", age: 20, {Math:30}} ,{ Name: "jack", age: 20, {Math:40,English:100,Chinese:76}}) target[ name ] = copy; } } } } // 最后,返回 target 对象 return target; };
在 jQuery 源码中,可大量发现 jQuery.extend、jQuery.fn.extend 使用,而此时,为 jQuery对象、jQuery.fn ( jQuery.fn ) 进行方法属性扩展。
最后,有一个问题,在 jQuery 大量的链式调用,实现原理如何?大家可联想一下,在 java、C# 中要实现这种调用,在一个类中,如何实现该种功能?在 Java、C# 中,在一个类中,每个方法逻辑写完后,最终都返回 this,是不是就可以实现该功能。当然,在 C# 中,现在已有扩展方法可实现该功能。
参考资料:http://www.cnblogs.com/chyingp/archive/2013/06/03/3115210.html