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.
30 Answers
Answers 1
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.
Answers 2
Checkout this benchmark: http://jsperf.com/cloning-an-object/2
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 deep flag is set to false (shallow clone). It is a good option because it includes some extra logic for type validation and doesnt 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. JS 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, .. }
I hope you found this helpful.
Answers 3
Assuming that you have only variables and not any functions in your object, you can just use:
var newObject = JSON.parse(JSON.stringify(oldObject));
Answers 4
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; }
Answers 5
Structured Clones
HTML5 defines a method for creating deep clones of objects. It only works for certain built-in types, but is considerably more flexible than using JSON. The internal structured cloning algorithm also supports an increasing number of types including Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, and Typed Arrays, and supports cyclical/recursive structures.
Asynchronous Cloning
There's no public API specifically for creating structured clones, but posting messages through MessageChannels is a pretty direct approach for asynchronous cloning. Here's one possible ES6 structuredClone(obj)
function returning a Promise
with a structured clone of obj
.
class StructuredCloner { constructor() { this.pendingClones_ = new Map(); this.nextKey_ = 0; const channel = new MessageChannel(); this.inPort_ = channel.port1; this.outPort_ = channel.port2; this.outPort_.onmessage = ({data: {key, value}}) => { const resolve = this.pendingClones_.get(key); resolve(value); this.pendingClones_.delete(key); }; this.outPort_.start(); } clone(value) { return new Promise(resolve => { const key = this.nextKey_++; this.pendingClones_.set(key, resolve); this.inPort_.postMessage({key, value}); }); } } const structuredClone = window.structuredClone = StructuredCloner.prototype.clone.bind(new StructuredCloner);
Example Use:
'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; structuredClone(original).then(clone => { // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }); }; class StructuredCloner { constructor() { this.pendingClones_ = new Map(); this.nextKey_ = 0; const channel = new MessageChannel(); this.inPort_ = channel.port1; this.outPort_ = channel.port2; this.outPort_.onmessage = ({data: {key, value}}) => { const resolve = this.pendingClones_.get(key); resolve(value); this.pendingClones_.delete(key); }; this.outPort_.start(); } clone(value) { return new Promise(resolve => { const key = this.nextKey_++; this.pendingClones_.set(key, resolve); this.inPort_.postMessage({key, value}); }); } } const structuredClone = window.structuredClone = StructuredCloner.prototype.clone.bind(new StructuredCloner); main();
Impractical Synchronous Cloning
Unfortunately, there are not yet any practical options for creating structured clones synchronously. Here are a couple of hacky impractical options.
history.pushState()
and history.replaceState()
both create a structured clone of their first argument, and assign that value to history.state
. You can use this to create a structured clone of any object like this:
const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; };
Example Use:
'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; }; main();
Though synchronous, this can be is extremely slow. It incurs all of the overhead associated with manipulating the browser history. Calling this method repeatedly can cause Chrome to become temporarily unresponsive.
The Notification
constructor creates a structured clone of its associated data. It also attempts to display a browser notification to the user, but this will silently fail unless you have requested notification permission. In case you have the permission for other purposes, we'll immediately close the notification we've created.
const structuredClone = obj => { const n = new Notification('', {data: obj, silent: true}); n.close(); return n.data; };
Example Use:
'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; const structuredClone = obj => { const n = new Notification('', {data: obj, silent: true}); n.close(); return n.data; }; main();
Answers 6
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);
Answers 7
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; }
Answers 8
var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i] && typeof this[i] == "object") { newObj[i] = this[i].clone(); } else { newObj[i] = this[i]; } } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
Answers 9
I know this is an old post, but I thought this may be of some help to the next person who stumbles along.
As long as you don't assign an object to anything it maintains no reference in memory. So to make an object that you want to share among other objects, you'll have to create a factory like so:
var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father);
Answers 10
If you're using it, the underscore.js library has a clone method.
var newObject = _.clone(oldObject);
Answers 11
There’s a library (called “clone”), that does this quite well. It provides the most complete recursive cloning/copying of arbitrary objects that I know of. It also supports circular references, which is not covered by the other answers, yet.
You can find it on npm, too. It can be used for the browser as well as Node.js.
Here is an example on how to use it:
Install it with
npm install clone
or package it with Ender.
ender build clone [...]
You can also download the source code manually.
Then you can use it in your source code.
var clone = require('clone'); var a = { foo: { bar: 'baz' } }; // inital value of a var b = clone(a); // clone a -> b a.foo.bar = 'foo'; // change a console.log(a); // { foo: { bar: 'foo' } } console.log(b); // { foo: { bar: 'baz' } }
(Disclaimer: I’m the author of the library.)
Answers 12
Deep copy by performance:
Ranked from best to worst
- Reassignment "=" (for string / number arrays only)
- Slice (for string / number arrays only)
- Concat (for string / number arrays only)
- Custom For-loop Copy
- Object.assign
- $.extend
- JSON.parse
- _.extend()
- _.cloneDeep()
Deep copy an array of strings or numbers:
When an array contains numbers and strings - functions like .slice(), .concat(), .splice(), and the assignment operator "="; 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:
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") ? copy(v) : v; } return out; } copy(arr1);
Use native solutions:
Object.assign({}, arr1); // Ecmascript 6
Use 3rd-party utility functions:
JSON.parse(JSON.stringify(arr1)); // Native $.extend(true, [], arr1); // Jquery Extend _.extend(arr1); // Underscore _.cloneDeep(arr1); // Lo-dash
Where Jquery's $.extend has better performance:
http://jsperf.com/js-deep-copy/2
http://jsperf.com/jquery-extend-vs-json-parse/2
Answers 13
Here's a version of ConroyP's answer above that works even if the constructor has required parameters:
//If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that's all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } function deepCopy(obj) { if(obj == null || typeof(obj) !== 'object'){ return obj; } //make sure the returned object has the same prototype as the original var ret = object_create(obj.constructor.prototype); for(var key in obj){ ret[key] = deepCopy(obj[key]); } return ret; }
This function is also available in my simpleoo library.
Edit:
Here's a more robust version (thanks to Justin McCandless this now supports cyclic references as well):
/** * Deep copy an object (make copies of all its object properties, sub-properties, etc.) * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone * that doesn't break if the constructor has required parameters * * It also borrows some code from http://stackoverflow.com/a/11621004/560114 */ function deepCopy(src, /* INTERNAL */ _visited) { if(src == null || typeof(src) !== 'object'){ return src; } // Initialize the visited objects array if needed // This is used to detect cyclic references if (_visited == undefined){ _visited = []; } // Otherwise, ensure src has not already been visited else { var i, len = _visited.length; for (i = 0; i < len; i++) { // If src was already visited, don't try to copy it, just return the reference if (src === _visited[i]) { return src; } } } // Add this object to the visited array _visited.push(src); //Honor native/custom clone methods if(typeof src.clone == 'function'){ return src.clone(true); } //Special cases: //Array if (Object.prototype.toString.call(src) == '[object Array]') { //[].slice(0) would soft clone ret = src.slice(); var i = ret.length; while (i--){ ret[i] = deepCopy(ret[i], _visited); } return ret; } //Date if (src instanceof Date){ return new Date(src.getTime()); } //RegExp if(src instanceof RegExp){ return new RegExp(src); } //DOM Elements if(src.nodeType && typeof src.cloneNode == 'function'){ return src.cloneNode(true); } //If we've reached here, we have a regular object, array, or function //make sure the returned object has the same prototype as the original var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__); if (!proto) { proto = src.constructor.prototype; //this line would probably only be reached by very old browsers } var ret = object_create(proto); for(var key in src){ //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc. //For an example of how this could be modified to do so, see the singleMixin() function ret[key] = deepCopy(src[key], _visited); } return ret; } //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that's all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; }
Answers 14
The efficient way to clone an object in one line of code
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; } }); }
Answers 15
In Prototype you would do something like
newObject = Object.clone(myObject);
The Prototype documentation notes that this makes a shallow copy.
Answers 16
Crockford suggests (and I prefer) using this function:
function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject);
It's terse, works as expected and you don't need a library.
EDIT:
This is a polyfill for Object.create
, so you also can use this.
var newObject = Object.create(oldObject);
NOTE: If you use some of this, you may have problems with some iteration who use hasOwnProperty
. Because, create
create new empty object who inherits oldObject
. But it is still useful and practical for cloning objects.
For exemple if oldObject.a = 5;
newObject.a; // is 5
but:
oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false
Answers 17
Shallow copy one-liner (ECMAScript 5th edition) :
var origin = { foo : {} }; var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{}); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true
Update 2015: And shallow copy one-liner (ECMAScript 6th edition) :
var origin = { foo : {} }; var copy = Object.assign({}, origin); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true
Answers 18
function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; }
Answers 19
There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig's jQuery cloner turns arrays with non-numeric properties into objects that are not arrays, and RegDwight's JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers:
function jQueryClone(obj) { return jQuery.extend(true, {}, obj) } function JSONClone(obj) { return JSON.parse(JSON.stringify(obj)) } var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]]; arrayLikeObj.names = ["m", "n", "o"]; var JSONCopy = JSONClone(arrayLikeObj); var jQueryCopy = jQueryClone(arrayLikeObj); alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) + "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) + "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names + "\nAnd what are the JSONClone names? " + JSONCopy.names)
Answers 20
Lodash has a nice _.cloneDeep method: http://lodash.com/docs#cloneDeep
The usual _.clone method also accepts a second parameter to make a deep copy instead of the shallow one: http://lodash.com/docs#clone
_.clone(value [, deep=false, callback, thisArg])
Answers 21
I have two good answers depending on whether your objective is to clone a "plain old javascript object" or not.
Let's also assume that your intention is to create a complete clone with no prototype references back to the source object. If you're not interested in a complete clone, then you can use many of the Object.clone() routines provided in some of the other answers (Crockford's pattern).
For plain old JavaScript objects, a tried and true good way to clone an object in modern runtimes is quite simply:
var clone = JSON.parse(JSON.stringify(obj));
Note that the source object must be a pure JSON object. This is to say, all of its nested properties must be scalars (like boolean, string, array, object, etc). Any functions or special objects like RegExp or Date will not be cloned.
Is it efficient? Heck yes. We've tried all kinds of cloning methods and this works best. I'm sure some ninja could conjure up a faster method. But I suspect we're talking about marginal gains.
This approach is just simple and easy to implement. Wrap it into a convenience function and if you really need to squeeze out some gain, go for at a later time.
Now, for non-plain JavaScript objects, there isn't a really simple answer. In fact, there can't be because of the dynamic nature of JavaScript functions and inner object state. Deep cloning a JSON structure with functions inside requires you recreate those functions and their inner context. And JavaScript simply doesn't have a standardized way of doing that.
The correct way to do this, once again, is via a convenience method that you declare and reuse within your code. The convenience method can be endowed with some understanding of your own objects so you can make sure to properly recreate the graph within the new object.
We're written our own but the best general approach I've seen is covered here:
http://davidwalsh.name/javascript-clone
This is the right idea. The author (David Walsh) has commented out the cloning of generalized functions. This is something you might choose to do, depending on your use case.
The main idea is that you need to special handle the instantiation of your functions (or prototypal classes, so to speak) on a per-type basis. Here, he's provided a few examples for RegExp and Date.
Not only is this code brief but it's also very readable. It's pretty easy to extend.
Is this efficient? Heck yes. Given that the goal is to produce a true deep-copy clone, then you're going to have to walk the members of the source object graph. With this approach, you can tweak exactly which child members to treat and how to manually handle custom types.
So there you go. Two approaches. Both efficient in my view.
Answers 22
I'm going to leave this here for posterity. Creates two instances of the same object. Found it, using it currently. It's simple and easy to use.
var objToCreate = (JSON.parse(JSON.stringify(cloneThis)));
Answers 23
This isn't generally the most efficient solution, but it does what I need. Simple test cases below...
function clone(obj, clones) { // Makes a deep copy of 'obj'. Handles cyclic structures by // tracking cloned obj's in the 'clones' parameter. Functions // are included, but not cloned. Functions members are cloned. var new_obj, already_cloned, t = typeof obj, i = 0, l, pair; clones = clones || []; if (obj === null) { return obj; } if (t === "object" || t === "function") { // check to see if we've already cloned obj for (i = 0, l = clones.length; i < l; i++) { pair = clones[i]; if (pair[0] === obj) { already_cloned = pair[1]; break; } } if (already_cloned) { return already_cloned; } else { if (t === "object") { // create new object new_obj = new obj.constructor(); } else { // Just use functions as is new_obj = obj; } clones.push([obj, new_obj]); // keep track of objects we've cloned for (key in obj) { // clone object members if (obj.hasOwnProperty(key)) { new_obj[key] = clone(obj[key], clones); } } } } return new_obj || obj; }
Cyclic array test...
a = [] a.push("b", "c", a) aa = clone(a) aa === a //=> false aa[2] === a //=> false aa[2] === a[2] //=> false aa[2] === aa //=> true
Function test...
f = new Function f.a = a ff = clone(f) ff === f //=> true ff.a === a //=> false
Answers 24
For future reference, the current draft of Ecmascript 6 introduces Object.assign as a way of cloning objects. Example code would be:
var obj1 = { a: true, b: 1 }; var obj2 = Object.assign(obj1); console.log(obj2); // { a: true, b: 1 }
At the time of writing support is limited to Firefox 34 in browsers so it’s not usuable in production code just yet (unless you’re writing a Firefox extension of course).
Answers 25
// obj target object, vals source object
var setVals = function (obj, vals) { if (obj && vals) { for (var x in vals) { if (vals.hasOwnProperty(x)) { if (obj[x] && typeof vals[x] === 'object') { obj[x] = setVals(obj[x], vals[x]); } else { obj[x] = vals[x]; } } } } return obj; };
Answers 26
Here is a comprehensive clone() method that can clone any js object. It handles almost all the cases:
function clone(src, deep) { var toString = Object.prototype.toString; if(!src && typeof src != "object"){ //any non-object ( Boolean, String, Number ), null, undefined, NaN return src; } //Honor native/custom clone methods if(src.clone && toString.call(src.clone) == "[object Function]"){ return src.clone(deep); } //DOM Elements if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){ return src.cloneNode(deep); } //Date if(toString.call(src) == "[object Date]"){ return new Date(src.getTime()); } //RegExp if(toString.call(src) == "[object RegExp]"){ return new RegExp(src); } //Function if(toString.call(src) == "[object Function]"){ //Wrap in another method to make sure == is not true; //Note: Huge performance issue due to closures, comment this :) return (function(){ src.apply(this, arguments); }); } var ret, index; //Array if(toString.call(src) == "[object Array]"){ //[].slice(0) would soft clone ret = src.slice(); if(deep){ index = ret.length; while(index--){ ret[index] = clone(ret[index], true); } } } //Object else { ret = src.constructor ? new src.constructor() : {}; for (var prop in src) { ret[prop] = deep ? clone(src[prop], true) : src[prop]; } } return ret; };
Answers 27
I usually use var newObj = JSON.parse( JSON.stringify(oldObje) );
but, here's a more proper way:
var o = {}; var oo = Object.create(o); (o === oo); // => false
Watch legacy browsers!
Answers 28
This is the fastest method I have created that doesn't use the prototype, so it will maintain hasOwnProperty in the new object. The solution is to iterate the top level properties of the original object, make 2 copies, delete each property from the original and then reset the original object and return the new copy. It only has to iterate as many times as top level properties. This saves all the if conditions to check if each property is a function/object/string etc, and doesn't have to iterate each descendant property. The only drawback is that the original object must be supplied with its original created namespace, in order to reset it.
copyDeleteAndReset:function(namespace,strObjName){ var obj = namespace[strObjName], objNew = {},objOrig = {}; for(i in obj){ if(obj.hasOwnProperty(i)){ objNew[i] = objOrig[i] = obj[i]; delete obj[i]; } } namespace[strObjName] = objOrig; return objNew; } var namespace = {}; namespace.objOrig = { '0':{ innerObj:{a:0,b:1,c:2} } } var objNew = copyDeleteAndReset(namespace,'objOrig'); objNew['0'] = 'NEW VALUE'; console.log(objNew['0']) === 'NEW VALUE'; console.log(namespace.objOrig['0']) === innerObj:{a:0,b:1,c:2};
Answers 29
I think that this is the best solution if you want to generalize your object cloning algorithm.
It can be used with or without jQuery, although I recommend leaving jQuery's extend method out if you want you the cloned object to have the same "class" as the original one.
function clone(obj){ if(typeof(obj) == 'function')//it's a simple function return obj; //of it's not an object (but could be an array...even if in javascript arrays are objects) if(typeof(obj) != 'object' || obj.constructor.toString().indexOf('Array')!=-1) if(JSON != undefined)//if we have the JSON obj try{ return JSON.parse(JSON.stringify(obj)); }catch(err){ return JSON.parse('"'+JSON.stringify(obj)+'"'); } else try{ return eval(uneval(obj)); }catch(err){ return eval('"'+uneval(obj)+'"'); } // I used to rely on jQuery for this, but the "extend" function returns //an object similar to the one cloned, //but that was not an instance (instanceof) of the cloned class /* if(jQuery != undefined)//if we use the jQuery plugin return jQuery.extend(true,{},obj); else//we recursivley clone the object */ return (function _clone(obj){ if(obj == null || typeof(obj) != 'object') return obj; function temp () {}; temp.prototype = obj; var F = new temp; for(var key in obj) F[key] = clone(obj[key]); return F; })(obj); }
Answers 30
Only when you can use ES6 or transpilers.
Features:
- Won't trigger getter/setter while copying.
- Preserves getter/setter.
- Preserves prototype informations.
- Works with both object-literal and functional OO writing styles.
Code:
function clone(target, source){ for(let key in source){ // use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter. let descriptor = Object.getOwnPropertyDescriptor(source, key); if(descriptor.value instanceof String){ target[key] = new String(descriptor.value); } else if(descriptor.value instanceof Array){ target[key] = clone([], descriptor.value); } else if(descriptor.value instanceof Object){ let prototype = Reflect.getPrototypeOf(descriptor.value); let cloneObject = clone({}, descriptor.value); Reflect.setPrototypeOf(cloneObject, prototype); target[key] = cloneObject; } else { Object.defineProperty(target, key, descriptor); } } let prototype = Reflect.getPrototypeOf(source); Reflect.setPrototypeOf(target, prototype); return target; }
0 comments:
Post a Comment