在javascript中执行foreach时是否可以更改数组的值?

is it possible to change values of the array when doing foreach in javascript?

例子:

1
2
3
4
5
6
7
8
var arr = ["one","two","three"];

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

alert(arr);

数组仍然具有其原始值,是否有任何方法可以通过迭代函数对数组的元素进行写入访问?


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

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

edit-如注释中所述,.forEach()函数可以接受第二个参数,该参数将在每次调用回调时用作this的值:

1
2
3
arr.forEach(function(part, index) {
  this[index] ="hello world";
}, arr); // use arr as this

第二个例子显示了arr本身在回调中被设置为this。有人可能认为,.forEach()调用中涉及的数组可能是this的默认值,但无论出于什么原因,它都不是;如果不提供第二个参数,this将是undefined

同样重要的是要记住,在数组原型上提供了一系列类似的实用程序,并且在stackoverflow上会出现许多关于一个函数或另一个函数的问题,这样最好的解决方案就是简单地选择一个不同的工具。你已经得到:

  • 用于对数组中的每个条目执行某项操作或对其执行某项操作;
  • 用于生成只包含限定项的新数组的filter
  • map通过对现有阵列进行变换,生成一对一的新阵列;
  • some检查数组中的至少一个元素是否符合某种描述;
  • every检查数组中的所有条目是否与描述匹配;
  • find查找数组中的值

等等。多链路


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

下面是我们要讨论的代码:

1
2
3
4
5
6
7
8
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是一个字符串数组,所以它是一个原始对象数组,这意味着它们是按值存储的。

所以对于上面的代码,这意味着每次for each()迭代时,partarr[index]的值相同,但不是相同的对象。

part ="four";将改变part变量,但只保留arr变量。

以下代码将更改所需的值:

1
2
3
4
5
6
7
var arr = ["one","two","three"];

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

alert(arr);

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

1
2
3
4
5
6
var arr=[num:"one",num:"two<div class="suo-content">[collapse title=""]<ul><li>的确,很好的解释!在其他迭代方法中扩展解释会更好,比如…of,map,…对于这种情况,新的for…of的工作方式与foreach非常相似,只是"index"对于最终更改原始数组(如foreach)是缺失的。ES5映射与foreach类似,可能只是从映射返回值的可能性使得从语法角度看事情比使用索引更清楚。</li><li>很好的解释。谢谢您。毕竟我不能理解这句话:<wyn>In functions, parameters are always passed by value.</wyn>第二个例子呢?</li><li>@7方面,该语句描述函数参数总是按值传递。所以对于原语,它将是原语指向的值。对于对象,它将是对象指向的位置。这个W3Schools页面有一个很好的解释。请参阅部分参数由值传递,对象由引用传递。</li><li>在函数中,参数总是按值传递。BAM。谢谢。+ 1</li></ul>[/collapse]</div><p><center>[wp_ad_camp_1]</center></p><hr><P>数组:<wyn>["1", 2, 3, 4]</wyn>。结果:<wyn>["foo1","foo2","foo3","foo4"]</wyn>。</P><wyn>Array.prototype.map()</wyn>保留原数组<P>通过引入一个新变量<wyn>modifiedArr</wyn>实现</P><P>[cc lang="javascript"]const arr = ["1", 2, 3, 4];

const modifiedArr = arr.map(v => `foo${v}`);

console.log("
Original:", arr );
console.log("
Modified:", modifiedArr );

Array.prototype.forEach()修改原数组

通过索引arr[i]确定其关键点来实现

4


javascript是传递值,这实质上意味着part是数组中值的副本。

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

arr[index] = 'new value';


.foreach函数可以具有回调函数(eachelement、elementindex)所以基本上你需要做的是:

1
2
3
4
arr.forEach(function(element,index){
    arr[index] ="four";   //set the value  
});
console.log(arr); //the array has been overwritten.

或者,如果要保留原始数组,可以在执行上述过程之前复制它。要制作副本,可以使用:

4


将其替换为数组的索引。

1
array[index] = new_value;


与基本for循环相比,使用数组对象方法可以修改数组内容,但这些方法缺少一个重要的功能。运行时不能修改索引。

例如,如果要删除当前元素并将其放置到同一数组中的另一个索引位置,则可以轻松执行此操作。如果您将当前元素移动到前一个位置,那么在下一个迭代中就没有问题了,您将得到下一个相同的项目,就好像您没有做任何事情一样。

当索引计数达到5时,考虑将索引位置5处的项移动到索引位置2处的代码。

1
2
3
4
5
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处的代码。

1
2
3
4
5
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循环中,当向前移动数组项时,您需要减小索引值,以便在下次运行时索引保持在相同的位置,并且您仍然可以评估移动到已删除项位置的项。这对于数组方法是不可能的。不能更改索引。检查以下代码

1
2
3
4
5
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
var data = [1,2,3,4];
data.forEach( (item, i, self) => self[i] = item + 10 );

给出结果:

1
[11,12,13,14]

对于箭头样式的函数,self参数不是必需的,因此

1
data.forEach( (item,i) => data[i] = item + 10);

也可以工作。


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

1
2
3
4
5
6
7
8
9
10
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' ]

在前面插入元素:

1
2
3
4
5
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' ]

在后面插入元素:

1
2
3
4
5
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']

更换元件:

1
2
3
4
5
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"插入,否则将无法按预期进行。