了解了jQuery对DOM进行遍历背后的工做机制,能够在编写代码时有意识地避免一些没必要要的重复操做,从而提高代码的性能css
从这章开始慢慢插入jQuery内部一系列工具方法的实现api
关于jQuery对象的包装dom
var $aaron = $("aaron");
经过对sizzle的分析呢,jQuery选择器,反正最终都是经过dom接口实现取值的, 可是经过jQuery处理后返回的不只仅只有dom对象,而是一个包装容器工具
返回的jQuery对象:$aaron性能
jQuery对象,其中有个prevObject这个是干吗用的呢?测试
jQuery对象栈优化
jQuery内部维护着一个jQuery对象栈。每一个遍历方法都会找到一组新元素(一个jQuery对象),而后jQuery会把这组元素推入到栈中。ui
而每一个jQuery对象都有三个属性:context、selector和prevObject,其中的prevObject属性就指向这个对象栈中的前一个对象,而经过这个属性能够回溯到最初的DOM元素集。this
简单的测试demospa
父元素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为咱们操做这个内部对象栈提供了两个很是有用的方法:
调用第一个方法只是简单地弹出一个对象(结果就是回到前一个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能取到上一个合集的引用了
总结: