HTML5技术

【深入浅出jQuery】源码浅析--整体架构 - ChokCoco(2)

字号+ 作者:H5之家 来源:博客园 2016-03-16 12:00 我要评论( )

1)首先要明确,使用 $('xxx') 这种实例化方式,其内部调用的是return new jQuery.fn.init(selector, context, rootjQuery) 这一句话,也就是构造实例是交给了jQuery.fn.init() 方法取完成。 2)将 jQuery.fn.init

1)首先要明确,使用 $('xxx') 这种实例化方式,其内部调用的是 return new jQuery.fn.init(selector, context, rootjQuery) 这一句话,也就是构造实例是交给了 jQuery.fn.init() 方法取完成。

2)将 jQuery.fn.init 的 prototype 属性设置为 jQuery.fn,那么使用 new jQuery.fn.init() 生成的对象的原型对象就是 jQuery.fn ,所以挂载到 jQuery.fn 上面的函数就相当于挂载到 jQuery.fn.init() 生成的 jQuery 对象上,所有使用 new jQuery.fn.init() 生成的对象也能够访问到 jQuery.fn 上的所有原型方法。

3)也就是实例化方法存在这么一个关系链  

 

   方法的重载

jQuery 源码晦涩难读的另一个原因是,使用了大量的方法重载,但是用起来却很方便:

// 获取 title 属性的值 $('#id').attr('title'); // 设置 title 属性的值 $('#id').attr('title','jQuery'); // 获取 css 某个属性的值 $('#id').css('title'); // 设置 css 某个属性的值 $('#id').css('width','200px');

方法的重载即是一个方法实现多种功能,经常又是 get 又是 set,虽然阅读起来十分不易,但是从实用性的角度考虑,这也是为什么 jQuery 如此受欢迎的原因,大多数人使用 jQuery() 构造方法使用的最多的就是直接实例化一个 jQuery 对象,但其实在它的内部实现中,有着 9 种不同的方法重载场景:

// 接受一个字符串,其中包含了用于匹配元素集合的 CSS 选择器 jQuery([selector,[context]]) // 传入单个 DOM jQuery(element) // 传入 DOM 数组 jQuery(elementArray) // 传入 JS 对象 jQuery(object) // 传入 jQuery 对象 jQuery(jQuery object) // 传入原始 HTML 的字符串来创建 DOM 元素 jQuery(html,[ownerDocument]) jQuery(html,[attributes]) // 传入空参数 jQuery() // 绑定一个在 DOM 文档载入完成后执行的函数 jQuery(callback)

所以读源码的时候,很重要的一点是结合 jQuery API 进行阅读,去了解方法重载了多少种功能,同时我想说的是,jQuery 源码有些方法的实现特别长且繁琐,因为 jQuery 本身作为一个通用性特别强的框架,一个方法兼容了许多情况,也允许用户传入各种不同的参数,导致内部处理的逻辑十分复杂,所以当解读一个方法的时候感觉到了明显的困难,尝试着跳出卡壳的那段代码本身,站在更高的维度去思考这些复杂的逻辑是为了处理或兼容什么,是否是重载,为什么要这样写,一定会有不一样的收获。其次,也是因为这个原因,jQuery 源码存在许多兼容低版本的 HACK 或者逻辑十分晦涩繁琐的代码片段,浏览器兼容这样的大坑极其容易让一个前端工程师不能学到编程的精髓,所以不要太执着于一些边角料,即使兼容性很重要,也应该适度学习理解,适可而止。

 

   jQuery.fn.extend 与 jQuery.extend

extend 方法在 jQuery 中是一个很重要的方法,jQuey 内部用它来扩展静态方法或实例方法,而且我们开发 jQuery 插件开发的时候也会用到它。但是在内部,是存在 jQuery.fn.extend 和 jQuery.extend 两个 extend 方法的,而区分这两个 extend 方法是理解 jQuery 的很关键的一部分。先看结论:

1)jQuery.extend(object) 为扩展 jQuery 类本身,为类添加新的静态方法;

2)jQuery.fn.extend(object) 给 jQuery 对象添加实例方法,也就是通过这个 extend 添加的新方法,实例化的 jQuery 对象都能使用,因为它是挂载在 jQuery.fn 上的方法(上文有提到,jQuery.fn = jQuery.prototype )。 

它们的官方解释是:

1)jQuery.extend(): 把两个或者更多的对象合并到第一个当中,

2)jQuery.fn.extend():把对象挂载到 jQuery 的 prototype 属性,来扩展一个新的 jQuery 实例方法。

也就是说,使用 jQuery.extend() 拓展的静态方法,我们可以直接使用 $.xxx 进行调用(xxx是拓展的方法名),

而使用 jQuery.fn.extend() 拓展的实例方法,需要使用 $().xxx 调用。

源码解析较长,点击下面可以展开,也可以去这里阅读:

// 扩展合并函数 // 合并两个或更多对象的属性到第一个对象中,jQuery 后续的大部分功能都通过该函数扩展 // 虽然实现方式一样,但是要注意区分用法的不一样,那么为什么两个方法指向同一个函数实现,但是却实现不同的功能呢, // 阅读源码就能发现这归功于 this 的强大力量 // 如果传入两个或多个对象,所有对象的属性会被添加到第一个对象 target // 如果只传入一个对象,则将对象的属性添加到 jQuery 对象中,也就是添加静态方法 // 用这种方式,我们可以为 jQuery 命名空间增加新的方法,可以用于编写 jQuery 插件 // 如果不想改变传入的对象,可以传入一个空对象:$.extend({}, object1, object2); // 默认合并操作是不迭代的,即便 target 的某个属性是对象或属性,也会被完全覆盖而不是合并 // 如果第一个参数是 true,则是深拷贝 // 从 object 原型继承的属性会被拷贝,值为 undefined 的属性不会被拷贝 // 因为性能原因,JavaScript 自带类型的属性不会合并 jQuery.extend = jQuery.fn.extend = function() { var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation // target 是传入的第一个参数 // 如果第一个参数是布尔类型,则表示是否要深递归, if (typeof target === "boolean") { deep = target; target = arguments[1] || {}; // skip the boolean and the target // 如果传了类型为 boolean 的第一个参数,i 则从 2 开始 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 // 如果参数的长度为 1 ,表示是 jQuery 静态方法 if (length === i) { target = this; --i; } // 可以传入多个复制源 // i 是从 1或2 开始的 for (; i < length; i++) { // Only deal with non-null/undefined values // 将每个源的属性全部复制到 target 上 if ((options = arguments[i]) != null) { // Extend the base object for (name in options) { // src 是源(即本身)的值 // copy 是即将要复制过去的值 src = target[name]; copy = options[name]; // Prevent never-ending loop // 防止有环,例如 extend(true, target, {'target':target}); if (target === copy) { continue; } // Recurse if we're merging plain objects or arrays // 这里是递归调用,最终都会到下面的 else if 分支 // jQuery.isPlainObject 用于测试是否为纯粹的对象 // 纯粹的对象指的是 通过 "{}" 或者 "new Object" 创建的 // 如果是深复制 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 // 返回新的 target // 如果 i < length ,是直接返回没经过处理的 target,也就是 arguments[0] // 也就是如果不传需要覆盖的源,调用 $.extend 其实是增加 jQuery 的静态方法 return target; };

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • Android -- 带你从源码角度领悟Dagger2入门到放弃(一) - 阿呆哥哥

    Android -- 带你从源码角度领悟Dagger2入门到放弃(一) - 阿呆哥哥

    2017-04-21 11:02

  • ASP.NET Core MVC 源码学习:详解 Action 的激活 - Savorboard

    ASP.NET Core MVC 源码学习:详解 Action 的激活 - Savorboard

    2017-04-14 13:04

  • 深入浅出数据库索引原理 - 陈宏鸿

    深入浅出数据库索引原理 - 陈宏鸿

    2017-04-03 10:00

  • 车大棒浅谈jQuery源码(二) - 车大棒

    车大棒浅谈jQuery源码(二) - 车大棒

    2017-04-02 10:07

网友点评
"