0

I don't understand why this behavior is happening. Lets say I define an object and make an array of 3 of this object. If I modify the objects in the array, it affects all instances of the object? Could someone explain why this is? Also, how do I make an array with independent "Copies" of the object to get the desired behavior? Thanks!

example

testObject = {"value1":"a","value2":"b"};
objArray = [];
for(i=0; i < 3; i++){
   var newobj = testObject; //make a new testObject
   objArray.push(newobj); //push new object to array
   }
delete objArray[0].value2 // Desired, delete value 2 ONLY from array object 0
objArray[2].value2 //Undefined? Why is value2 missing from object 2 
testObject.value2 //Undefined? Why is value2 missing from original object?
Dani
  • 1,220
  • 7
  • 22
  • *"`var newobj = testObject; //make a new testObject`"*: the comment is wrong. This does not create a new object, but shares an existing one. – trincot Dec 04 '17 at 19:04
  • Possible duplicate of [How do I correctly clone a JavaScript object?](https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object) – Dani Dec 04 '17 at 19:05
  • By the way are `value1` and `value2` just placeholder names or are you actually intending to use an Object like an array? – Explosion Pills Dec 04 '17 at 19:13

3 Answers3

2

As opposed to primitives (strings, numbers, booleans, symbols null, undefined), objects in javascript are passed by reference. Variables serve as placeholders/pointers to these objects. To create a copy of an object without the risk of mutation you'd use spread (barring compatibility):

const newObject = { ...testObject };

or traditionally, Object.assign(), passing an empty object literal to avoid mutability of the original testObject:

const newObject = Object.assign({}, testObject);

As far as deep cloning, MDN suggests using a combination of JSON.parse() and JSON.stringify(). So for example:

const testObject = { value: "a", other: { value2: b } }; 

const newObject = JSON.parse(JSON.stringify(testObject));
Carl Edwards
  • 13,826
  • 11
  • 57
  • 119
  • Good catch. Fixed. – Carl Edwards Dec 04 '17 at 19:08
  • This ALMOST works, but the same problem happens if there are objects within the object. How do I make those independent as well? do I have to layer the Object Assign? Example: testObject = {"value:"a", "Other":{"value2":"b"}}; newObject = Object.assign({}, testObject); delete newObject.Other.value2 testObject.Other.value2 //Undefined – Dani Dec 04 '17 at 19:16
  • There is no passing by reference in JS. You're passing a reference to an object by value. – Mvarta Dec 04 '17 at 19:35
0

You are pushing same object's reference again and again in the loop.

for(i=0; i < 3; i++){
   var newobj = testObject; //no new object,same object's reference again 
   objArray.push(newobj); //push new object to array
   }

It should be

for(i=0; i < 3; i++){
   var newobj =  {"value1":"a","value2":"b"}; //make a new testObject
   objArray.push(newobj); //push new object to array
   }
TruckDriver
  • 1,383
  • 13
  • 28
0

When creating an Object in JavaScript, you are actually creating a reference to that object. You can store this in a variable and pass it around ... perhaps append it to an array. When you go to do operations on an object reference, it finds the original object that the reference points to and updates it. Thus when you use .push it's not creating a new object but simply pushing the reference to that object. If you update it in one spot it will update it in the other and any others where you have assigned that reference.

Copying an object into a new object is generally called cloning. There are a lot of different ways to clone objects in JavaScript with varying results.

You can use var newobj = { ...testObject } as the other answer suggests. This spread operator essentially copies the properties of testObject and creates a new object (declared with the outer { }). The reference to that new object is then assigned to newobj. You can think of it as doing this:

var newobj = {
  value1: testObject.value1,
  value2: testObject.value2,
};

However, you should keep in mind that this gives you only one level of cloning. That is to say if your object contains other objects then the reference to that object will be assigned as the property rather than a clone of that object. For example: let's say you had:

var testObject = { obj: { a: "b" } };
var newobj = { ...testObject };
delete testObject.obj.a;
console.log(newobj); // { obj: {} }

In order to solve this, you need to do what is called a deep clone which in JavaScript can be done by recursively cloning object properties that are also objects. There are a bunch of ways to do this including libraries like lodash or home-grown functions. One example on SO: What is the most efficient way to deep clone an object in JavaScript?

Finally, if testObject is supposed to be something like an object template or initial state from which other newobj are derived, it might make more sense to use a function:

function newObjFactory() {
  return {
    value1: "a",
    value2: "b",
  };
}

Then you can do var newobj = newObjFactory() and you'll get a new object each time since a new object is created by the function each time it's called and returned.

Explosion Pills
  • 188,624
  • 52
  • 326
  • 405