What is the most efficient way to clone a JavaScript object? I've seen obj = eval(uneval(o));
being used, but that's non-standard and only supported by Firefox.
I've done things like obj = JSON.parse(JSON.stringify(o));
but question the efficiency.
I've also seen recursive copying functions with various flaws.
I'm surprised no canonical solution exists.
Deep copy by performance: Ranked from best to worst
Deep copy an array of strings or numbers (one level - no reference pointers):
When an array contains numbers and strings - functions like .slice(), .concat(), .splice(), the assignment operator "=", and Underscore.js's clone function; will make a deep copy of the array's elements.
Where reassignment has the fastest performance:
var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];
And .slice() has better performance than .concat(), http://jsperf.com/duplicate-array-slice-vs-concat/3
var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy
Deep copy an array of objects (two or more levels - reference pointers):
var arr1 = [{object:'a'}, {object:'b'}];
Write a custom function (has faster performance than $.extend() or JSON.parse):
function copy(o) {
var out, v, key;
out = Array.isArray(o) ? [] : {};
for (key in o) {
v = o[key];
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
}
return out;
}
copy(arr1);
Use third-party utility functions:
$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash
Where jQuery's $.extend has better performance:
Note: This is a reply to another answer, not a proper response to this question. If you wish to have fast object cloning please follow Corban's advice in their answer to this question.
I want to note that the .clone()
method in jQuery only clones DOM elements. In order to clone JavaScript objects, you would do:
// Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);
More information can be found in the jQuery documentation.
I also want to note that the deep copy is actually much smarter than what is shown above – it's able to avoid many traps (trying to deep extend a DOM element, for example). It's used frequently in jQuery core and in plugins to great effect.
If there wasn't any builtin one, you could try:
function clone(obj) {
if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
return obj;
if (obj instanceof Date)
var temp = new obj.constructor(); //or new Date(obj);
else
var temp = obj.constructor();
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = clone(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
</div>
Code:
// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
if (from == null || typeof from != "object") return from;
if (from.constructor != Object && from.constructor != Array) return from;
if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
from.constructor == String || from.constructor == Number || from.constructor == Boolean)
return new from.constructor(from);
to = to || new from.constructor();
for (var name in from)
{
to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
}
return to;
}
Test:
var obj =
{
date: new Date(),
func: function(q) { return 1 + q; },
num: 123,
text: "asdasd",
array: [1, "asd"],
regex: new RegExp(/aaa/i),
subobj:
{
num: 234,
text: "asdsaD"
}
}
var clone = extend(obj);
This is what I'm using:
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(typeof(obj[i])=="object" && obj[i] != null)
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
Checkout this benchmark: http://jsben.ch/#/bWfk9
In my previous tests where speed was a main concern I found
JSON.parse(JSON.stringify(obj))
to be the fastest way to deep clone an object (it beats out jQuery.extend with deep flag set true by 10-20%).
jQuery.extend is pretty fast when the deep flag is set to false (shallow clone). It is a good option, because it includes some extra logic for type validation and doesn't copy over undefined properties, etc., but this will also slow you down a little.
If you know the structure of the objects you are trying to clone or can avoid deep nested arrays you can write a simple for (var i in obj)
loop to clone your object while checking hasOwnProperty and it will be much much faster than jQuery.
Lastly if you are attempting to clone a known object structure in a hot loop you can get MUCH MUCH MORE PERFORMANCE by simply in-lining the clone procedure and manually constructing the object.
JavaScript trace engines suck at optimizing for..in
loops and checking hasOwnProperty will slow you down as well. Manual clone when speed is an absolute must.
var clonedObject = {
knownProp: obj.knownProp,
..
}
Beware using the JSON.parse(JSON.stringify(obj))
method on Date
objects - JSON.stringify(new Date())
returns a string representation of the date in ISO format, which JSON.parse()
doesn't convert back to a Date
object. See this answer for more details.
Additionally, please note that, in Chrome 65 at least, native cloning is not the way to go. According to this JSPerf, performing native cloning by creating a new function is nearly 800x slower than using JSON.stringify which is incredibly fast all the way across the board.
An Object.assign
method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need.
var clone = Object.assign({}, obj);
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.
The polyfill to support older browsers:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
Assuming that you have only variables and not any functions in your object, you can just use:
var newObject = JSON.parse(JSON.stringify(oldObject));