Use JSON for deep copy
var newObject = JSON.parse(JSON.stringify(oldObject))
var oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
var newObject = JSON.parse(JSON.stringify(oldObject));
newObject.address.city = 'Delhi';
console.log('newObject');
console.log(newObject);
console.log('oldObject');
console.log(oldObject);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Answer from Nikhil Mahirrao on Stack OverflowUse JSON for deep copy
var newObject = JSON.parse(JSON.stringify(oldObject))
var oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
var newObject = JSON.parse(JSON.stringify(oldObject));
newObject.address.city = 'Delhi';
console.log('newObject');
console.log(newObject);
console.log('oldObject');
console.log(oldObject);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
No such functionality is built-in to ES6. I think you have a couple of options depending on what you want to do.
If you really want to deep copy:
- Use a library. For example, lodash has a
cloneDeepmethod. - Implement your own cloning function.
Alternative Solution To Your Specific Problem (No Deep Copy)
However, I think, if you're willing to change a couple things, you can save yourself some work. I'm assuming you control all call sites to your function.
Specify that all callbacks passed to
mapCopymust return new objects instead of mutating the existing object. For example:mapCopy(state, e => { if (e.id === action.id) { return Object.assign({}, e, { title: 'new item' }); } else { return e; } });This makes use of
Object.assignto create a new object, sets properties ofeon that new object, then sets a new title on that new object. This means you never mutate existing objects and only create new ones when necessary.mapCopycan be really simple now:export const mapCopy = (object, callback) => { return Object.keys(object).reduce(function (output, key) { output[key] = callback.call(this, object[key]); return output; }, {}); }
Essentially, mapCopy is trusting its callers to do the right thing. This is why I said this assumes you control all call sites.
javascript - Object copy using spread syntax actually shallow or deep? - Stack Overflow
javascript - Does Spread Syntax create a shallow copy or a deep copy? - Stack Overflow
Efficient deep copy?
Yes. If you create your own "deep copying" function from scratch you can achieve a good performance.
Something like this: https://github.com/hex13/transmutable/blob/master/copyDeep.js
More on reddit.comTwo exceptional use cases for the spread operator you may not know of
So, for this problem, you have to understand what is the shallow copy and deep copy.
Shallow copy is a bit-wise copy of an object which makes a new object by copying the memory address of the original object. That is, it makes a new object by which memory addresses are the same as the original object.
Deep copy, copies all the fields with dynamically allocated memory. That is, every value of the copied object gets a new memory address rather than the original object.
Now, what a spread operator does? It deep copies the data if it is not nested. For nested data, it deeply copies the topmost data and shallow copies of the nested data.
In your example,
const oldObj = {a: {b: 10}};
const newObj = {...oldObj};
It deep copy the top level data, i.e. it gives the property a, a new memory address, but it shallow copy the nested object i.e. {b: 10} which is still now referring to the original oldObj's memory location.
If you don't believe me check the example,
const oldObj = {a: {b: 10}, c: 2};
const newObj = {...oldObj};
oldObj.a.b = 2; // It also changes the newObj `b` value as `newObj` and `oldObj`'s `b` property allocates the same memory address.
oldObj.c = 5; // It changes the oldObj `c` but untouched at the newObj
console.log('oldObj:', oldObj);
console.log('newObj:', newObj);
.as-console-wrapper {min-height: 100%!important; top: 0;}
Run code snippetEdit code snippet Hide Results Copy to answer Expand
You see the c property at the newObj is untouched.
How do I deep copy an object.
There are several ways I think. A common and popular way is to use JSON.stringify() and JSON.parse().
const oldObj = {a: {b: 10}, c: 2};
const newObj = JSON.parse(JSON.stringify(oldObj));
oldObj.a.b = 3;
oldObj.c = 4;
console.log('oldObj', oldObj);
console.log('newObj', newObj);
.as-console-wrapper {min-height: 100%!important; top: 0;}
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Now, the newObj has completely new memory address and any changes on oldObj don't affect the newObj.
Another approach is to assign the oldObj properties one by one into newObj's newly assigned properties.
const oldObj = {a: {b: 10}, c: 2};
const newObj = {a: {b: oldObj.a.b}, c: oldObj.c};
oldObj.a.b = 3;
oldObj.c = 4;
console.log('oldObj:', oldObj);
console.log('newObj:', newObj);
.as-console-wrapper {min-height: 100%!important; top: 0;}
Run code snippetEdit code snippet Hide Results Copy to answer Expand
There are some libraries available for deep-copy. You can use them too.
structuredClone() is a new global method for real deep clones, all nested elements of the copy will have new memory addresses.
const obj = { a: "a", nestedObj: { b: "b", c: "c" } };
const newObj = structuredClone(obj);
newObj.a = "different a";
newObj.nestedObj.b = "different b";
obj.nestedObj.c = "original c";
console.log(obj);
console.log(newObj);
// output
//{ a: 'a', nestedObj: { b: 'b', c: 'original c' } }
//{ a: 'different a', nestedObj: { b: 'different b', c: 'c' } }
A variable can contain either a value (in case of primitive values, like 1), or a reference (in case of objects, like { food: "pasta" }. Primitive types can only be copied, and since they contain no properties, the shallow/deep distinction does not exist.
If you considered references themselves as primitive values, b = a is a copy of a reference. But since JavaScript does not give you direct access to references (like C does, where the equivalent concept is a pointer), refering to copying a reference as "copy" is misleading and confusing. In context of JavaScript, "copy" is a copy of a value, and a reference is not considered a value.
For non-primitive values, there are three different scenarios:
- Coreferences, as created by
b = a, merely point to the same object; if you modifyain any way,bis affected the same way. Intuitively you'd say that whichever object you modify it is reflected in the copy, but it would be wrong: there is no copy, there is just one object. Regardless of which reference you access the object from, it is the same entity. It is like slapping Mr. Pitt, and wondering why Brad is mad at you — Brad and Mr. Pitt are the same person, not clones.
let a = {
food: "pasta",
contents: {
flour: 1,
water: 1
}
}
let b = a;
a.taste = "good";
a.contents.flour = 2;
console.log(b);
- Shallow copy makes a copy of an object, but any properties that contain references remain so — resulting in coreferent properties. If you change either object, the other is not affected; but if you change any object that is refered to by a property, you will see the change in the other as well. In this example, you will see
b.contents.flouris affected by the change ina(becauseb.contentsanda.contentsrefer to the same object,{ flour: 1, water: 2 }), buta.tastedoes not exist (sinceaandbare objects in their own right).
let a = {
food: "pasta",
contents: {
flour: 1,
water: 1
}
}
let b = {...a};
a.taste = "good";
a.contents.flour = 2;
console.log(b);
- Deep copy copies each property as well, recursively, so that there are no coreferences — whatever happens to the original object and its properties, the copy is not affected. Here, both
b.tasteandb.contents.flourare unaffected by changes ina.
let a = {
food: "pasta",
contents: {
flour: 1,
water: 1
}
}
let b = structuredClone(a);
a.taste = "good";
a.contents.flour = 2;
console.log(b);
Note that at this time structuredClone is still pretty new; if any users are using older browsers, you might need to use a polyfill.
The spread operator allows you to make a shallow copy. To make a deep copy, use the structuredClone() method.
So in your case it would look like this:
let a = {
food: "pasta",
restaurantName: "myPastPlace"
}
let b = structuredClone(a);
Read more: https://developer.mozilla.org/en-US/docs/Web/API/structuredClone