循环遍历数组时修改数组是否安全?

4

在循环数组时修改它是否安全,例如推入一个元素?

我正在使用underscore的each方法。


1
一般来说不需要,但如果您能提供一小段代码片段,那么我们就可以更准确地回答您的问题。 - Simon M
1
不确定 Underscore,但通常提供迭代方法的库会缓存数组长度,因此如果在迭代时添加或删除元素可能会出现混乱。但是,用传统的 for 循环替换并自己管理索引变量并不难。 - nnnnnn
@nnnnnn 你说得对,如果你查看下划线源代码,它会在操作对象之前获取对象的键。在这种情况下,它会知道在你更改它们之前要迭代多少个元素。 - EmptyArsenal
2个回答

2

我建议除非你绝对需要针对集合中的每个项目引发副作用(触发事件、打印结果等),否则避免使用each。对于简单的修改集合,有更好的方法。

如果每个添加的元素仅是个别输入元素的结果,则典型的函数模式将是对数组进行flatmap,可以将其视为两个步骤:

  1. 使用map应用一个函数,为每个元素生成一个数组作为结果。整体结果将是一个数组的数组。
  2. 在该数组上使用flatten以获得一个一维数组。

使用underscorelodash

var origArray = [1, 2, 3, 4];
var duplicateIf3 = function (val) { return val === 3 ? [val, val] : val; };
_.flatten(origArray.map(duplicateIf3));
// -> [1, 2, 3, 3, 4]

(在类型为FP的情况下,对于不是3的值,该函数必须返回[val],但flatten不关心——它将任何您提供的内容展平为一维。)

如果新元素取决于前面所有元素,您可能会使用reducefold,并将空数组作为初始值。

var origArray = [1, 2, 3, 4];
origArray.reduce(function (acc, val) { return acc.concat(acc).concat(val); }, []);
// -> [1, 1, 2, 1, 1, 2, 3, 1, 1, 2, 1, 1, 2, 3, 4]

抱歉,我无法想到一个现实的例子,但是这里每一步都以前面所有步骤的完整输出为基础,以便说明目的,您可以从原始数组的每个值中看出正在发生的事情。还要注意,如果您不想使用underscore / lodash,则可以从reduce创建自己的flattenreduceflatmap更通用,但两种方法都能将数组转换为某种程度上取决于第一个数组的更大数组。
如果您对此感兴趣,我强烈建议查看Reg Braithwaite的免费(在线)Javascript Allongé

1

我不确定你所说的安全是什么意思,但你必须有充分的理由去这样做。我稍微尝试了一下,以下是我得到的结果:

_.each(a, function(el) { 
    if (el % 2 == 0) 
        a.push(el + 1); 
    console.log(el); 
});

// logs 1, 2, 3, 4, 5

console.log(a);
// logs [ 1, 2, 3, 4, 5, 3, 5 ]

在这种情况下,除非您想要经历添加元素的过程,否则不会有负面影响,但如果您更改特定元素,则可能会陷入更棘手的情况:
_.each(a, function(el, index) { 
    a[index + 1] = el - 1;
    if (el % 2 == 0) 
        a.push(el + 1); 
    console.log(el); 
});

// logs 1, 0, -1, -2, -3

对于大多数用例,使用 _.map 更为合理。


1
很确定这对于像.splice()这样的函数来说不安全。 - nrabinowitz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接