Perform deep copy of object in JavaScript/jQuery
This post will discuss how to efficiently perform a deep copy of an object in JavaScript and jQuery.
Deep copying an object in JavaScript means creating a new object that has the same properties and values as the original object, but is independent from it. This means that modifying the new object will not affect the original object, and vice versa. An object in JavaScript can contain circular references, functions, Array, Map, Set, Blob, Date, RegExp, and several other complex types, it is not recommended writing our own deep clone function. However, there are several third-party libraries that offers deep cloning functionality:
1. Using Lodash Library
The Lodash library has the _.cloneDeep function that can recursively clone the value of an object, including any nested objects, arrays, functions, dates, etc. It also preserves the prototype chain and the constructor of the original object. Following is a simple example demonstrating usage of this function:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// import lodash module let _ = require('lodash'); const obj = { string: 'primitive string', stringObj: new String('string object'), number: 100, numberObj: new Number(100), bigInt: BigInt("9007199254740991"), nan: NaN, array: [1, 2, 3], arrayObj: new Array(2), bool: false, boolobj: new Boolean(false), nul: null, date: new Date(), undef: undefined, inf: Infinity, regExp: /(\w+)\s(\w+)/ } const clone = _.cloneDeep(obj); console.log(clone); |
Output:
{
string: ‘primitive string’,
stringObj: [String: ‘string object’],
number: 100,
numberObj: [Number: 100],
bigInt: 9007199254740991n,
nan: NaN,
array: [ 1, 2, 3 ],
arrayObj: [ undefined, undefined ],
bool: false,
boolobj: [Boolean: false],
nul: null,
date: 2020-10-02T11:21:37.553Z,
undef: undefined,
inf: Infinity,
regExp: /(\w+)\s(\w+)/
}
2. Using jQuery
Alternatively, we can deep clone an object in JavaScript using jQuery Library. We can use the jQuery’s $.extend() function, which copies all the properties and values from one or more source objects to a target object. We can pass a boolean value as the first argument to indicate whether we want a deep or shallow copy. For example, when the first argument to $.extend is true, the merge becomes recursive and performs a deep copy.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// import jQuery library const { JSDOM } = require("jsdom"); const { window } = new JSDOM(); let $ = require("jquery")(window); const obj = { string: 'primitive string', stringObj: new String('string object'), number: 100, numberObj: new Number(100), bigInt: BigInt("9007199254740991"), nan: NaN, array: [1, 2, 3], arrayObj: new Array(2), // array length is lost bool: false, boolobj: new Boolean(false), nul: null, date: new Date(), undef: undefined, // property is lost inf: Infinity, regExp: /(\w+)\s(\w+)/ } const clone = $.extend(true, {}, obj); // pass 'true' for a deep copy console.log(clone); |
Output:
{
string: ‘primitive string’,
stringObj: [String: ‘string object’],
number: 100,
numberObj: [Number: 100],
bigInt: 9007199254740991n,
nan: NaN,
array: [ 1, 2, 3 ],
arrayObj: [],
bool: false,
boolobj: [Boolean: false],
nul: null,
date: 2020-10-02T11:22:42.848Z,
inf: Infinity,
regExp: /(\w+)\s(\w+)/
}
3. Using custom function
This involves writing our own function that recursively copies all the properties and values from the original object to a new object, taking care of nested objects, arrays, and other complex types. This way, we get a new object that is identical to the original one in every aspect, but has a different reference. For example, the following code can be used as a starting point for all our copying needs:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
deepClone = function(obj) { if (obj == null || typeof obj !== 'object') { return obj; } if (obj instanceof Date) { return new Date(obj); } if (obj instanceof String) { return new String(obj); } if (obj instanceof Number) { return new Number(obj); } if (obj instanceof Boolean) { return new Boolean(obj); } if (obj instanceof RegExp) { return new RegExp(obj); } // handle other objects if required let clone = {}; if (obj instanceof Array) { clone = new Array(obj.length); } for (let key in obj) { clone[key] = deepClone(obj[key]); } return clone; }; const obj = { string: 'primitive string', stringObj: new String('string object'), number: 100, numberObj: new Number(100), bigInt: BigInt("9007199254740991"), nan: NaN, array: [1, 2, 3], arrayObj: new Array(2), bool: false, boolobj: new Boolean(false), nul: null, date: new Date(), undef: undefined, inf: Infinity, regExp: /(\w+)\s(\w+)/ } console.log(deepClone(obj)); |
Output:
{
string: ‘primitive string’,
stringObj: [String: ‘string object’],
number: 100,
numberObj: [Number: 100],
bigInt: 9007199254740991n,
nan: NaN,
array: [ 1, 2, 3 ],
arrayObj: [ <2 empty items> ],
bool: false,
boolobj: [Boolean: true],
nul: null,
date: 2020-10-02T11:23:21.746Z,
undef: undefined,
inf: Infinity,
regExp: /(\w+)\s(\w+)/
}
This function is the most flexible and reliable, but it is also the most complex and time-consuming. It requires us to handle different types of objects and values, and to check for circular references or infinite recursion. It also may not work for some objects that have non-enumerable or non-writable properties, or that use getters and setters.
That’s all about performing a deep copy of an object in JavaScript and jQuery.
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)