在执行foreach时更改数组中的值

例子:

var arr = ["one","two","three"];


arr.forEach(function(part){
part = "four";
return "four";
})


alert(arr);

数组仍然是它的原始值,有任何方法来写访问数组的元素从迭代函数?

599679 次浏览

Javascript是按值传递的,这本质上意味着part是数组中值的复制

要更改该值,请在循环中访问数组本身。

arr[index] = 'new value';

回调函数传递元素、索引和数组本身。

arr.forEach(function(part, index, theArray) {
theArray[index] = "hello world";
});

编辑 -正如注释中所指出的,.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链接

将其替换为数组的索引。

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();

让我们试一试保持简单,并讨论它是如何实际工作的。它与变量类型和函数参数有关。

下面是我们正在讨论的代码:

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 < a href = " 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);

使用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

正如你所看到的,当我们递减i时,它将不再从5开始,而是从6开始。

记住这一点。

数组:[1, 2, 3, 4]
结果:["foo1", "foo2", "foo3", "foo4"] < / p >

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.forEach()覆盖原始数组

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
originalArr.forEach((name, index) => originalArr[index] = `${name}man`);


console.log( "Overridden: %s", originalArr );

要完全添加或删除会改变索引的元素,可以通过扩展zhujy_8833的slice()建议迭代一个副本,只需计算已经删除或添加的元素的数量,并相应地更改索引。例如,要删除元素:

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的解决方案,但重要的是要记住,“老派”的方法往往是最好的。如果出于某种原因你需要从循环中break;(并且有很多项),for循环更有效,因为你不能跳出for循环。