It turns out there are a lot of different ways to duplicate objects
In JavaScript, a variable can store two types of data:
- Primitive
- Reference
When we copy primitive values, only the value will be copied.
var a = 10;var b = a;console.log(a, b); // 10, 10b = 20;console.log(a, b); // 10, 20
But when we copy reference values, the memory address of the object is shared.
var a = { one : 1};var b = a;a.one = 2;console.log(b.one); // 2b.one = 3;console.log(a.one); //3
Once we change the property of a
or b
, we are changing the value at the address of the object.
If we want to copy a primitive value, we can use assignment operator (=)
, but for objects we cannot use the assignment operator.
When copying objects, there are two types:
- Shallow copy
- Deep copy
When we shallow-copy a source object to a target object, if the property value of the source object is primitive, then the value is copied to the target object. But if the property value of the source object is reference, then the reference is shared between the source and target objects.
In a deep copy, all the properties (including reference) of the source object are copied as values to the target object. There is no sharing of the reference between the source and target objects.
Shallow Copy
Using the spread operator
var obj = {one : 1, two : 2}; var copiedObj = {...obj}; copiedObj.one = 3; console.log(copiedObj.one); // 3 console.log(obj.one); // 1
The spread operator will copy all the enumerable properties of the obj
to the copiedObj
.
Using loop
function copy(sourceObj) { let targetObj = {}; let keys = Object.keys(sourceObj); for (let i = 0, len = keys.length; i < len; i++) { let key = keys[i]; targetObj[key] = sourceObj[key]; } return targetObj; }
This above code will loop through all the properties of the object and copy the value to the target object.
Object.assign
var source = {one : 1, nested: {two : 2}};var target = Object.assign({}, source);
Deep Copy
Using JSON.stringify
and JSON.parse
function JSONCopy(source) { return JSON.parse(JSON.stringify(source)); }
JSON.stringify
and JSON.parse
only work with number, string, and object literals and don’t support function or symbol properties.
Also, if the value of the property in the object is Date
, then using JSON.stringify
will convert the Date object
to a string.
var a = { d : new Date() };var b = JSON.parse(JSON.stringify(a));b ; // {d: "2019-11-26T00:28:18.775Z"}
What is a circular object?
var a = {}; a.a = a;
Circular objects are objects that have a property value referencing to themselves.
When we perform a deep copy of a circular object, it goes on endlessly. JSON.stringify/parse
will throw an exception error when we perform a deep copy on a circular object.
We can use Object.assign
to copy a circular object — but avoid creating a circular object in the first place.
Implementing Custom Clone
In the deepClone
method, we will loop through all the properties of the object. If the value of the object is primitive, just copy it. If the value is reference, call the deepclone
method.
function isObject(obj) { var type = typeof obj; return (type === 'function' || type === 'object') && !!obj; // this will handle obj , null and undefined }; function deepClone(src) { let target = {}; let keys = Object.keys(src); for (let i = 0, len = keys.length; i < len; i++ ) { let key = keys[i]; if (src.hasOwnProperty(key)) { // if the value is a referece(object), recursively copy all properties by calling deepClone let val = src[key]; let isObj = isObject(val); if (isObj) { target[key] = deepClone(val); } else { target[key] = val; } } } return target; }
All the above methods only focus on objects, not arrays. They may not work for arrays.
Source: medium