JavaScript如何在函数内部递归自调用并重新选取新的DOM元素
问题根因
代码出现新旧元素同时被修改的问题,由4个细节bug共同导致:
- 函数名拼写不统一:定义的函数名为
funA,但递归调用、初始触发时写的是funcA,容易触发隐式全局变量或未定义报错 - 闭包缓存旧DOM引用:每次执行
funA时会把当前.current元素存在作用域变量currentRow里,同步递归时外层作用域不会销毁,所有历史调用的currentRow会一直持有对应旧DOM节点的引用,回调执行时会顺着作用域链操作所有缓存过的节点 - 节点选择API用错:
nextSibling会匹配包括文本节点、注释节点在内的所有兄弟节点,大概率选不到你要的下一个业务元素,甚至会拿到null触发报错 - 缺少递归终止判断:代码没有写递归停止的边界条件,会一直往下找兄弟节点直到报错。
修复代码
按以下逻辑调整即可解决问题:
// 如果需要跨递归保留state状态,把变量定义在函数外层 // let state = []; function funA () { let state = []; // 每次执行实时查询当前带current类的节点,不依赖闭包缓存的旧引用 let currentRow = document.querySelector(".current"); // 递归终止边界:没有当前节点、没有下一个元素节点时直接退出 if (!currentRow || !currentRow.nextElementSibling) return; evaluateThings(function () { // 操作前二次校验:当前节点是否还持有current类,彻底避免操作旧节点 if (!currentRow.classList.contains("current")) return; // 切换类名 currentRow.classList.remove("current"); // 用nextElementSibling只匹配元素节点,跳过文本/注释节点 currentRow.nextElementSibling.classList.add("current"); // 递归调用 funA(); }); } function evaluateThings(callback) { // 原有业务逻辑 callback() } // 初始触发,注意函数名和定义保持一致 funA()
可选优化
如果你的state数组需要在多次递归间共享数据,不要把它定义在funA函数内部——每次递归调用都会生成新的函数作用域,内部的state会被重置为空数组。可以把state提到funA外层作为作用域共享变量,或者作为参数传入每次递归调用。
内容的提问来源于stack exchange,提问作者relidon
相关产品推荐
相关产品推荐

