需要注意的是这一句 jQuery.extend = jQuery.fn.extend = function() {} ,也就是 jQuery.extend 的实现和 jQuery.fn.extend 的实现共用了同一个方法,但是为什么能够实现不同的功能了,这就要归功于 Javascript 强大(怪异?)的 this 了。
1)在 jQuery.extend() 中,this 的指向是 jQuery 对象(或者说是 jQuery 类),所以这里扩展在 jQuery 上;
2)在 jQuery.fn.extend() 中,this 的指向是 fn 对象,前面有提到 jQuery.fn = jQuery.prototype ,也就是这里增加的是原型方法,也就是对象方法。
jQuery 的链式调用及回溯
另一个让大家喜爱使用 jQuery 的原因是它的链式调用,这一点的实现其实很简单,只需要在要实现链式调用的方法的返回结果里,返回 this ,就能够实现链式调用了。
当然,除了链式调用,jQuery 甚至还允许回溯,看看:
// 通过 end() 方法终止在当前链的最新过滤操作,返回上一个对象集合 $('div').eq(0).show().end().eq(1).hide();
当选择了 ('div').eq(0) 之后使用 end() 可以回溯到上一步选中的 jQuery 对象 $('div'),其内部实现其实是依靠添加了 prevObject 这个属性:
jQuery 完整的链式调用、增栈、回溯通过 return this 、 return this.pushStack() 、return this.prevObject 实现,看看源码实现:
jQuery.fn = jQuery.prototype = { // 将一个 DOM 元素集合加入到 jQuery 栈 // 此方法在 jQuery 的 DOM 操作中被频繁的使用, 如在 parent(), find(), filter() 中 // pushStack() 方法通过改变一个 jQuery 对象的 prevObject 属性来跟踪链式调用中前一个方法返回的 DOM 结果集合 // 当我们在链式调用 end() 方法后, 内部就返回当前 jQuery 对象的 prevObject 属性 pushStack: function(elems) { // 构建一个新的jQuery对象,无参的 this.constructor(),只是返回引用this // jQuery.merge 把 elems 节点合并到新的 jQuery 对象 // this.constructor 就是 jQuery 的构造函数 jQuery.fn.init,所以 this.constructor() 返回一个 jQuery 对象 // 由于 jQuery.merge 函数返回的对象是第二个函数附加到第一个上面,所以 ret 也是一个 jQuery 对象,这里可以解释为什么 pushStack 出入的 DOM 对象也可以用 CSS 方法进行操作 var ret = jQuery.merge(this.constructor(), elems); // 给返回的新 jQuery 对象添加属性 prevObject // 所以也就是为什么通过 prevObject 能取到上一个合集的引用了 ret.prevObject = this; ret.context = this.context; // Return the newly-formed element set return ret; }, // 回溯链式调用的上一个对象 end: function() { // 回溯的关键是返回 prevObject 属性 // 而 prevObject 属性保存了上一步操作的 jQuery 对象集合 return this.prevObject || this.constructor(null); }, // 取当前 jQuery 对象的第 i 个 eq: function(i) { // jQuery 对象集合的长度 var len = this.length, j = +i + (i < 0 ? len : 0); // 利用 pushStack 返回 return this.pushStack(j >= 0 && j < len ? [this[j]] : []); }, }
总的来说,
1)end() 方法返回 prevObject 属性,这个属性记录了上一步操作的 jQuery 对象合集;
2)而 prevObject 属性由 pushStack() 方法生成,该方法将一个 DOM 元素集合加入到 jQuery 内部管理的一个栈中,通过改变 jQuery 对象的 prevObject 属性来跟踪链式调用中前一个方法返回的 DOM 结果集合
3)当我们在链式调用 end() 方法后,内部就返回当前 jQuery 对象的 prevObject 属性,完成回溯。
正则与细节优化
不得不提 jQuery 在细节优化上做的很好。也存在很多值得学习的小技巧,下一篇将会以 jQuery 中的一些编程技巧为主题行文,这里就不再赘述。
然后想谈谈正则表达式,jQuery 当中用了大量的正则表达式,我觉得如果研读 jQuery ,正则水平一定能够大大提升,如果是个正则小白,我建议在阅读之前先去了解以下几点:
1)了解并尝试使用 Javascript 正则相关 API,包括了 test() 、replace() 、match() 、exec() 的用法;
2)区分上面 4 个方法,哪个是 RegExp 对象方法,哪个是 String 对象方法;
3)了解简单的零宽断言,了解什么是匹配但是不捕获以及匹配并且捕获。
变量冲突处理
最后想提一提 jQuery 变量的冲突处理,通过一开始保存全局变量的 window.jQuery 以及 windw.$ 。
当需要处理冲突的时候,调用静态方法 noConflict(),让出变量的控制权,源码如下: