最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin1浏览0评论

Let's say we have an array of objects like:

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

I want to populate a "heavy_fruits" array with items from the "fruits" array above which weight is > 100. Here is my code:

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

However it shows: name:"orange", weight:160 name:"orange", weight:160 name:"orange", weight:160 name:"orange", weight:160

I know this is an issue with mixing closures and loops... but I read an article (/) explaining that I would avoid this kind of issue using a forEach loop instead of the classic for loop.

I know I can use array methods like filter(), etc. but I'm asking that on purpose since I'm actually having troubles with a much bigger function that I cannot expose here... So I tried to summarize and simplify my issue description with "fruits".

Let's say we have an array of objects like:

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

I want to populate a "heavy_fruits" array with items from the "fruits" array above which weight is > 100. Here is my code:

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

However it shows: name:"orange", weight:160 name:"orange", weight:160 name:"orange", weight:160 name:"orange", weight:160

I know this is an issue with mixing closures and loops... but I read an article (http://zsoltfabok.com/blog/2012/08/javascript-foreach/) explaining that I would avoid this kind of issue using a forEach loop instead of the classic for loop.

I know I can use array methods like filter(), etc. but I'm asking that on purpose since I'm actually having troubles with a much bigger function that I cannot expose here... So I tried to summarize and simplify my issue description with "fruits".

Share Improve this question edited Aug 9, 2016 at 15:53 Albzi 15.6k6 gold badges48 silver badges67 bronze badges asked Aug 9, 2016 at 15:52 nadirnadir 1,3234 gold badges12 silver badges21 bronze badges 5
  • 2 myfruit is referencing to same object. Move myfruit = {}; in the forEach callback. And I'll suggest to use filter as var heavy_fruits = fruits.filter(f => f.weight > 100);. – Tushar Commented Aug 9, 2016 at 15:53
  • It's not closure issue, it's about referencing same object. – Tushar Commented Aug 9, 2016 at 15:55
  • Basically what you are doing is mutating object's properties and because the array is in fact storing references to the same object, when you change one of the object's property, the change is visible in each reference. – user5466293 Commented Aug 9, 2016 at 15:59
  • Amongst all array methods forEach has got <= 2% use case. For this particular case you should use either filter or reduce. – Redu Commented Aug 9, 2016 at 19:26
  • I was having the same issue as the original poster, and the thing that fixed it for me was the first comment above by Tushar. The variable where the empty object is declared must be inside the forEach. – Kate Commented Dec 30, 2019 at 14:38
Add a comment  | 

4 Answers 4

Reset to default 8
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
});

You end up with an array [myfruit, myfruit, myfruit, myfruit].

Now if you modify myfruit anywhere in the code, the change will be visible in every single occurence of myfruit. Why?

Because you are modifying the referenece to the object. In this example, your array stores just copies of your object. And if you change one of it, every single one will change, because they are all references.

To fix this with each iteration you should be creating a new object and then doing some stuff on it.

BTW, as a matter of fact, your if could just be like this:

if ( item.weight > 100 ) { 
    heavy_fruits.push(item); // if `item` only has `name` and `weight` properties
}
 fruits.forEach(function (item, index) {
  if (item.weight > 100) {
    myfruit = {};
    myfruit["name"] = item.name;
    myfruit["weight"] = item.weight;
    heavy_fruits.push(myfruit);
  }
}); 

The shorter would use filter

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

But if you realy want to use forEach do this way

var heavy_fruits = [];
fruits.forEach(x => {if(x.weight > 100) heavy_fruits.push(x)} );

forEach is not only an array method, so you might encounter it with DOM list of elements, that may have no map method.

As forEach is unfortunately not returning an array (as opposed to map), it's usage is pushing to an array you should predefine, as with the previous answers here.

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

评论列表(0)

  1. 暂无评论