Modifying a copy of a JavaScript object is causing the original object to change
Asked Answered
C

13

123

I am copying objA to objB

const objA = { prop: 1 }, 
const objB = objA; 
objB.prop = 2;
console.log(objA.prop); // logs 2 instead of 1

same problem for Arrays

const arrA = [1, 2, 3], 
const arrB = arrA; 
arrB.push(4); 
console.log(arrA.length); // `arrA` has 4 elements instead of 3.
Churchill answered 14/3, 2015 at 14:25 Comment(1)
This might help if u're not using jQuery: #728860Spirituel
G
168

It is clear that you have some misconceptions of what the statement var tempMyObj = myObj; does.

In JavaScript objects are passed and assigned by reference (more accurately the value of a reference), so tempMyObj and myObj are both references to the same object.

Here is a simplified illustration that may help you visualize what is happening

// [Object1]<--------- myObj

var tempMyObj = myObj;

// [Object1]<--------- myObj
//         ^ 
//         |
//         ----------- tempMyObj

As you can see after the assignment, both references are pointing to the same object.

You need to create a copy if you need to modify one and not the other.

// [Object1]<--------- myObj

const tempMyObj = Object.assign({}, myObj);

// [Object1]<--------- myObj
// [Object2]<--------- tempMyObj

Old Answer:

Here are a couple of other ways of creating a copy of an object

Since you are already using jQuery:

var newObject = jQuery.extend(true, {}, myObj);

With vanilla JavaScript

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var newObject = clone(myObj);

See here and here

Get answered 14/3, 2015 at 14:33 Comment(7)
@JAAulde, thanks for updating the answers post your update to the question. I am not sure I would have caught that.Get
No problem! When I make such an extensive edit to a question which has been answered, I try to follow up. :)Austriahungary
It might be worth noting this seems to start rearing its head when you create your own objects (e.g. let a={}; let b=a; b.blah=1;console.log(a) ). I'm guessing many novices (like me) won't notice this because many commands output a new object for us (e.g. map/filter/etc). I am struggling to understand why anyone would sanely want the current behavior...Nappie
Object.assign({}, myObj); didn't create a copy for me. Instead var tempMyObj = Object.create(myObj); worked.Gloom
This answer should include how to do this with arrays as well: const arrB = arrA.slice();, const arrB = Array.from(arrA);, or const arrB = [ ...arrA ];.Pu
Note! ^ if your array consists of objects, then the above will not work! The original object will still be modified (the arrays themselves are different, but the object references are the same)Mackinnon
@Get What happens actually on the memory side? Is the same memory allocation assigned to temp variable right? That's why value is changing when changing temp/original one. right? Is my understand correct?Manatee
R
37

To sum it all up, and for clarification, there's four ways of copying a JS object.

  1. A normal copy. When you change the original object's properties, the copied object's properties will change too (and vice versa).
const a = { x: 0}
const b = a;
b.x = 1; // also updates a.x
  1. A shallow copy. Top level properties will be unique for the original and the copied object. Nested properties will be shared across both objects though. Use the spread operator ...{} or Object.assign().
const a = { x: 0, y: { z: 0 } };
const b = {...a}; // or const b = Object.assign({}, a);

b.x = 1; // doesn't update a.x
b.y.z = 1; // also updates a.y.z
  1. A deep copy. All properties are unique for the original and the copied object, even nested properties. For a deep copy, serialize the object to JSON and parse it back to a JS object.
const a = { x: 0, y: { z: 0 } };
const b = JSON.parse(JSON.stringify(a)); 

b.y.z = 1; // doesn't update a.y.z
  1. A full deep copy. With the above technique, property values that are not valid in JSON (like functions) will be discarded. If you need a deep copy and keep nested properties that contain functions, you might want to look into a utility library like lodash.
import { cloneDeep } from "lodash"; 
const a = { x: 0, y: { z: (a, b) => a + b } };
const b = cloneDeep(a);

console.log(b.y.z(1, 2)); // returns 3
  1. Using Object.create() does create a new object. The properties are shared between objects (changing one also changes the other). The difference with a normal copy, is that properties are added under the new object's prototype __proto__. When you never change the original object, this could also work as a shallow copy, but I would suggest using one of the methods above, unless you specifically need this behaviour.
Romance answered 6/7, 2021 at 11:23 Comment(2)
Note that using JSON.stringify to deep copy an object will discard any values that are not valid in JSON (like functions) and any which are not enumerable.Korykorzybski
Thank you for pointing out that ...{} is a shallow copy and does not make unique copies of the nested objects. I was running into this problem and not sure why my original value was being modified!Chervil
E
31

deep clone object with JSON.parse() and JSON.stringify

// Deep Clone
obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));

refrence: this article

Better reference: this article

Edmundson answered 26/9, 2019 at 8:13 Comment(2)
this one is great but it doesnt clone functions inside the objectFriedlander
cloning object which includes multi levels or includes functions is a complicated process, you should use lodash (lodash.com) library to help you in this process or make your own algorithm.Edmundson
M
7

Try using the create() method like as mentioned below.

var tempMyObj = Object.create(myObj);

This will solve the issue.

Mushy answered 10/8, 2018 at 10:6 Comment(1)
Though your answer is correct and absolutely fits here. But there's a downside of using Object.create(). It adds the properties to dunder proto _proto_ of the object and not the object. Object.assign() adds the properties to the object.Truth
M
7

use three dots to spread object in the new variable

const a = {b: 1, c: 0};
let d = {...a};
Merchant answered 2/4, 2021 at 7:32 Comment(2)
Cloud you develop a little bit your answer to explain the reason why spread operator are also working? It may help others understand your answerLewislewisite
this will copy only the top-level properties of the object but if the object has a nested object will still refer to the original nested objects, so this approach will not copy the nested object, it will only include the original nested object.Nevermore
M
6

Try using $.extend():

If, however, you want to preserve both of the original objects, you can do so by passing an empty object as the target:

var object = $.extend({}, object1, object2);


var tempMyObj = $.extend({}, myObj);
Metchnikoff answered 14/3, 2015 at 14:30 Comment(3)
bingo, that was what i about to write.. :)Iota
JavaScript now has a similar function built-in, Object.assign(). Example usage: Object.assign({}, myObj).Xhosa
@RoryO'Kane - this got me what I needed, huge thanks!Grearson
M
4

As I couldn't find this code anywhere around suggested answers for shallow copy/cloning cases, I'll leave this here:

// shortcuts
const {
  create,
  getOwnPropertyDescriptors,
  getPrototypeOf
} = Object;

// utility
const shallowClone = source => create(
  getPrototypeOf(source),
  getOwnPropertyDescriptors(source)
);

// ... everyday code ...

const first = {
  _counts: 0,
  get count() {
    return ++this._counts;
  }
};

first.count;  // 1

const second = shallowClone(first);

// all accessors are preserved
second.count; // 2
second.count; // 3
second.count; // 4

// but `first` is still where it was
first.count;  // just 2

The main difference compared to Object.assign or {...spread} operations, is that this utility will preserve all accessors, symbols, and so on, in the process, including the inheritance.

Every other solution in this space seems to miss the fact cloning, or even copying, is not just about properties values as retrieved once, but accessors and inheritance might be more than welcome in daily cases.

For everything else, use native structuredClone method or its polyfill 👋

Monovalent answered 9/3, 2022 at 18:29 Comment(0)
D
2

This might be very tricky, let me try to put this in a simple way. When you "copy" one variable to another variable in javascript, you are not actually copying its value from one to another, you are assigning to the copied variable, a reference to the original object. To actually make a copy, you need to create a new object use

The tricky part is because there's a difference between assigning a new value to the copied variable and modify its value. When you assign a new value to the copy variable, you are getting rid of the reference and assigning the new value to the copy, however, if you only modify the value of the copy (without assigning a new value), you are modifying the copy and the original.

Hope the example helps!

let original = "Apple";
let copy1 = copy2 = original;
copy1 = "Banana";
copy2 = "John";

console.log("ASSIGNING a new value to a copied variable only changes the copy. The ogirinal variable doesn't change");
console.log(original); // Apple
console.log(copy1); // Banana
console.log(copy2); // John 

//----------------------------

original = { "fruit" : "Apple" };
copy1 = copy2 = original;
copy1 = {"animal" : "Dog"};
copy2 = "John";

console.log("\n ASSIGNING a new value to a copied variable only changes the copy. The ogirinal variable doesn't change");
console.log(original); //{ fruit: 'Apple' }
console.log(copy1); // { animal: 'Dog' }
console.log(copy2); // John */

//----------------------------
// HERE'S THE TRICK!!!!!!!

original = { "fruit" : "Apple" };
let real_copy = {};
Object.assign(real_copy, original);
copy1 = copy2 = original;
copy1["fruit"] = "Banana"; // we're not assiging a new value to the variable, we're only MODIFYING it, so it changes the copy and the original!!!!
copy2 = "John";


console.log("\n MODIFY the variable without assigning a new value to it, also changes the original variable")
console.log(original); //{ fruit: 'Banana' } <====== Ops!!!!!!
console.log(copy1); // { fruit: 'Banana' }
console.log(copy2); // John 
console.log(real_copy); // { fruit: 'Apple' } <======== real copy!
Dukie answered 1/9, 2018 at 19:45 Comment(0)
K
2

If you have the same problem with arrays then here is the solution

let sectionlist = [{"name":"xyz"},{"name":"abc"}];
let mainsectionlist = [];
for (let i = 0; i < sectionlist.length; i++) {
     mainsectionlist[i] = Object.assign({}, sectionlist[i]);
}
Kristofer answered 7/1, 2021 at 7:29 Comment(0)
A
0

In Javascript objects are passed as reference and they using shallow comparison so when we change any instance of the object the same changes is also referenced to the main object.

To ignore this replication we can stringify the JSON object.

example :-

let obj = {
   key: "value"
}
function convertObj(obj){
   let newObj = JSON.parse(obj);
   console.log(newObj)
}
convertObj(JSON.stringify(obj));
Almagest answered 28/3, 2022 at 13:10 Comment(0)
C
0

The following would copy objA to objB without referencing objA

let objA = { prop: 1 }, 
let objB = Object.assign( {}, objA )   
objB.prop = 2;

console.log( objA , objB )
Crotchet answered 2/8, 2022 at 9:44 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Recife
P
0

You can now use structuredClone() for deep object clones :

https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

const newItem = structuredClone(oldItem);
Papyrus answered 5/1, 2023 at 13:17 Comment(0)
P
-3

Serialize the original object into JSON and Deserialize to another object variable of same type. This will give you copy of object with all property values. And any modification to original object will not impact the copied object.

string s = Serialize(object); //Serialize to JSON
//Deserialize to original object type
tempSearchRequest = JsonConvert.DeserializeObject<OriginalObjectType>(s);
Prog answered 20/4, 2021 at 0:6 Comment(1)
This is a JavaScript specific question related to how JavaScript handles references, why is your code example .NET?Heldentenor

© 2022 - 2024 — McMap. All rights reserved.