javascript,forEach和removeChild不可预测的行为

时间:2016-01-26 05:32:20

标签: javascript removechild

我希望从列表中删除所有项目并将其替换为其他项目

var list = document.querySelector("ul");
[].forEach.call(list.childNodes, list.removeChild.bind(list));

上面的代码没有按预期工作,相反,它只删除了一半的项目(在列表中留下每一秒项目)。 如果我把它改为

var list = document.querySelector("ul");
[].slice.call(list.childNodes).forEach(list.removeChild.bind(list));

然后按预期工作 任何人都可以解释一下吗?

2 个答案:

答案 0 :(得分:2)

在第一个中,你正在改变你正在迭代的数组。

在第二个中,你正在制作一个副本然后迭代它。

以下是另一个不需要复制的选项:

for(; list.firstChild; list.removeChild(list.firstChild));

这会删除firstChild,而不是null

答案 1 :(得分:1)

概念

要解释第一种情况中的“不可预测”行为,请考虑以下情况:

var array = [0, 1, 2, 3, 4, 5, 6, 7];

这使得行为更容易解释,而不会分散注意力.call().bind()方法。

array.forEach(function(num, index) {
  console.log(num, index);
  array.splice(index, 1);
});

您可能想知道为什么输出是:

0 0
2 1
4 2
6 3

但它实际上非常简单。 .forEach()遍历索引,直到i < array.length不再满足为止,而在每次迭代开始时,您的数组看起来像这样:

[0, 1, 2, 3, 4, 5, 6, 7];
 ^
 0

[1, 2, 3, 4, 5, 6, 7];
    ^
    1

[1, 3, 4, 5, 6, 7];
       ^
       2

[1, 3, 5, 6, 7];
          ^
          3

[1, 3, 5, 7];
             ^
            (4 < array.length) !== true

当你在.forEach()的调用中操纵一个被迭代的数组时会发生这种情况。

对于执行[].slice.call(array)的情况,您所做的只是对数组的所有索引进行浅表复制。这允许您在从原始中删除节点时迭代副本的索引。

以下是一个全面的示例,但请确保您的浏览器支持ES6模板字符串。

演示

var array = [0, 1, 2, 3, 4, 5, 6, 7];

document.write(`<p>original.forEach()</p>`);

array.forEach(function(num, index) {
  document.write(`<pre>num: ${num}, index: ${index}, array: [${array}]</pre>`);
  array.splice(index, 1);
});

document.write(`<pre>result: [${array}]</pre>`);

array = [0, 1, 2, 3, 4, 5, 6, 7];

var copy = array.slice();

document.write(`<p>copy.forEach()</p>`);

copy.forEach(function(num, index) {
  document.write(`<pre>num: ${num}, index: ${index}, array: [${array}]</pre>`);
  array.splice(array.indexOf(num), 1); // removing by reference, not by index
});

document.write(`<pre>result: [${array}]</pre>`);
body > * {
  padding: 0;
  margin: 0;
}