【问题标题】:Is it supposed to be safe to remove elements from an array while iterating with for..of in JavaScript?在 JavaScript 中使用 for..of 进行迭代时从数组中删除元素是否应该是安全的?
【发布时间】:2019-11-04 10:34:27
【问题描述】:

我知道它适用于 Set,但我的印象是它也适用于 Array。所以我在 Chrome 中试了一下,很惊讶它不起作用:

const array = [1,2,3,4,5,6]

for (const item of array) {
    if (item === 3 || item === 4) {
        array.splice(array.indexOf(item), 1);
    }
}

console.log(array) // [1,2,4,5,6]

它没有删除4

所以我的问题是,迭代安全是否只适用于 SetMap,而不适用于 Array

(如果是这种情况,那么除了简单的语法之外,我看不出使用它比for(;;) 有什么好处。我的印象是for..of 可以防止错误,即使使用@ 987654330@,就像 SetMap)

注意,作为一个技巧,我可以通过克隆数组(或反向迭代)来做到这一点:

const array = [1,2,3,4,5,6]

for (const item of Array.from(array)) {
    if (item === 3 || item === 4) {
        array.splice(array.indexOf(item), 1);
    }
}

console.log(array) // [1,2,5,6]

【问题讨论】:

  • 您是否尝试过不相邻的元素?这可能是 4 移回索引 3 导致基本上跳过的结果。反向迭代有效的事实支持了该理论。至于克隆版本,这无关紧要,因为您正在对恰好具有相同元素的两个不同数组执行两个不同的操作。
  • @AvinKavish 当然,这可能适用于这种特殊情况,但它适用于 Set(迭代时删除 3 和 4)。
  • Set 必须有一个不是简单索引访问的迭代器。
  • @AvinKavish Yes indeed
  • @Bergi 为什么 ArrayIterator 被设计成不像 Set 或 Map 那样方便?

标签: javascript arrays iteration for-of-loop


【解决方案1】:

根据MDN

一般来说,最好不要在其中添加、修改或删除属性 迭代期间的对象,而不是当前的属性 参观了。无法保证添加的属性是否会 已访问,修改后的属性(当前属性除外)是否会 在被修改之前或之后被访问,或者是否被删除 属性将在被删除之前被访问。

或者,您可以试试这个演示使用filter()

const array = [1, 2, 3, 4, 5, 6]

for (const item of [3, 4]) {
  array.splice(array.indexOf(item), 1);
}

console.log(array)

【讨论】:

    【解决方案2】:

    不,(如您的示例所示)在迭代数组时从数组中删除元素是不安全的。

    默认数组迭代器存储当前索引,当你在数组上调用splice时它不会更新这个索引。无论您对数组中的元素做了什么,它都会在相同的位置继续。您可以阅读spec for ArrayIterator objects,它们基本上像for (var index=0; index<array.length; index++) yield array[index]; 循环一样工作。

    【讨论】:

    • 很高兴知道。为什么for (const item of [...arr]) 会在阅读您的回复和 OP 问题后让我感到困惑?它使用相同的 ArrayIterator 等
    • @Akrion 它也使用ArrayIterator,但它会迭代数组的副本。修改arr不影响副本。与Array.from(arr)arr.slice()arr.map(x => x) 相同。
    • @Akrion Nah,克隆数组只是为了保持迭代工作是我所说的不好的做法。人们真的不应该在迭代数组时简单地从数组中删除元素,而是应该创建一个新数组。无论如何,使用 filter 或迭代要删除的元素(如 AswinKumar 的回答)比 array.splice(array.indexOf(item), 1) 更有效。
    【解决方案3】:

    这是因为当循环转到3(索引:2)时,数组删除了3 值,4 现在变为索引:2。 下一次迭代将转到索引:3,即5

    你可以这样做:

    const array = [1,2,3,4,5,6]
    for (var i=0;i<array.length;i++) {
        if (array[i] === 3 || array[i] === 4) {
            array.splice(array.indexOf(array[i]), 1);
            --i;
        }
    }
    
    console.log(array) 

    【讨论】:

      猜你喜欢
      • 2015-04-03
      • 2014-09-22
      • 1970-01-01
      • 1970-01-01
      • 2015-06-11
      • 2012-01-26
      • 2016-04-27
      • 1970-01-01
      • 2014-04-27
      相关资源
      最近更新 更多