Testdome任务求助:appendChildren函数无限循环问题排查
为什么appendChildren函数会陷入无限循环?
问题场景
需要实现appendChildren函数,为页面中所有已存在的div添加一个经过decorateDiv装饰的子div。比如原DOM结构:
<div id="a"> <div id="b"> </div> </div>
执行函数后(假设decorateDiv无操作)应变为:
<div id="a"> <div id="b"> <div></div> </div> <div></div> </div>
但以下代码运行时会陷入无限循环,原因是什么?
有问题的代码
function appendChildren(decorateDiv) { var allDivs = document.getElementsByTagName("div"); for (var i = 0; i < allDivs.length; i++) { var newDiv = document.createElement("div"); decorateDiv(newDiv); allDivs[i].appendChild(newDiv); } } document.body.innerHTML = ` <div id="a"> <div id="b"> </div> </div>`; console.log(document.body.innerHTML);
原因解析
问题核心在于document.getElementsByTagName("div")返回的是动态HTMLCollection集合——它会实时同步DOM的变化:
- 初始页面有2个div(id=a和id=b),此时
allDivs.length为2 - 第一次循环(i=0):给id=a的div添加新div,DOM中div总数变为3,
allDivs.length自动更新为3 - 第二次循环(i=1):给id=b的div添加新div,DOM中div总数变为4,
allDivs.length再次更新为4 - 后续循环中,
allDivs[i]会指向刚新增的div,循环又会给这个新div添加子div,导致div总数持续增长,allDivs.length也跟着不断变大,最终i < allDivs.length的条件永远成立,循环无法终止
修复方案
只需将动态集合转换为静态数组,或者固定初始遍历范围,就能避免DOM变化干扰循环:
方法1:转换为静态数组
function appendChildren(decorateDiv) { // 把动态集合转成静态数组,后续DOM变化不会影响数组长度 var allDivs = Array.from(document.getElementsByTagName("div")); for (var i = 0; i < allDivs.length; i++) { var newDiv = document.createElement("div"); decorateDiv(newDiv); allDivs[i].appendChild(newDiv); } }
方法2:固定初始长度,倒序遍历
function appendChildren(decorateDiv) { var allDivs = document.getElementsByTagName("div"); // 记录初始div数量,后续不再变化 var initialCount = allDivs.length; // 倒序遍历,新增的div不会出现在初始范围内 for (var i = initialCount - 1; i >= 0; i--) { var newDiv = document.createElement("div"); decorateDiv(newDiv); allDivs[i].appendChild(newDiv); } }
内容的提问来源于stack exchange,提问作者reactjs
相关产品推荐
相关产品推荐

