提问人:rsk82 提问时间:9/19/2012 最后编辑:Roko C. Buljanrsk82 更新时间:1/7/2023 访问量:674845
执行 foreach 时更改数组中的值
change values in array when doing foreach
问:
例:
var arr = ["one","two","three"];
arr.forEach(function(part){
part = "four";
return "four";
})
alert(arr);
数组仍然具有其原始值,有没有办法从迭代函数中写入数组的元素?
答:
Javascript 是按值传递的,本质上意味着数组中值的副本。part
若要更改值,请在循环中访问数组本身。
arr[index] = 'new value';
评论
part
回调将传递元素、索引和数组本身。
arr.forEach(function(part, index, theArray) {
theArray[index] = "hello world";
});
edit — 如注释中所述,该函数可以采用第二个参数,该参数将用作每次调用回调的值:.forEach()
this
arr.forEach(function(part, index) {
this[index] = "hello world";
}, arr); // use arr as this
第二个示例显示自己被设置为在回调中。有人可能会认为调用中涉及的数组可能是 的默认值,但无论出于何种原因,它都不是; 如果未提供第二个参数,则为。arr
this
.forEach()
this
this
undefined
(注意:如果回调是一个函数,上述内容不适用,因为在调用此类函数时永远不会绑定到任何内容。this
=>
this
此外,重要的是要记住,Array 原型上提供了一整套类似的实用程序,并且 Stackoverflow 上会出现许多关于一个或另一个函数的问题,因此最好的解决方案是简单地选择不同的工具。你有:
forEach
用于对数组中的每个条目执行操作;filter
用于生成仅包含合格条目的新数组;map
用于通过转换现有数组来制作一对一的新数组;some
检查数组中是否至少有一个元素符合某些描述;every
检查数组中的所有条目是否与描述匹配;find
在数组中查找值
等等。MDN 链接
评论
.forEach()
thisArg
this
.forEach
this
.forEach()
function()
() => {}
theArray
arr[index] = "hello world";
将其替换为数组的索引。
array[index] = new_value;
.forEach 函数可以有一个回调函数(eachelement, elementIndex) 所以基本上你需要做的是:
arr.forEach(function(element,index){
arr[index] = "four"; //set the value
});
console.log(arr); //the array has been overwritten.
或者,如果要保留原始数组,则可以在执行上述过程之前复制它。 要制作副本,您可以使用:
var copy = arr.slice();
评论
map()
forEach()
map()
让我们试着保持简单,并讨论它的实际工作原理。它与变量类型和函数参数有关。
这是我们正在谈论的代码:
var arr = ["one","two","three"];
arr.forEach(function(part) {
part = "four";
return "four";
})
alert(arr);
首先,这里是您应该阅读有关 Array.prototype.forEach() 的内容:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
其次,让我们简要谈谈 JavaScript 中的值类型。
基元(undefined、null、String、Boolean、Number)存储实际值。
前任:var x = 5;
引用类型(自定义对象)存储对象的内存位置。
前任:var xObj = { x : 5 };
第三,函数参数的工作原理。
在函数中,参数始终按值传递。
因为是一个字符串数组,所以它是一个原始对象的数组,这意味着它们是按值存储的。arr
因此,对于上面的代码,这意味着每次 forEach() 迭代时,都等于与 相同的值,但不是同一个对象。part
arr[index]
part = "four";
将更改变量,但将单独保留。part
arr
以下代码将更改所需的值:
var arr = ["one","two","three"];
arr.forEach(function(part, index) {
arr[index] = "four";
});
alert(arr);
现在,如果数组是引用类型的数组,则以下代码将起作用,因为引用类型存储的是对象的内存位置,而不是实际对象。arr
var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];
arr.forEach(function(part, index) {
// part and arr[index] point to the same object
// so changing the object that part points to changes the object that arr[index] points to
part.num = "four";
});
alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);
下面演示了可以更改为指向新对象,同时将对象单独存储在:part
arr
var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];
arr.forEach(function(part, index) {
// the following will not change the object that arr[index] points to because part now points at a new object
part = 5;
});
alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);
评论
In functions, parameters are always passed by value.
使用 Array 对象方法可以修改 Array 内容,但与基本的 for 循环相比,这些方法缺少一个重要的功能。您不能在运行时修改索引。
例如,如果要删除当前元素并将其放置到同一数组中的另一个索引位置,则可以轻松执行此操作。如果你将当前元素移动到上一个位置,那么在下一次迭代中就没有问题了,你会得到相同的下一个项目,就好像你什么都没做一样。
考虑以下代码,一旦索引计数到 5,我们将索引位置 5 的项目移动到索引位置 2。
var ar = [0,1,2,3,4,5,6,7,8,9];
ar.forEach((e,i,a) => {
i == 5 && a.splice(2,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 6 - 7 7 - 8 8 - 9 9
但是,如果我们将当前元素移动到当前索引位置之外的某个位置,事情就会变得有点混乱。然后,下一个项目将移动到移动的项目位置,在下一次迭代中,我们将无法查看或评估它。
考虑以下代码,一旦索引计数到 5,我们将索引位置 5 的项目移动到索引位置 7。
var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && a.splice(7,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 7 - 7 5 - 8 8 - 9 9
所以我们从未在循环中遇到过 6。通常,在 for 循环中,当您向前移动数组项时,您应该递减索引值,以便您的索引在下一次运行中保持在同一位置,并且您仍然可以评估移动到已删除项位置的项。这对于数组方法是不可能的。您无法更改索引。检查以下代码
var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && (a.splice(7,0,a.splice(i,1)[0]), i--);
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 4 5 - 6 7 - 7 5 - 8 8 - 9 9
正如你所看到的,当我们递减时,它不会从 5 继续,而是从它离开的地方继续 6。i
所以请记住这一点。
数组: 结果: [1, 2, 3, 4]
["foo1", "foo2", "foo3", "foo4"]
Array.prototype.map()
保留原始阵列
const originalArr = ["Iron", "Super", "Ant", "Aqua"];
const modifiedArr = originalArr.map(name => `${name}man`);
console.log( "Original: %s", originalArr );
console.log( "Modified: %s", modifiedArr );
Array.prototype.map()
覆盖原始数组
let originalArr = ["Iron", "Super", "Ant", "Aqua"];
originalArr = originalArr.map(name => `${name}man`);
console.log( "Original: %s", originalArr );
Array.prototype.forEach()
覆盖原始数组
const originalArr = ["Iron", "Super", "Ant", "Aqua"];
originalArr.forEach((name, index) => originalArr[index] = `${name}man`);
console.log( "Overridden: %s", originalArr );
评论
let arr1 = ["1", 2, 3, 4]; arr1.map(function(v) { return "foo"+ v; }); console.log( arr );
Array.prototype.map() 从不修改原始数组,forEach mutate。
map
forEach
要完全添加或删除会改变索引的元素,通过扩展 slice() 的建议来迭代副本zhujy_8833只需计算您已经删除或添加的元素的数量并相应地更改索引。例如,要删除元素:
let values = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"];
let count = 0;
values.slice().forEach((value, index) => {
if (value === "A2" || value === "A5") {
values.splice(index - count++, 1);
};
});
console.log(values);
// Expected: [ 'A0', 'A1', 'A3', 'A4', 'A6', 'A7', 'A8' ]
要在以下位置之前插入元素:
if (value === "A0" || value === "A6" || value === "A8") {
values.splice(index - count--, 0, 'newVal');
};
// Expected: ['newVal', A0, 'A1', 'A2', 'A3', 'A4', 'A5', 'newVal', 'A6', 'A7', 'newVal', 'A8' ]
要在以下位置插入元素:
if (value === "A0" || value === "A6" || value === "A8") {
values.splice(index - --count, 0, 'newVal');
};
// Expected: ['A0', 'newVal', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'newVal', 'A7', 'A8', 'newVal']
要替换元素:
if (value === "A3" || value === "A4" || value === "A7") {
values.splice(index, 1, 'newVal');
};
// Expected: [ 'A0', 'A1', 'A2', 'newVal', 'newVal', 'A5', 'A6', 'newVal', 'A8' ]
注意:如果同时实现“before”和“after”插入,代码应首先处理“before”插入,否则将不符合预期
下面是使用样式函数的类似答案:=>
var data = [1,2,3,4];
data.forEach( (item, i, self) => self[i] = item + 10 );
给出结果:
[11,12,13,14]
对于箭头样式函数来说,该参数并不是绝对必需的,因此self
data.forEach( (item,i) => data[i] = item + 10);
也有效。
如果要覆盖,可以尝试此操作
var newArray= [444,555,666];
var oldArray =[11,22,33];
oldArray.forEach((name, index) => oldArray [index] = newArray[index]);
console.log(newArray);
如果要删除项目
如果您需要进行的更改是从列表中完全删除一个或多个项目,则使用循环并向后循环会更安全。for
for (let i = myArray.length - 1; i >= 0; i--)
{
const item = myArray[i];
if (...)
{
// remove item
// https://stackoverflow.com/questions/5767325/how-can-i-remove-a-specific-item-from-an-array?rq=1
}
};
向后退意味着每个项目的数组永远不会改变。如果您要通过循环和删除进行前进,那么现在是新的,这不会使任何事情变得更容易。你不会有这个问题倒退。item[3]
item[4]
item[3]
这当然是一种不使用 foreach 的解决方案,但重要的是要记住,“老派”方法通常是最好的。如果出于某种原因你需要从循环中(并且有不止几个项目),那么 for 循环更有效,因为你不能跳出 for 循环。break;
评论
x=[2,3,4]; x=x.map(n=>n*2); // [4,6,8]