在操作DOM的时候,jQuery提供的查找方法有很多,其中find()和children()经常被拿来比较,也经常被混用。有些开发者在需要获取子元素时随手写了find(),结果匹配到了一堆孙子辈甚至更深的节点,页面逻辑莫名出错;也有人明明只需要直接子元素,却因为用了find()导致在深层嵌套的大列表里性能白白浪费。要搞清楚这两者的区别,核心在于理解一个概念:查找深度。

一、从DOM树结构理解两个方法的本质区别
先明确两个方法的行为定义。children()只遍历目标元素的直接子节点,也就是在DOM树中只向下走一层,任何孙元素及更深的后代都不会出现在结果集中。而find()会递归遍历所有后代节点,无论嵌套多少层,只要匹配选择器就会被收集进来。这个差异可以用一段简单的HTML结构直观地看出来。
<div id="box">
<ul>
<li>第一项</li>
<li>第二项</li>
</ul>
<p class="tip">提示文字</p>
</div>对#box分别调用两个方法,结果完全不同。用$('#box').children()得到的是ul和p.tip这两个直接子元素;而用$('#box').find('li')得到的是两个li,它们属于孙级节点,children()根本碰不到。反过来,$('#box').find('*')会返回ul、li、p在内的全部四个后代元素。简单概括:children()管一层,find()管到底。
还有一个容易被忽略的细节:children()的结果集顺序是文档顺序,且不会包含文本节点和注释节点,它天然过滤了非元素节点。而find()除了可以接受选择器字符串,还能接受一个jQuery对象或DOM元素作为参数,用来在当前集合的后代中判断某个元素是否存在,用法上更灵活一些。
二、内部实现原理与选择器匹配规则
翻看jQuery的源码会发现,children()内部依赖的是jQuery.map()配合原生的elem.children属性(旧版本里通过childNodes过滤nodeType实现)。因为只需要遍历一层,所以它的工作量与直接子元素的数量成正比,是一个浅层的线性操作。而find()内部走的是jQuery.find,也就是著名的Sizzle选择器引擎(新版jQuery在支持querySelectorAll的浏览器上直接转发给原生方法),它会先把上下文限定在当前集合内,再执行一次完整的后代选择器匹配。
// children()的典型等价原生写法
const kids = [...box.children].filter(el => el.matches('li'));
// find()的典型等价原生写法
const list = box.querySelectorAll('li');这里有一个值得注意的匹配规则差异。当你传入复杂选择器时,比如$('#box').find('ul li.active'),选择器是从整体上做后代匹配的,匹配范围是#box的所有后代中符合ul li.active结构的节点。而$('#box').children('ul').children('li.active')则要求每一层都必须是直接子级,结构上只要有一层中间嵌套就会失败。所以两者不仅是“查多深”的区别,还直接决定了选择器的解释方式。
另外要提醒一点,children()不能接受空字符串参数,而find()可以传'*'或者不传选择器(不传时返回所有后代,前提是参数为必填的版本需要注意,建议显式写'*')。这些细节在平时写代码时经常引发隐蔽的bug。
三、性能实测:深层嵌套下的差距有多大
在结构简单的页面上,两个方法的耗时差异几乎可以忽略。但在深层嵌套、元素数量庞大的场景下,差距就会被放大。假设页面里有一个包含五千个节点的深层树形控件,我们分别测试两种查找方式。
// 模拟深层结构测试
console.time('find');
for (let i = 0; i < 1000; i++) {
$('#tree').find('.node-item');
}
console.timeEnd('find');
console.time('children');
for (let i = 0; i < 1000; i++) {
$('#tree').children('.node-item');
}
console.timeEnd('children');实测下来,在深度为七八层、后代总数数千的容器中,children()的耗时通常只有find()的几分之一。原因不难理解:children()只遍历一层,遍历成本是O(直接子元素数);而find()借助querySelectorAll虽然也是一次调用,但引擎内部要完成的匹配工作覆盖全部后代,节点越多、层级越深,扫描成本越高。如果把find()放在循环里反复执行,累积的耗时差距会非常明显。
所以在性能敏感的场景下,有一条实用的优化建议:如果你明确知道目标元素就在下一层,优先用children();如果目标埋得深,与其用find('.deep .target')这种宽泛的查找,不如先通过children()或closest()缩小上下文范围,再在小范围内查找。例如把$('#tree').find('.node-item')`改成`$('#tree').children('ul').find('.node-item')`,在树很宽的情况下能明显减少无关节点的扫描。这一点在事件委托、表格批量更新等高频操作中尤其值得注意。
四、常见误用场景与正确写法
最典型的误用是在遍历插件生成的列表时。很多UI组件的HTML结构是固定的,比如每个列表项的直接子元素里有一个标题节点,开发者随手写了$(this).find('.title'),一旦列表项内部又嵌套了子列表,子列表的标题也会被一起选中,造成重复处理。正确做法是明确层级关系:如果标题一定是直接子元素,就用$(this).children('.title');如果不确定层级,再退回find()并用:first或eq(0)限定。
// 误用:可能匹配到嵌套子项中的标题
const titles = $(item).find('.title');
// 更精确的写法
const title = $(item).children('.title').first();
// 层级不确定时的兜底方案
const title2 = $(item).find('> .title').length
? $(item).children('.title')
: $(item).find('.title').first();另一个常见坑是把children()当成parent()的反向记忆,认为“children()就是找孩子,find()就是找所有亲戚”。这个理解方向没错,但要注意find()不包含元素自身,如果目标可能就是当前元素本身,需要写$('#box').find('.target').addBack('.target')`或改用`filter()`的组合。理解了查找深度这个核心概念之后,这两个方法的选择其实非常简单:一层用children,多层用find,范围大时先收窄上下文。把这个原则落实到位,代码既准确又高效。
jQuery find()children()方法jQuery性能优化修改时间:2026-09-04 16:30:44