执行 foreach 时更改数组中的值

IT技术 javascript arrays foreach pass-by-reference
2021-01-21 20:45:32

例子:

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

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

alert(arr);

该数组仍然具有其原始值,有什么方法可以从迭代函数中写入对数组元素的访问权限?

6个回答

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

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,但无论出于何种原因,它都不是;thisundefined如果没有提供第二个参数。

(注意:this如果回调是=>函数,则上述内容不适用,因为this调用此类函数时永远不会绑定到任何内容。)

同样重要的是要记住,Array 原型上提供了一整套类似的实用程序,并且 Stackoverflow 上会弹出许多关于一个或另一个函数的问题,因此最好的解决方案是简单地选择一个不同的工具。你有:

  • forEach 用于对数组中的每个条目进行处理;
  • filter 用于生成仅包含符合条件的条目的新数组;
  • map 用于通过转换现有数组来制作一对一的新数组;
  • some 检查数组中是否至少有一个元素符合某种描述;
  • every检查数组中的所有条目是否与描述匹配;
  • find 在数组中查找值

等等。MDN链接

为什么part(甚至o在 es6 中)是 undefined ?如何获得迭代值?
2021-03-12 20:45:32
要使用this作为第二个参数传递给.forEach(),您需要使用语法传递回调函数function(),因为使用 ES6 的箭头函数() => {}不会绑定上下文。
2021-03-16 20:45:32
谢谢!ES6: arr.forEach((o, i, a) => a[i] = myNewVal)
2021-03-20 20:45:32
为了完整起见,.forEach()还需要第二个参数thisArg,您可以this在回调中使用它注意:这是.forEach不是回调参数的参数。
2021-03-21 20:45:32
@DaniilMashkinpart将是undefined如果数组的元素已经被分配undefined明确。.forEach()和大多数其他数组迭代方法会跳过数组的“空”槽(从未分配任何值的数组条目)
2021-04-08 20:45:32

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

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

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]但不是相同的对象

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);
很好的解释。谢谢你。毕竟我无法得到这样的陈述:In functions, parameters are always passed by value. 第二个例子呢?
2021-03-20 20:45:32
在函数中,参数总是按值传递。营商事工。谢谢。+1
2021-03-22 20:45:32
@7sides,该语句描述了函数参数将始终按值传递。所以对于基元,它将是基元指向的值。对于对象,它将是对象指向的位置。这个 w3schools 页面有一个很好的解释。请参阅参数按值传递对象按引用传递部分
2021-03-27 20:45:32
确实,很好的解释!在其他迭代方法中扩展解释会更好,for...of,map,...新的 for...of 的工作方式与 forEach 非常相似,除了“ index" 丢失以最终更改原始数组(如 forEach 中)。ES5 映射似乎与 forEach 相似,也许只是从映射返回值的可能性从语法的角度来看比使用索引更清晰。
2021-04-10 20:45:32

数组:[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 );

@AnupamMaurya 这根本不是真的。map绝对可以改变它的数组forEach,一般来说,不会。
2021-03-16 20:45:32
“Array.prototype.map() 修改原始数组,通过重新分配arr变量实现” .map() 永远不会修改原始数组,而是创建一个新数组。变量的重新分配不会改变原始对象。
2021-03-19 20:45:32
@SebastianSimon,感谢您的回复。我想添加的所有内容,forEach 覆盖数据,但地图不能,它会创建一个新副本。
2021-03-21 20:45:32
let arr1 = ["1", 2, 3, 4]; arr1.map(function(v) { return "foo"+ v; }); console.log( arr ); Array.prototype.map() 从不修改原始数组,forEach mutate。
2021-03-23 20:45:32
比别人清楚
2021-03-26 20:45:32

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

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

arr[index] = 'new value';

@Bergi:不,类型无关紧要。所有值都在赋值时复制——JavaScript 中唯一的值是基元和引用。原语和引用都在赋值时复制。
2021-03-28 20:45:32
不是粗略的概括。一个完全正确和绝对的陈述。Javascript 按值传递引用。Javascript 总是按值传递。
2021-04-01 20:45:32
这取决于它part是否被复制的类型——虽然你说的对,变量不是指针,而是一个值
2021-04-08 20:45:32
说“JavaScript 是按值传递的”是一种粗略的概括。JavaScript 中有引用类型。值类型按值传递。
2021-04-08 20:45:32
@Bergi 仅仅因为一种语言有一种称为引用的东西,并不意味着它使用按引用传递。引用可以(并且是)按值传递,即引用的值被复制并且该副本用作参数。
2021-04-10 20:45:32

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

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

也有效。