zoukankan      html  css  js  c++  java
  • jQuery 2.0.3 源码分析 回溯魔法 end()和pushStack()

    了解了jQuery对DOM进行遍历背后的工作机制,可以在编写代码时有意识地避免一些不必要的重复操作,从而提升代码的性能

    从这章开始慢慢插入jQuery内部一系列工具方法的实现

    关于jQuery对象的包装

    var $aaron = $("aaron");

    通过对sizzle的分析呢,jQuery选择器,反正最终都是通过dom接口实现取值的, 但是通过jQuery处理后返回的不仅仅只有dom对象,而是一个包装容器

    返回的jQuery对象:$aaron

    image

    jQuery对象,其中有个prevObject这个是干嘛用的呢?


    jQuery对象栈

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

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


    简单的测试demo

    父元素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)
        })

    jQuery为我们操作这个内部对象栈提供了两个非常有用的方法:

    • .end()
    • .andBack()

    调用第一个方法只是简单地弹出一个对象(结果就是回到前一个jQuery对象)。第二个方法更有意思,调用它会在栈中回溯一个位置,然后把两个位置上的元素集组合起来,并把这个新的、组合之后的元素集推入栈的上方。

    利用这个DOM元素栈可以减少重复的查询和遍历的操作,而减少重复操作也正是优化jQuery代码性能的关键所在。

    .end() 方法

    大多数 jQueryDOM遍历 方法来操作 jQuery 对象实例,并创建一个新的对象,匹配一个不同的 DOM 元素集合。当发生这种情况时,实际上是新的元素集合被压入到对象内部维护的栈中。每次过滤方法都会被压入栈中。当我们需要返回到前一个状态时,我们可以使用end() 进行出栈操作,来返回栈中的前一个状态。

    假设页面上有几个短的列表

    <ul class="first">
       <li class="foo">list item 1</li>
       <li>list item 2</li>
       <li class="bar">list item 3</li>
    </ul>
    <ul class="second">
       <li class="foo">list item 1</li>
       <li>list item 2</li>
       <li class="bar">list item 3</li>
    </ul>

    end() 方法主要用于 jQuery 的链式属性中。当没有使用链式用法时,我们通常只是调用变量名上的前一个对象,所以我们不需要操作栈。使用 end() 时,我们可以一次性调用所有需要的方法:

    $('ul.first').find('.foo').css('background-color', 'red')
      .end().find('.bar').css('background-color', 'green');

    链式的原理就是要返回当前操作的上下文

    错误的:

    跟上面的demo一样,上下文被切换了,所以下面find(‘bar’)出错了

    $('ul.first').find('.foo').css('background-color', 'red').find('.bar').css('background-color', 'green');

    正确的:

    首先在链式用法中只在第一个列表中查找样式为 foo 的项目,并将其背景色变成红色。然后 end()返回调用 find() 之前的状态。因此,第二次 find() 将只会查找 <ul class="first"> 中的 '.bar',而不是继续在<li class="foo"> 中进行查找,结果是将匹配到的元素的背景色变成绿色。上述代码的最终结果是,第一个列表中的第 1 和第 3 个列表项的背景色有颜色,而第二个列表中的任何项目都没有背景色。

    $('ul.first').find('.foo').css('background-color', 'red')
      .end().find('.bar').css('background-color', 'green');

    总的来说end方法就是回溯到上一个dom合集,因此对于链式操作与优化,这个方法还是很有意义的


    源码实现

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

    其实就是返回prevObject对象了

    end: function() {
                return this.prevObject || this.constructor(null);
            },

    prevObject在什么情况下会产生?

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

    jQuery.fn.extend({
            find: function( selector ) {
    
                ...........................省略................................
    
                 //通过sizzle选择器,返回结果集
                 jQuery.find( selector, self[ i ], ret );
    
                // Needed because $( selector, context ) becomes $( context ).find( selector )
                ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
                ret.selector = this.selector ? this.selector + " " + selector : selector;
                return ret;
            }

     

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

    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.
  • 相关阅读:
    Tomcat详解系列(3)
    Tomcat详解系列(2)
    Tomcat详解系列(1)
    常用开发库
    单元测试
    [MongoDB知识体系] 一文全面总结MongoDB知识体系
    问题记录:net::ERR_CERT_AUTHORITY_INVALID
    CSS+DIV特色开关按钮
    Jquery的Ajax简易优化思路
    CSS+DIV简易灯泡案例
  • 原文地址:https://www.cnblogs.com/aaronjs/p/3387278.html
Copyright © 2011-2022 走看看