导读:本期聚焦于吴凌云创作的《jQuery中end()方法是做什么的?如何回退到上一个选择器状态实现链式调用?》,敬请观看详情。链式调用是jQuery最优雅的特性之一,通过codeaddClass/code、codefind/code等方法连续操作DOM,代码简洁流畅。但当一个链式调用中进行了多次筛选后,想回到最初选中的元素继续操作,很多人只能重新写一遍选择器。其实jQuery早就内置了end()方法来解决这个问题。end()方法会终止当前链中最近的筛选操作,将匹配集合回退到上一个状态,整个回退过程依赖jQuery内部维护的prevObject栈结构。本文将深入剖析end()方法的实现原理,讲解pushStack如何记录每一次筛选前的对象,并通过addBack、prevObject等关联知识点和完整代码示例,帮助掌握复杂链式调用中的状态管理技巧。

写jQuery代码时,链式调用几乎是最吸引人的特性。选中一组元素,连续调用方法完成操作,一行代码就能完成传统写法好几行的事情。但链越写越长,问题也随之而来:比如先用$('#menu')选中菜单,再用find('li')找到所有列表项,操作完之后还想给菜单容器本身加个class,怎么办?重新写一遍$('#menu')当然可以,但jQuery提供了更优雅的方案——end()方法,它可以回退到上一次筛选操作之前的jQuery对象,让链式调用得以延续。

jQuery中end()方法是做什么的?如何回退到上一个选择器状态实现链式调用?

end()方法的基本用法

end()的作用一句话就能说清:结束当前链中最近一次筛选操作,返回上一次筛选之前的jQuery对象。常见的筛选类方法包括find()filter()children()parents()eq()slice()等,这些方法都会改变当前匹配的元素集合,同时在内部把操作前的对象压入一个栈中,end()就是从这个栈里弹出上一个状态。

来看一个直观的例子:

$('#menu')            // 选中id为menu的元素
  .find('li')         // 筛选出所有li子元素
    .addClass('item') // 给li添加class
    .end()            // 回退到#menu对象
  .addClass('active') // 给#menu本身添加class
  .find('.title')     // 再次筛选
    .css('color', 'red')
    .end()            // 又回退到#menu
  .show();

上面这段代码全程没有重复书写$('#menu')选择器,所有操作都在一条链上完成。每次调用end(),匹配集合就回到上一次筛选前的状态。如果链上没有任何筛选操作,调用end()会返回一个空jQuery对象,这一点要注意,盲目调用可能导致后续操作全部失效且不报错,排查起来比较麻烦。

底层原理:prevObject与pushStack机制

jQuery对象内部有一个prevObject属性,指向筛选前的那个jQuery对象,这一系列引用构成了所谓的“选择器栈”。几乎所有筛选方法在返回新对象之前,都会先调用内部的pushStack()函数,它的逻辑大致是:创建一个新的jQuery对象装载数筛后的元素,并把当前对象记录为新对象的prevObject

简化后的源码逻辑如下:

// jQuery内部简化实现
pushStack: function(elems) {
  // 构建新的jQuery对象,元素为筛选结果
  var ret = jQuery.merge(this.constructor(), elems);
  // 关键一步:把当前对象压入栈,作为前一个状态
  ret.prevObject = this;
  ret.context = this.context;
  return ret;
},
end: function() {
  // 直接返回上一个状态,栈空则返回空对象
  return this.prevObject || this.constructor();
}

也就是说,end()并不是“撤销”了什么操作,它只是简单地返回prevObject引用的旧对象。之前对DOM做的修改(比如已经加上的class)不会回滚,回退的仅仅是“当前操作的元素集合”。理解了这一点,就不会误以为end()具备撤销功能。

连续多次筛选时,栈会不断加深,end()可以连续调用多次逐层回退:

$('ul')
  .find('li')          // 第一层筛选:所有li
    .filter('.on')     // 第二层筛选:class含on的li
      .css('background', 'yellow')
      .end()           // 回退到所有li
    .end()             // 回退到ul
  .css('border', '1px solid #ccc');

end()与addBack()的区别及实用技巧

end()经常一起被提到的还有addBack()(旧版本叫andSelf())。二者方向完全不同:end()是丢弃当前筛选结果,回到之前的状态;addBack()是把之前的状态合并进来,新旧两个集合合并成一个再操作。

举个例子对比,假设要对li和它所属的ul同时设置样式:

// 用end()的写法:分两次设置
$('li.on').css('color', 'red').parent('ul').css('border', '1px solid blue');

// 用addBack()的写法:一次性操作两个集合
$('li.on').parent('ul').addBack().css('border', '1px solid blue');

实际开发中还有几个建议值得参考。第一,长链中筛选次数最好不超过三层,否则end()的层次关系很难读懂,可以用换行和缩进提升可读性,如前面示例那样每次筛选后缩进一级。第二,调试时可以在控制台直接查看prevObject,比如$('li').filter('.on').prevObject.length能看到筛选前的元素数量,帮助你确认end()会回退到哪一步。第三,如果你的团队已经在逐步迁移到原生JavaScript,需要知道querySelectorAll返回的NodeList并没有这套栈机制,链式回退的思路需要自己封装实现。

总结一下,end()的本质是利用jQuery对象内部的prevObject栈,把筛选操作前的状态保存下来,让链式调用可以随时回退。掌握了它,配合addBack()等关联方法,在处理复杂的DOM遍历和批量操作时,代码会紧凑得多,也能避免反复执行选择器带来的性能损耗。

jQueryend()方法链式调用修改时间:2026-09-11 12:00:43

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260911/54654.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。