408

例子:

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

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

alert(arr);

数组仍然是它的原始值,有没有办法从迭代函数中对数组元素进行写访问?

4

11 回答 11

634

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

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 链接

于 2012-09-18T18:38:39.037 回答
152

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

这是我们正在谈论的代码:

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 中的值类型。

基元(未定义、空值、字符串、布尔值、数字)存储实际值。

前任:var x = 5;

引用类型(自定义对象)存储对象的内存位置。

前任:var xObj = { x : 5 };

第三,函数参数是如何工作的。

在函数中,参数总是按值传递。

因为arr它是一个字符串数组,所以它是一个原始对象数组,这意味着它们是按值存储的。

因此,对于您上面的代码,这意味着每次 forEach() 迭代时,part都等于与 相同的值arr[index]但不是相同的 object

part = "four";将更改part变量,但会不理会arr

以下代码将更改您想要的值:

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

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

alert(arr);

现在如果 arrayarr是一个引用类型的数组,下面的代码将起作用,因为引用类型存储了一个对象的内存位置,而不是实际的对象。

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);
于 2015-07-08T16:23:27.240 回答
125

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

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

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

于 2016-07-08T12:32:14.437 回答
16

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

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

arr[index] = 'new value';

于 2012-09-18T18:38:02.467 回答
8

这是使用=>样式函数的类似答案:

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

也有效。

于 2019-02-27T23:56:04.340 回答
3

用数组的索引替换它。

array[index] = new_value;
于 2012-09-18T18:39:54.527 回答
3

.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();
于 2012-09-18T23:17:52.523 回答
3

要完全添加或删除会改变索引的元素,通过扩展 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' 插入,否则将不会像预期的那样

于 2018-11-19T11:19:02.087 回答
2

使用 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 开始,从它离开的地方开始。

所以请记住这一点。

于 2016-05-15T18:23:41.090 回答
1

如果要删除项目

如果您需要进行的更改是从列表中完全删除一个或多个项目,那么使用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]现在是 new item[3],这并没有使任何事情变得更容易。你不会有这个问题倒退。

这当然是一个不使用 foreach 的解决方案,但重要的是要记住“老派”的方法通常是最好的。而且,如果出于某种原因您需要break;从循环中退出(并且有多个项目),则 for 循环会更有效,因为您无法跳出 for 循环。

于 2021-08-26T20:53:21.583 回答
0

如果你想覆盖你可以试试这个

var newArray= [444,555,666];
var oldArray =[11,22,33];
oldArray.forEach((name, index) => oldArray [index] = newArray[index]);
console.log(newArray);
于 2020-05-12T11:36:08.387 回答