Reputation: 323
I am new to javascript (new to programming overall, really). And I encountered this behavior of for/in loop that I don't quite understand. The following pieces of code were run with $node command in console.
code_0:
var result = {};
var list = ['A', 'B', 'C'];
for(var index in list){
var id = list[index];
result[id] = {};
result[id]['name'] = id;
}
console.log(result);
result_0:
{ A: { name: 'A' }, B: { name: 'B' }, C: { name: 'C' } }
code_1:
var result = {};
var list = ['A', 'B', 'C'];
var INIT = {'a': 0, 'b': 0, 'c': 0,}
for(var index in list){
var id = list[index];
result[id] = INIT;
result[id]['name'] = id;
}
console.log(result);
result_1:
{ A: { a: 0, b: 0, c: 0, name: 'C' },
B: { a: 0, b: 0, c: 0, name: 'C' },
C: { a: 0, b: 0, c: 0, name: 'C' } }
I can understand why code_0 would give result_0. But result_1 is what I don't understand. I expected result_1 to be:
{ A: { a: 0, b: 0, c: 0, name: 'A' },
B: { a: 0, b: 0, c: 0, name: 'B' },
C: { a: 0, b: 0, c: 0, name: 'C' } }
What's the difference between code_0 and code_1? Why would code_1 give result_1?
EDIT: * Add tags related to the question. * Change title. (Title used to be about for/in loops)
Upvotes: 2
Views: 128
Reputation: 707318
Objects are assigned by reference (not by copy) in Javascript. This is a common point of confusing and learning when first getting up to speed in Javascript.
In your code_1
block, this line of code:
result[id] = INIT;
is assigning a reference to the exact same object in every iteration of your loop, thus they all point to the same object and will all have the same properties (since they are all the same object). So, any further changes to result[id]
are really just changes to INIT which is the same object that all the slots in result
are using so they all appear to change at once.
In Javascript, if you want a separate copy of an object in each assignment, then you have to create a new object before assigning. In the latest browsers, you can use Object.assign()
to copy enumerable properties from one object to another.
Using Object.assign()
, here's one way around the issue that makes a copy of the object before assigning it:
var result = {};
var list = ['A', 'B', 'C'];
var INIT = {'a': 0, 'b': 0, 'c': 0,}
for(var index in list){
var id = list[index];
// make a new object that is a copy of INIT
var obj = Object.assign({}, INIT);
obj.name = id;
// put that new object into result[id]
result[id] = obj;
}
console.log(result);
For older browsers, there's a polyfill for Object.assign()
here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Here's a little simpler example:
var items = {a:1, b:2, c:3};
var list1 = items;
var list2 = items;
list1.a = 10;
console.log(items); // {a:10, b:2, c:3}
console.log(list1); // {a:10, b:2, c:3}
console.log(list2); // {a:10, b:2, c:3}
console.log(list1 === items); // true, they are the same object
console.log(list2 === items); // true, they are the same object
You can see that all three variables show the same values because they all point to the exact same object so modifying the object via any one of the three variables make the exact same change.
Upvotes: 6