jQuery源码学习(10)-回溯end()和addBack()

1、问题发现:

父元素ul,嵌套了li节点, 我们现给li绑定一个事件:

<ul id="aaron">
    parent
    <li>child</li>
</ul>

这个很简单找到ul下面的li,绑定即可:

var aaron = $("#aaron");
    aaron.find('li').click(function(){
        alert(1)     //1
    })

此时我又想给父元素绑定一个事件,我们是不是又要在aaron上绑定事件?

通过find处理后,此时的上下文是每一个li了,所以必须要重新引用aaron父元素

aaron.click(function(){
      alert(2)     //1
 })

所有jQuery引入一个机制,可以回溯到之前的dom元素集合,通过end()方法:

aaron.find('li').click(function(){
        alert(1)
    }).end().click(function(){
        alert(2)
    })
2、jQuery对象栈

jQuery内部维护着一个jQuery对象栈。每个遍历方法都会找到一组新元素(一个jQuery对象),然后jQuery会把这组元素推入到栈中。

而每个jQuery对象都有三个属性:context、selector和prevObject,其中的prevObject属性就指向这个对象栈中的前一个对象,而通过这个属性可以回溯到最初的DOM元素集

如:

<div id="view">
  <h1 class="header">标题</h1>
  <p>container</p>
  <strong class="espe">重点</strong>
</div>

对于上面的 html:

var $view = $("#view");
// $header 是由 $view 操作得到的
var $header = $view.find('.header');
$header.prevObject === $view; // true

3、end()和addBack()

jQuery为我们操作这个内部对象栈提供了两个非常有用的方法,这两个函数其实就是 prevObject 的应用

  • .end()
  • .andBack()

end() 函数只是单纯的进行出栈操作,并返回出栈的这个 jQuery 对象,而 addBack() 函数不会执行出栈,而是把栈顶对象和当前的对象组成一个新对象,入栈。利用这个DOM元素栈可以减少重复的查询和遍历的操作,而减少重复操作也正是优化jQuery代码性能的关键所在。

例如:

$('#view').find('.header').css({'color': 'red'})
  .end()
  .find(.espe).css({'color': 'white'})

加了 end之后,当前执行的 jQuery 对象就变成 $('#view')了,所以可以继续执行 find 操作等等。

$('#view').find('.header').nextAll()
  .addBack()
  .css({'color': 'red'}) // 颜色全红

上面的代码,会使得 #view 的三个子元素的颜色都设置为红色。

4、源码实现

既然是回溯到上一个dom合集,那么肯定end方法中返回的就是一个jQuery对象了,所以我们看源码

其实就是返回prevObject对象了

jQuery.fn.end: function() {
            return this.prevObject || this.constructor(null);
        },
jQuery.fn.addBack = function (selector) {
  // 可以看出有参数的 addBack 会对 prevObject 进行过滤
  return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector));
}

prevObject在什么情况下会产生?

在构建jQuery对象的时候,通过pushStack方法构建

pushStack:将一个DOM元素集合加入到jQuery栈。

// Take an array of elements and push it onto the stack
        // (returning the new matched element set)
        jQuery.fn.pushStack: function( elems ) {

            // Build a new jQuery matched element set
            var ret = jQuery.merge( this.constructor(), elems );

            // Add the old object onto the stack (as a reference)
            ret.prevObject = this;
            ret.context = this.context;

            // Return the newly-formed element set
            return ret;
        },

流程解析:

1. 构建一个新的jQuery对象,无参 this.constructor(),只是返回引用this

2. jQuery.merge 把elems节点,合并到新的jQuery对象

3. 给返回的新jQuery对象添加属性prevObject ,所以我们看到prevObject 其实还是当前jQuery的一个引用罢了

所以也就是为什么通过prevObject能取到上一个合集的引用了

总结:

  • pushStack()方法在jQuery的DOM操作中被频繁的使用, 如在parent(), find(), filter()中, 当然还有其他许多类似的方法, 它们往往需要返回一个jQuery封装过的DOM结果集.但在我们自己写jQuery代码的时候,却很少关注或使用过pushStack()
  • 在jQuery内部,pushStack()方法通过改变一个jQuery对象的prevObject属性来"跟踪"链式调用中前一个方法返回的DOM结果集(被jQuery封装过,也是个jQuery对象,说是"跟踪",是因为实际存储的是个引用). 当我们再链式调用end()方法后, 内部就返回当前jQuery对象的prevObject.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值