Javascript:forEach() 循环来填充数组 - 闭包问题

Javascript: forEach() loop to populate an array - closure issue

提问人:nadir 提问时间:8/9/2016 最后编辑:Albzinadir 更新时间:11/1/2023 访问量:17652

问:

假设我们有一个对象数组,例如:

var fruits = [ {name:"banana", weight:150},{name:"apple", weight:95},{name:"orange", weight:160},{name:"kiwi", weight:80} ];

我想用“fruits”数组中的项目填充一个“heavy_fruits”数组,上面的权重> 100。这是我的代码:

var heavy_fruits = [];
myfruit = {};

fruits.forEach(function(item,index) {
  if ( item.weight > 100 ) { 
    myfruit ["name"] = item.name;
    myfruit ["weight"] = item.weight;
  }

heavy_fruits.push(myfruit);
});

但是,它显示: 名称:“orange”, weight:160 名称:“orange”, weight:160 名称:“orange”, weight:160 名称:“orange”, weight:160

我知道这是混合闭包和循环的问题......但我读了一篇文章(http://zsoltfabok.com/blog/2012/08/javascript-foreach/),解释说我会使用 forEach 循环而不是经典的 for 循环来避免此类问题。

我知道我可以使用filter()等数组方法,但我是故意问的,因为我实际上遇到了一个更大的函数的麻烦,我不能在这里公开......所以我试图用“水果”来总结和简化我的问题描述。

JavaScript 数组循环 foreach 闭包

评论

2赞 Tushar 8/9/2016
myfruit引用同一对象。移入回调。我建议使用 .myfruit = {};forEachfiltervar heavy_fruits = fruits.filter(f => f.weight > 100);
0赞 Tushar 8/9/2016
这不是闭包问题,而是关于引用同一个对象。
0赞 8/9/2016
基本上,您正在做的是改变对象的属性,并且由于数组实际上存储了对同一对象的引用,因此当您更改对象的某个属性时,更改在每个引用中都是可见的。
0赞 Redu 8/10/2016
在所有数组方法中,有 <= 2% 的用例。对于这种特殊情况,您应该使用 或 。forEachfilterreduce
0赞 Kate 12/30/2019
我遇到了与原始海报相同的问题,为我修复它的事情是 Tushar 上面的第一条评论。声明空对象的变量必须位于 .forEach

答:

3赞 Gangz 8/9/2016 #1
 fruits.forEach(function (item, index) {
  if (item.weight > 100) {
    myfruit = {};
    myfruit["name"] = item.name;
    myfruit["weight"] = item.weight;
    heavy_fruits.push(myfruit);
  }
}); 

评论

1赞 Tushar 8/9/2016
编辑更多信息。不鼓励使用纯代码和“尝试此”答案,因为它们不包含可搜索的内容,并且解释为什么有人应该“尝试此操作”。我们在这里努力成为知识的资源。
0赞 Tushar 8/9/2016
这与所讨论的代码没有什么不同。除了,这将给出两个橙子的数组而不是四个橙子
0赞 Gangz 8/9/2016
heavy_fruits.push(我的水果);超出了 if 范围 ..这就是问题所在
0赞 Gangz 8/9/2016
这是一个代码问题......我没有发现任何其他问题
6赞 user5466293 8/10/2016 #2
var heavy_fruits = [];
myfruit = {}; // here's your object

fruits.forEach(function(item,index) {
    if ( item.weight > 100 ) { 
        myfruit ["name"] = item.name;
        myfruit ["weight"] = item.weight; // you modify it's properties
    }

    heavy_fruits.push(myfruit); // you push it to the array
});

你最终会得到一个数组。[myfruit, myfruit, myfruit, myfruit]

现在,如果修改代码中的任意位置,则每次出现 时都会看到更改。为什么?myfruitmyfruit

因为您正在修改对对象的引用。 在此示例中,数组仅存储对象的副本。如果你改变其中一个,每一个都会改变,因为它们都是参考。

要在每次迭代中解决这个问题,您应该创建一个新对象,然后对它做一些事情。

顺便说一句,事实上,你可以是这样的:if

if ( item.weight > 100 ) { 
    heavy_fruits.push(item); // if `item` only has `name` and `weight` properties
}

评论

2赞 nadir 8/10/2016
非常感谢所有花时间帮助我找到解决方案的人:Albzi、Tushar、Adrian 和 Ganga。但是,Adrian 提供了解决方案 + 对我的问题的明确解释 + 为什么它不起作用 + 我如何解决它。我已经为这个问题苦苦挣扎了好几天!问候
2赞 kevin ternet 8/10/2016 #3

较短的将使用过滤器

var heavy_fruits = fruits.filter(x => x.weight > 100);

但是,如果你真的想使用forEach,请这样做

var heavy_fruits = [];
fruits.forEach(x => {if(x.weight > 100) heavy_fruits.push(x)} );
0赞 alex 11/1/2023 #4

forEach不仅是一个数组方法,所以你可能会遇到它 DOM 元素列表,可能没有映射方法。

不幸的是,它没有返回数组(而不是),它的用法是推送到您应该预先定义的数组,就像前面的答案一样。forEachmap

var values = Array();
var subelem = document.querySelectorAll('#elemid subelem');
values.push(subelem.value);