DOM广度优先遍历中visitedNodes的作用与节点重复检查逻辑疑问
遍历代码问题解答
visitedNodes数组的作用
这个数组用来记录已经完成处理的DOM节点引用,核心是避免同一个节点被重复执行操作。
遍历过程中待访问队列可能出现同一节点多次入队的情况,比如DOM树存在循环引用、入队逻辑重复时,如果没有访问记录,同一个节点会被反复执行生成日志、遍历子节点的操作,极端情况下会导致遍历死循环。
!visitedNodes.includes(currentNode) 的运行逻辑
Array.prototype.includes判断对象相等性时,是严格按照内存引用对比实现的,和===对比对象的逻辑完全一致,不会对比节点的类名、内容、结构等属性值:
运行时会依次遍历visitedNodes里存储的每一个节点引用,和当前的currentNode做引用对比,只有当数组中某一项和currentNode指向同一个内存地址的DOM对象时,才会返回true;只要是不同的对象,哪怕外观、属性完全一致,对比结果都是false。
相同类名、相同结构的div会不会被跳过
完全不会。
两个独立存在的、类名和结构完全一致的div,本质是内存中两个完全独立的对象引用,includes的判断逻辑根本不会把它们识别为同一个节点,第二个div会正常进入处理流程,完成日志打印、子节点入队的操作,不会被跳过。
当前处理逻辑的实际问题
用已访问节点列表避免重复处理的思路本身是合理的,你担心的“外观相似就被跳过”的问题不存在,但当前代码的实现确实有几处明显缺陷:
- 实际运行的不是预期的广度优先遍历:代码用
pop()从数组尾部取出待访问节点,本质是后进先出的栈结构,最终执行效果是深度优先遍历,和代码注释里标注的“from the front(从队头取)”的预期完全相反。要实现标准广度优先遍历,需要把取节点的方法换成shift(),从数组头部取出最早入队的节点。 - 数组存储已访问节点的性能较差:
includes是线性查找,DOM节点数量较多时,每次判断的时间复杂度为O(n),节点量大时会出现明显的性能损耗。更优的实现是用Set结构存储已访问节点,查找操作的时间复杂度为O(1)。 - 代码鲁棒性不足:如果初始获取的根节点不存在(比如
.a类名的元素不存在),虽然循环里有currentNode的判断,但流程没有兜底逻辑,很容易出现异常。
内容的提问来源于stack exchange,提问作者JerryMan228
相关产品推荐
相关产品推荐

