What is the most efficient way to deep clone an object in JavaScript?

4 736

1 381

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.

jschrab

Posted 2008-09-23T16:26:09.163

Reputation: 5 805

532Eval is not evil. Using eval poorly is. If you are afraid of its side effects you are using it wrong. The side effects you fear are the reasons to use it. Did any one by the way actually answer your question? – Prospero – 2012-03-22T14:08:54.113

13Cloning objects is a tricky business, especially with custom objects of arbitrary collections. Which probably why there is no out-of-the box way to do it. – b01 – 2013-03-11T22:25:34.330

9eval() is generally a bad idea because many Javascript engine's optimisers have to turn off when dealing with variables that are set via eval. Just having eval() in your code can lead to worse performance. – user568458 – 2014-09-08T13:37:45.690

1

Possible duplicate of Most elegant way to clone a JavaScript object

– John Slegers – 2016-02-21T18:21:08.920

1

here's a performance comparison between the most common types of cloning objects: http://jsben.ch/#/t917Z

– EscapeNetscape – 2016-10-17T09:58:47.500

1Note that JSON method will loose any Javascript types that have no equivalent in JSON. For example: JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false})) will generate {a: null, b: null, c: null, g: false} – oriadam – 2017-05-24T13:06:47.167

Answers

4 151

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.

John Resig

Posted 2008-09-23T16:26:09.163

Reputation: 30 979

43

For those who didn't realize, John Resig's answer was probably intended as a kind of response/clarification to ConroyP's answer, instead of a direct reply to the question.

– S. Kirby – 2012-09-02T20:11:31.583

7

@ThiefMaster https://github.com/jquery/jquery/blob/master/src/core.js at line 276 (there's a bit of code that does something else but the code for "how to do this in JS" is there :)

– Rune FS – 2013-03-27T08:16:19.910

7

Here's the JS code behind the jQuery deep copy, for anyone interested: https://github.com/jquery/jquery/blob/master/src/core.js#L265-327

– Alex W – 2013-04-11T14:24:13.630

1https://github.com/jquery/jquery/blob/master/src/core.js#L121-184 – Ethan Reesor – 2014-01-03T07:36:24.630

2

Since there seems to be some contention around using jQuery, thought I would mention that I've extracted the extend functionality out to a standalone script: https://gist.github.com/jonjaques/3036701

– Jon Jaques – 2014-01-09T22:40:49.560

170

Woah! Just to be super-clear: no idea why this response was picked as the right answer, this was a reply to responses given below: https://stackoverflow.com/a/122190/6524 (which was recommending .clone(), which is not the right code to be using in this context). Unfortunately this question has gone through so many revisions the original discussion is no longer even apparent! Please just follow Corban's advice and write a loop or copy the properties directly over to a new object, if you care about speed. Or test it out for yourself!

– John Resig – 2014-01-21T03:37:38.210

If my objects has a reference to a dom element (let say obj.div = document.getElementById('id') ), does this also clone the dom element and puts it on the document? – albanx – 2014-02-28T17:29:51.077

1A non-jQuery equivalent of this would be var newobj = eval(oldobj.toSource()). – Octopus – 2014-05-14T04:56:29.313

@albanx—the short answer is no. If you have a question, ask it as a question. Posting it as a comment to an old question that is now irrelevant is unlikely to be helpful. To clone a DOM element use its cloneNode method. To put it in the document, use insertBefore or appendChild or one of the other insertion or replacement methods.

– RobG – 2014-08-15T23:48:43.070

@RobG I work with javascript complex object with reference to dom, and a clone of them will clone only the reference and not the dom element obviously. I know very well the cloneNode and company jquery functions and the comment it is not irrelevant, is just a completion to the question. – albanx – 2014-08-17T07:40:40.310

@albanx No, you were asking a question in the comments of an answer, which is already frowned upon, and it wasn't any sort of "completion to the question", you were just drawing attention to your own off-topic problem, which gladly for you was met with condescendence from RobG, which you even reply in a pedantic manner ("I know very well the cloneNode and company"), despite being clear by your question (and response) that you are at a novice level with javascript. – Camilo Martin – 2014-09-22T00:21:47.003

@CamiloMartin No camilo, my question is just a point of clarification, what does that clone ( it does not clone referenced objects). Should be clear that this clone only primitive properties of an object. so you and robg think that I should open a new question like "does the reply of this question also satisfy my question?". – albanx – 2014-09-22T14:00:13.230

@albanx You asked if .clone() will insert new objects in the DOM. That's a totally off-topic new question and yes, you should open a new question since this isn't a forum but a Q&A site. (Not that I encourage hijacking forum threads either.) – Camilo Martin – 2014-09-23T01:32:39.577

What about a library like lodash with the clone tool? https://lodash.com/docs#clone

– mrshickadance – 2015-01-14T14:25:09.933

5This is a JavaScript question (no mention of jQuery). – gphilip – 2015-01-22T08:30:39.987

45How would one do this without using jQuery? – Awesomeness01 – 2015-04-30T02:22:57.367

According to the documentation for extend(), deep copy makes sense only for real merges of objects, and has no sense when using extend() to just clone an object. – bearoff – 2015-06-24T13:27:22.683

2This is the very good method, but I have a problem with this because it does NOT convert array properly. it changed array into objects with property names 0,1,2... Therefore I end up using JSON.parse(JSON.stringify(data)) which worked perfectly for me – hehe – 2015-08-19T07:38:38.623

2Be aware that this method will not preserve setters and getters – lepe – 2015-10-20T04:02:31.133

This won't work if the object has cyclic properties. For such cases, if you want to deep copy, use either angular.copy or lodash's _.cloneDeep – satJ – 2015-10-27T16:31:05.433

1Answer is useful if using jQuery and I'm lazy; I'm lazy only. Therefore this answer is not useful. +1 @Corban Brook's answer below. – nf071590 – 2015-12-22T00:36:51.593

1

With ES6 you can invoke the spread operator: let person = { firstname: "foo", lastname: "bar", address: { city: "Baz"} }; let clonePerson={...person }; delete clonePerson.firstname; console.log(clonePerson.firstname);//undefined console.log(person.firstname);//"foo" This only works at the first level deep: delete clonePerson.address.city; console.log(clonePerson.address.city);//undefined console.log(person.address.city);//undefined You'd have to recursively use the spread operator... spread

– Brandon K – 2016-03-01T14:41:26.323

1@superluminary—so you thought it was worth a down vote based on the content, but didn't because of the author? This isn't even an answer to the OP, how can it not cop a down vote? – RobG – 2016-05-23T06:46:46.857

5The question states javascript in both the title and body. It mentions nothing of jQuery. So, why is a jQuery answer so highly voted? – Twifty – 2016-10-09T16:57:03.803

without jquery Object.create method can be used to clone a object.

var newObject = Object.create(oldObject); – Manoj Yadav – 2017-04-12T12:48:00.313

1Hey Guys ... How about es6 Object.assign() ? – Yuvaraj – 2017-04-24T04:53:53.490

1@John Resig: That approach fails when your Object contains any type of data that is more complicated than a scalar. – HoldOffHunger – 2017-08-15T13:46:07.717

1Given the question the accepted answer ought to be one that uses vanilla JS in my opinion. – faintsignal – 2017-09-06T19:10:08.373

Attempted to use Object.assign() - it does not even pretend to work as expected - this solution worked a treat! – 1owk3y – 2017-11-30T23:05:41.197

3Down-voted for answering a JavaScript question with a jQuery answer. Down-voted the OP too for cheaping out. Frameworks and libraries are not written well and dump tons of bandwidth on clients. Do it right the first time: professionals use real code. – John – 2017-12-30T00:27:36.080

1Since it only offers a jQuery solution, this answer should not be marked as the accepted correct answer. It's just causing a confusing feedback loop. – metaColin – 2018-01-18T19:44:31.340

1How would one do this without using jQuery? – Joe – 2018-03-04T09:01:18.420

I took a wee bitty peep at the source code (https://code.jquery.com/jquery-3.3.1.js)... disgusting. Simply vomit-inducing wretched garbage. Do not use jQuery. Ever.

– Jack Giffin – 2018-04-07T00:20:49.717

1 991

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.

Corban Brook

Posted 2008-09-23T16:26:09.163

Reputation: 18 443

4

@trysis Object.create is not cloning the object, is using the prototype object... http://jsfiddle.net/rahpuser/yufzc1jt/2/

– rahpuser – 2014-11-25T21:37:12.477

I was scoping this npm module when I found this question. Great answer. I wonder if there's any feedback on that module, though? I glanced at the source and it seems alright. I especially like this quote: "copies the ES5 descriptor of every property that Object.getOwnPropertyDescriptor() returns".

– Jeremy Anderson – 2015-01-10T01:23:04.387

80This method will also remove the keys from your object, which have functions as their values, because the JSON doesn't support functions. – Karlen Kishmiryan – 2015-05-29T09:52:16.950

27Also keep in mind that using JSON.parse(JSON.stringify(obj)) on Date Objects will also convert the date back to UTC in the string representation in the ISO8601 format. – dnlgmzddr – 2015-07-30T21:37:10.823

1JSON.stringify doesnt convert String or Function since they are not valid JSON. So {type: String, value: 123} would become {"value": 123} which is probably not what you want. – datacarl – 2015-09-30T19:45:55.037

http://jsperf.com/cloning-an-object/101 for latest tests – Josh Mc – 2015-11-28T23:09:01.807

24JSON approach also chokes on circular references. – rich remer – 2016-02-13T05:25:15.133

If you have functions as values of your object - it will NOT work for you. – inferus-vv – 2016-05-04T09:11:50.537

2JSON approach will fail to copy anything that is not part of the JSON spec (json.org) including date objects, functions, circular references among others – Dheeraj Bhaskar – 2016-07-12T11:31:20.657

Just came by to add comment that this throws away values that are Symbol objects as well. – D. Hayes – 2016-10-12T20:23:06.963

Will JSON clone method faster than Object.assign({}, objToClone)? – tom10271 – 2016-10-17T07:45:08.880

3@aokaddaoc no Object.assign is much faster. I added it to the benchmark given in the answer. – velop – 2016-10-23T21:40:15.863

Copying larger objects gets slightly different benchmarks than this. – Íhor Mé – 2016-11-02T16:22:44.900

Also, in lodash version of the benchmark cloneDeep would be correct. clone(a,true) is an inadequate comparison, as it only copies up to 2 depth levels. – Íhor Mé – 2016-11-02T18:00:45.223

Mind that Set doesn't get parsed by JSON.stringify – Íhor Mé – 2016-11-02T18:43:27.807

13@velop , Object.assign({}, objToClone) seems like it does a shallow clone though -- using it while playing around in the dev tools console, the object clone still pointed to a reference of the cloned object. So I don't think it's really applicable here. – Garrett Simpson – 2016-11-03T18:00:14.133

In my experience, this also replaces special chars with their UTF8 representation. – RobertoNovelo – 2016-11-07T22:35:54.833

1Object.assign is NOT a deep clone :( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone – Rico Kahler – 2016-12-18T00:17:42.200

Only properties are cloned not functions, just to mention: for example: {a:function(){alert("hi");}, b: "I am b"} --> here only property b is cloned. – Legends – 2017-01-09T09:23:56.400

What if obj has a circular JSON structure? – Tarun – 2017-01-31T12:35:54.443

Results completely changed with your benchmark, at least for me running a decent version of Chrome. Perhaps the answer should get updated regarding this? – Florian Loch – 2017-03-06T17:05:44.753

1Also MDN states that Object.assign() CANNOT be used for deep cloning. – Florian Loch – 2017-03-07T11:06:28.683

A recursive deep clone is much faster than the JSON.parse/JSON.stringify approach. See my answer here: https://stackoverflow.com/a/44612374/1110941

– prograhammer – 2017-06-18T06:36:03.153

This also removes the exact type of the object, right? So that instanceof checks no longer work on the resulting object. So this is rather a "primitive" copying than a real deep cloning. – Jenny O'Reilly – 2017-11-12T16:38:06.780

2it's no longer the fastest in Chrome – Andy – 2017-12-04T23:31:48.180

It's also worth noting that properties with undefined values will not be included if you use stringify. This problem is what drove me to this thread, so I can't use stringify. – loctrice – 2018-02-26T16:24:34.883

1@AlexanderF. o1 = {a:1, b:{c:2,d:[1,2,3,{e:555}]}, f: function(){console.log('Hi!')}}; o2 = Object.assign({}, o1); o2.b.c = "It was shallow..."; console.log(o1.b.c); – Jason Goemaat – 2018-07-26T04:26:38.820

@JasonGoemaat you're absolutely right! I remember playing with it a few weeks down the road and finding unexpected results. Forgot about my silly comment here though. Cannot edit anymore so will just delete it. – Alexander F. – 2018-07-26T16:39:38.380

1

“This one line of Javascript made FT.com 10 times slower” @adgad https://medium.com/ft-product-technology/this-one-line-of-javascript-made-ft-com-10-times-slower-5afb02bfd93f

– Cemre – 2018-08-02T22:17:07.153

420

Assuming that you have only variables and not any functions in your object, you can just use:

var newObject = JSON.parse(JSON.stringify(oldObject));

Sultan Shakir

Posted 2008-09-23T16:26:09.163

Reputation: 490

83the con of this approach as I've just found is if your object has any functions (mine has internal getters & setters) then these are lost when stringified.. If that's all you need this method is fine.. – Markive – 2013-01-17T11:00:08.270

31

@Jason, The reason why this method is slower than shallow copying (on a deep object) is that this method, by definition, deep copies. But since JSON is implemented in native code (in most browsers), this will be considerably faster than using any other javascript-based deep copying solution, and may sometimes be faster than a javascript-based shallow copying technique (see: http://jsperf.com/cloning-an-object/79).

– MiJyn – 2013-07-04T07:42:37.710

33JSON.stringify({key: undefined}) //=> "{}" – Web_Designer – 2014-04-30T06:24:36.813

28this technique is going to destroy also all Date objects that are stored inside the object, converting them to string form. – fstab – 2014-08-21T12:51:24.407

it will also fail to copy any RegExp objects within – iss42 – 2014-11-13T13:27:10.193

jQuery 1.5 now has the new jQuery.sub() method (http://api.jquery.com/jQuery.sub/) for anyone is using the library.

– Sultan Shakir – 2011-02-02T02:20:13.067

12

It will fail to copy anything that is not part of the JSON spec (http://json.org/)

– cdmckay – 2016-02-09T15:07:33.613

ALso, looking at the benchmarks now, Chrome is considerably faster with clone(). on mobile chrome, stringify/parse is fastest. – Manuel Graf – 2018-02-08T09:09:22.503

This is a disturbing answer! – 16kb – 2018-06-22T01:05:03.160

1is there any reason why this wouldn't work? this seems amazingly elegant to me. how is its efficiency? – Jason – 2011-09-27T18:07:46.493

so i determined that shallow copying is super fast, between 0 and 2ms for even a HUGE object. your way takes considerably longer, and a deep copy using john's method takes about a third longer still. – Jason – 2011-09-27T23:40:07.073

parse(stringify) is great and elegant solution for most cases. Thanks a lot, Sultan Shakir! – Alkis Mavridis – 2018-11-22T13:58:30.743

299

Structured Cloning

HTML5 defines an internal "structured" cloning algorithm that can create deep clones of objects. It is still limited to certain built-in types, but in addition to the few types supported by JSON it also supports Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays, and probably more in the future. It also preserves references within the cloned data, allowing it to support cyclical and recursive structures that would cause errors for JSON.

Direct Support in Browsers: Coming Soon?

Browsers do not currently provide a direct interface for the structured cloning algorithm, but a global structuredClone() function is being actively discussed in whatwg/html#793 on GitHub and may be coming soon! As currently proposed, using it for most purposes will be as simple as:

const clone = structuredClone(original);

Until this is shipped, browsers' structured clone implementations are only exposed indirectly.

Asynchronous Workaround: Usable.

The lower-overhead way to create a structured clone with existing APIs is to post the data through one port of a MessageChannels. The other port will emit a message event with a structured clone of the attached .data. Unfortunately, listening for these events is necessarily asynchronous, and the synchronous alternatives are less practical.

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();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Example Use:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(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.");
};

main();

Synchronous Workarounds: Awful!

There are no good options for creating structured clones synchronously. Here are a couple of impractical hacks instead.

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 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.onshow = n.close.bind(n);
  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();

user

Posted 2008-09-23T16:26:09.163

Reputation: 1

3@rynah I just looked through the spec again and you're right: the history.pushState() and history.replaceState() methods both synchronously set history.state to a structured clone of their first argument. A little weird, but it works. I'm updating my answer now. – user – 2013-05-06T03:11:03.653

34This is just so wrong! That API is not meant to be used this way. – Fardin K. – 2014-07-31T23:34:51.960

165As the guy who implemented pushState in Firefox, I feel an odd mix of pride and revulsion at this hack. Well done, guys. – Justin L. – 2014-08-14T18:37:25.657

290

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;
}

ConroyP

Posted 2008-09-23T16:26:09.163

Reputation: 34 275

5This function breaks if the object being cloned has a constructor that requires parameters. It seems like we can change it to "var temp = new Object()" and have it work in every case, no? – Andrew Arnott – 2009-10-04T22:06:39.177

1

Similar to limscoder's answer, see my answer below on how to do this without calling the constructor: http://stackoverflow.com/a/13333781/560114

– Matt Browne – 2012-11-11T17:55:03.703

20The JQuery solution will work for DOM elements but not just any Object. Mootools has the same limit. Wish they had a generic "clone" for just any object...

The recursive solution should work for anything. It's probably the way to go. – jschrab – 2008-09-23T17:23:51.520

2For objects that contain references to sub-parts (i.e., networks of objects), this does not work: If two references point to the same sub-object, the copy contains two different copies of it. And if there are recursive references, the function will never terminate (well, at least not in the way you want it :-) For these general cases, you have to add a dictionary of objects already copied, and check whether you already copied it... Programming is complex when you use a simple language – virtualnobi – 2013-11-11T08:34:37.343

The link to Keith Deven's blog post is broken. – Peter Mortensen – 2014-02-22T15:11:31.137

@ConroyP this is not a healty solution in case of circular reference. http://jsfiddle.net/d3KcP/

– Cihad Turhan – 2014-03-27T10:08:22.880

@CihadTurhan added a circular reference fix at jsfiddle.net/d3KcP/9. Note it will add a temporary property to cloned objects

– Garet Claborn – 2015-06-05T11:26:11.447

does not work for Event objects - constructor call fails – kofifus – 2016-09-22T23:30:48.607

1Fails for: clone({isActiveClone: false, isHijackingObjectKeysAGoodIdea: false }) – Phil – 2017-06-30T10:58:04.140

3Andrew, if you change it to var temp = new Object(), then your clone won't have the same prototype as the original object. Try using: 'var newProto = function(){}; newProto.prototype = obj.constructor; var temp = new newProto();' – limscoder – 2011-09-14T15:53:31.357

144

The efficient way to clone(not deep-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.

Read more...

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;
    }
  });
}

Eugene Tiurin

Posted 2008-09-23T16:26:09.163

Reputation: 1 289

92

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);

Kamarey

Posted 2008-09-23T16:26:09.163

Reputation: 7 520

3what about var obj = {} and obj.a = obj – neaumusic – 2015-05-05T22:40:00.617

5I don't understand this function. Suppose from.constructor is Date for example. How would the third if test be reached when the 2nd if test would succeed & cause the function to return (since Date != Object &amp;&amp; Date != Array)? – Adam McKee – 2015-09-01T02:10:41.657

1

@AdamMcKee Because javascript argument passing and variable assignment is tricky. This approach works great, including dates (which indeed are handled by the second test) - fiddle to test here: https://jsfiddle.net/zqv9q9c6/.

– brichins – 2016-06-14T20:39:15.620

*I meant "parameter assignment" (within a function body), not "variable assignment". Although it helps to understand both thoroughly. – brichins – 2016-06-15T16:42:40.370

Nice idea, but doesn't win on performance. I added to the other benchmarks mentioned above: http://jsben.ch/#/r4ZGL

– OzBob – 2016-12-13T01:53:58.447

What about Infinity, NaN, null, and undefined? – Nick Sweeting – 2017-07-10T07:12:56.397

1@NickSweeting: Try - may be it works. If not - fix it and update the answer. That's how it works here in community:) – Kamarey – 2017-07-10T07:20:58.190

This function does not clone the regex in the test, the condition "from.constructor != Object && from.constructor != Array" always returns true for other constructors like Number, Date, and so. – aMarCruz – 2018-10-26T20:23:29.540

82

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;
}

Alan

Posted 2008-09-23T16:26:09.163

Reputation: 241

8This does not seem right. cloneObject({ name: null }) => {"name":{}} – Niyaz – 2013-02-27T14:36:45.080

12This is due to another dumb thing in javascript typeof null &gt; "object" but Object.keys(null) &gt; TypeError: Requested keys of a value that is not an object. change the condition to if(typeof(obj[i])=="object" &amp;&amp; obj[i]!=null) – Vitim.us – 2013-04-16T16:42:45.333

This will assign inherited enumerable properties of obj directly to the clone, and assumes that obj is a plain Object. – RobG – 2016-02-01T22:58:11.937

This also messes up arrays, which get converted to objects with numeric keys. – blade – 2016-11-03T12:29:03.953

Not a problem if you don't use null. – Jorge Bucaran – 2017-03-01T06:17:44.213

68

Deep copy by performance: Ranked from best to worst

  • Reassignment "=" (string arrays, number arrays - only)
  • Slice (string arrays, number arrays - only)
  • Concatenation (string arrays, number arrays - only)
  • Custom function: for-loop or recursive copy
  • jQuery's $.extend
  • JSON.parse (string arrays, number arrays, object arrays - only)
  • Underscore.js's _.clone (string arrays, number arrays - only)
  • Lo-Dash's _.cloneDeep

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:

tfmontague

Posted 2008-09-23T16:26:09.163

Reputation: 7 175

I tested a few and _.extend({}, (obj)) was BY FAR the fastest: 20x faster than JSON.parse and 60% faster than Object.assign, for example. It copies all sub-objects quite well. – Nico – 2016-05-09T19:56:02.353

@NicoDurand - Are your performance tests online? – tfmontague – 2016-05-17T13:36:31.400

3All of your examples are shallow, one level. This is not a good answer. The Question was regarding deep cloning i.e. at least two levels. – Karl Morrison – 2017-04-21T10:02:14.330

1A deep copy is when an object is copied in its' entirety without the use of reference pointers to other objects. The techniques under the section "Deep copy an array of objects", such as jQuery.extend() and the custom function (which is recursive) copy objects with "at least two levels". So, no not all the examples are "one level" copies. – tfmontague – 2017-04-21T15:42:01.623

I like your custom copy function, but you should exclude null values, otherwise all null values are being converted to objects, i.e.: out[key] = (typeof v === "object" &amp;&amp; v !== null) ? copy(v) : v; – josi – 2018-02-01T16:53:21.203

@tfmontague The custom function has a bug, it convert any null value to {}. I had to refactor all of my codebase because I was depending heavily on it. – Hossam Mourad – 2018-03-19T10:49:25.423

@HossamMourad - The bug was fixed by Josi on Feb1 (in the comment above), and I failed to correctly update the answer. Sorry that this bug resulted in a refactor of your code base. – tfmontague – 2018-03-20T15:29:57.547

58

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});

Zibri

Posted 2008-09-23T16:26:09.163

Reputation: 4 239

51

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);

Joe

Posted 2008-09-23T16:26:09.163

Reputation: 187

15This answer is not really relevant because the question is: given instance b how does one create copy c WHILE not knowing about factory a or not wanting to use factory a. The reason one may not want to use the factory is that after instantiation b may have been initialised with additional data (e.g. user input). – Noel Abrahams – 2012-05-08T09:57:53.283

11It's true that this is not really an answer to the question, but I think it's important that it be here because it is the answer to the question I suspect many of the people coming here are really meaning to ask. – Semicolon – 2013-03-06T23:31:03.073

8Sorry guys, I don't really understand why so many upvotes. Cloning an object is a pretty clear concept, you cone an object from ANOTHER object, and it doesn't have much to do with creating a new one with the factory pattern. – opensas – 2014-08-18T12:51:28.387

1

While this works for predefined objects, "cloning" in this manner will not recognize new properties added to the original object. If you create a, add a new property to a, then create b. b will not have the new property. Essentially the factory pattern is immutable to new properties. This is not paradigmatically cloning. See: https://jsfiddle.net/jzumbrun/42xejnbx

– Jon – 2016-09-03T13:59:46.680

1I think this is good advice, generally, since instead of using const defaultFoo = { a: { b: 123 } }; you can go const defaultFoo = () =&gt; ({ a: { b: 123 } }; and your problem is solved. However, it really isn't an answer to the question. It might have made more sense as a comment on the question, not a full answer. – Josh from Qaribou – 2017-02-06T14:25:41.873

49

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.)

pvorb

Posted 2008-09-23T16:26:09.163

Reputation: 3 989

2npm clone has been invaluable to me for cloning arbitrarily nested objects. This is the right answer. – Andy Ray – 2015-09-10T03:21:23.167

what is the performance of your lib compared to let's say JSON.parse(JSON.stringify(obj))? – pkyeck – 2016-12-01T16:03:35.670

Here's a library that states that there are faster options. Haven't tested though. – pvorb – 2016-12-01T21:07:34.473

49

Cloning an Object was always a concern in JS, but it was all about before ES6, I list different ways of copying an object in JavaScript below, imagine you have the Object below and would like to have a deep copy of that:

var obj = {a:1, b:2, c:3, d:4};

There are few ways to copy this object, without changing the origin:

1) ES5+, Using a simple function to do the copy for you:

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }
    throw new Error("Unable to copy obj this object.");
}

2) ES5+, using JSON.parse and JSON.stringify.

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3) AngularJs:

var  deepCopyObj = angular.copy(obj);

4) jQuery:

var deepCopyObj = jQuery.extend(true, {}, obj);

5) UnderscoreJs & Loadash:

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

Hope these help...

Alireza

Posted 2008-09-23T16:26:09.163

Reputation: 43 514

47

If you're using it, the Underscore.js library has a clone method.

var newObject = _.clone(oldObject);

itsadok

Posted 2008-09-23T16:26:09.163

Reputation: 18 974

22

lodash has a cloneDeep method, it also support another param to clone to make it deep: http://lodash.com/docs#clone and http://lodash.com/docs#cloneDeep

– opensas – 2013-03-02T17:14:53.807

10@opensas agreed. Lodash is generally superior to underscore – nha – 2014-07-31T12:12:37.243

4I advocate deleting this and all other answers which are just one-line references to a utility library's .clone(...) method. Every major library will have them, and the repeated brief non-detailed answers aren't useful to most visitors, who won't be using that particular library. – user – 2016-04-07T17:25:25.360

35

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, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //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 dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    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
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//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();
    };
}

Matt Browne

Posted 2008-09-23T16:26:09.163

Reputation: 8 446

30

The following creates two instances of the same object. I found it and am using it currently. It's simple and easy to use.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));

nathan rogers

Posted 2008-09-23T16:26:09.163

Reputation: 71

Is there anything wrong with this answer? It's more useful as being a standalone solution, yet simple; but jQuery solution is more popular. Why is that? – ceremcem – 2015-09-24T17:15:39.660

Yes please let me know. It seems to be working as intended, if there is some hidden breakage somewhere, I need to use a different solution. – nathan rogers – 2015-09-25T15:34:58.613

4For a simple object, this is around 6 times slower in Chrome than the given answer, and gets much slower as the complexity of the object grows. It scales terribly and can bottleneck your application very quickly. – tic – 2015-12-08T23:10:37.463

@tic do you have data to back your statement up? I would be interested in seeing the performance degradation. – Jon – 2016-08-18T02:30:18.313

1You don't need data, just an understanding of what's going on. This cloning technique serializes the entire object in to a string, then parses that string serialization to build an object. Inherently that's just going to be a lot slower than simply re-arranging some memory (which is what the more sophisticated clones do). But with that being said, for small to medium-sized projects (depending on your definition of "medium-sized") who cares if it's even 1000x less efficient? If your objects are small and you don't clone them a ton 1000x of virtually nothing is still virtually nothing. – machineghost – 2016-12-19T20:20:26.383

2Also, this method loses methods (or any stuff that is not allowed in JSON), plus - JSON.stringify will convert Date objects to strings, ... and not the other way around ;) Stay off this solution. – Mr MT – 2017-08-11T22:11:09.837

Good to know. Thanks for the updates – nathan rogers – 2018-05-14T18:19:04.467

22

function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }

Mark Cidade

Posted 2008-09-23T16:26:09.163

Reputation: 83 530

1just make it recursive so the sub objects will be cloned deeply. – fiatjaf – 2013-01-25T22:38:29.293

just curious ... wont be the clone variable will have the pointers to the properties of the original object? because it seems no new memory allocation – Rupesh Patel – 2013-02-04T09:42:17.987

3Yes. This is just a shallow copy, so the clone will point to the exact same objects pointed-to by the original object. – Mark Cidade – 2013-02-04T10:18:09.020

14The problem with method, that if you have sub objects within the obj, their references will be cloned, and not the values of every sub object. – Kamarey – 2009-06-25T07:46:29.870

21

Lodash has a nice _.cloneDeep(value) method:

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

opensas

Posted 2008-09-23T16:26:09.163

Reputation: 26 104

2I advocate deleting this and all other answers which are just one-line references to a utility library's .clone(...) method. Every major library will have them, and the repeated brief non-detailed answers aren't useful to most visitors, who won't be using that particular library. – user – 2016-04-07T17:23:49.977

An easier way is to use _.merge({}, objA). If only lodash didn't mutate objects in the first place then the clone function wouldn't be necessary. – Rebs – 2017-02-27T00:48:02.070

4Google searches for cloning JS objects refer to here. I'm using Lodash so this answer is relevant to me. Lets not go all "wikipedia deletionist" on answers please. – Rebs – 2017-02-27T00:49:11.857

In Node 9, JSON.parse(JSON.stringify(arrayOfAbout5KFlatObjects)) is a lot faster than _.deepClone(arrayOfAbout5KFlatObjects). – Dan Dascalescu – 2017-12-31T13:07:15.823

21

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

Chris Broski

Posted 2008-09-23T16:26:09.163

Reputation: 1 685

var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; }; ...

http://davidshariff.com/blog/javascript-inheritance-patterns/

– Cody – 2014-04-24T18:32:03.400

True that inheritance != cloning but the JS world needs more awareness on inheritance and it's advantages – Dan Ochiana – 2014-09-09T13:16:23.607

9correct me if I am wrong, but isn't that Crockford's beget function for prototypal inheritance? How does it apply to clone? – Alex Nolasco – 2010-10-06T15:17:18.223

3Yes, I was afraid of this discussion: What is the practical difference between clone, copy and prototypal inheritance, when should you use each and which functions on this page are actually doing what?

I found this SO page by googling "javascript copy object". What I was really looking for was the function above, so I came back to share. My guess is the asker was looking for this too. – Chris Broski – 2010-10-06T19:51:48.777

49Difference between clone/copy and inheritance is, that - using your example, when I change a property of oldObject, the property also gets changed in newObject. If you make a copy, you can do what you want with oldObject without changing newObject. – Ridcully – 2010-12-06T13:13:26.393

13This will break the hasOwnProperty check so its a pretty hacky way to clone an object and will give you unexpected results. – Corban Brook – 2011-03-16T18:17:18.853

18

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

And shallow copy one-liner (ECMAScript 6th edition, 2015):

var origin = { foo : {} };
var copy = Object.assign({}, origin);

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

Maël Nison

Posted 2008-09-23T16:26:09.163

Reputation: 4 072

6This may be fine for simple objects, but it only copies property values. It does not touch the prototype chain and by using Object.keys it skips non-enumerable and inherited properties. Also, it loses property descriptors by doing direct assignment. – Matt Bierner – 2013-11-23T17:51:52.663

If you copy the prototype too, you'd be missing only non-enumerables and property descriptors, yes? Pretty good. :) – sam – 2014-06-08T06:24:04.677

Performance aside, this is a really convenient way to shallow copy an object. I often use this to sort of fake rest properties in a destructuring assignment in my React components. – mjohnsonengr – 2016-03-17T13:56:48.720

15

Just because I didn't see AngularJS mentioned and thought that people might want to know...

angular.copy also provides a method of deep copying objects and arrays.

Dan Atkinson

Posted 2008-09-23T16:26:09.163

Reputation: 8 873

14

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)

Page Notes

Posted 2008-09-23T16:26:09.163

Reputation: 99

14as others have pointed out in comments to Resig's answer, if you want to clone an array-like object you change the {} to [] in the extend call, eg jQuery.extend(true, [], obj) – Anentropic – 2011-03-03T21:27:05.090

JSON.stringify not work with functions – Vlada – 2016-03-12T15:08:19.943

13

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 are efficient in my view.

Michael Uzquiano

Posted 2008-09-23T16:26:09.163

Reputation: 307

11

Deep copying objects in JavaScript (I think the best and the simplest)

1. Using JSON.parse(JSON.stringify(object));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2.Using created method

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Using Lo-Dash's _.cloneDeep link lodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Using Object.assign() method

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

BUT WRONG WHEN

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5.Using Underscore.js _.clone link Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

BUT WRONG WHEN

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

Reference medium.com

JSBEN.CH Performance Benchmarking Playground 1~3 http://jsben.ch/KVQLd Performance Deep copying objects in JavaScript

TinhNQ

Posted 2008-09-23T16:26:09.163

Reputation: 963

11

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

neatonk

Posted 2008-09-23T16:26:09.163

Reputation: 97

9

// 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;
};

Dima

Posted 2008-09-23T16:26:09.163

Reputation: 21

9

AngularJS

Well if you're using angular you could do this too

var newObject = angular.copy(oldObject);

azerafati

Posted 2008-09-23T16:26:09.163

Reputation: 10 390

9

I disagree with the answer with the greatest votes here. A Recursive Deep Clone is much faster than the JSON.parse(JSON.stringify(obj)) approach mentioned.

And here's the function for quick reference:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}

prograhammer

Posted 2008-09-23T16:26:09.163

Reputation: 10 168

7

Here is a comprehensive clone() method that can clone any JavaScript 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;
};

user1547016

Posted 2008-09-23T16:26:09.163

Reputation: 21

It converts primitives into wrapper objects, not a good solution in most cases. – Danubian Sailor – 2014-08-01T09:58:31.290

@DanubianSailor - I don't think it does...it seems to return primitives right away from the start, and doesn't seem to be doing anything to them that would turn them into wrapper objects as they are returned. – Jimbo Jonny – 2016-02-02T18:06:42.053

7

For the people who want to use the JSON.parse(JSON.stringify(obj)) version, but without losing the Date objects, you can use the second argument of parse method to convert the strings back to Date:

function clone(obj) {
  var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
  return JSON.parse(JSON.stringify(x), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v);
    return v;
  });
}

Buzinas

Posted 2008-09-23T16:26:09.163

Reputation: 9 722

6

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!

Cody

Posted 2008-09-23T16:26:09.163

Reputation: 6 890

The second way needs a Prototype, I prefer the first way, even if it's not the best one on performance due to you can use with a lot of browsers and with Node JS. – Hola Soy Edu Feliz Navidad – 2014-05-03T10:03:02.740

That's cool and all but suppose o has a property a. Now does oo.hasOwnProperty('a')? – user420667 – 2016-03-29T23:54:35.533

No -- o is essentially added as a prototype of oo. This is likely not going to be the desired behavior, which is why 99.9% of the serialize() methods I write use the JSON approach mentioned above. I basically always use JSON, and there're other caveats exposed when using Object.create. – Cody – 2016-03-30T02:30:18.897

No, watch this code! Object.create doesn't necessary create a copy of an object instead it uses the older object as a prototype for the clone – 16kb – 2018-06-22T01:07:33.613

6

Only when you can use ECMAScript 6 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;
}

andrew

Posted 2008-09-23T16:26:09.163

Reputation: 190

6

Cloning an object using today's JavaScript: ECMAScript 2015 (formerly known as ECMAScript 6)

var original = {a: 1};

// Method 1: New object with original assigned.
var copy1 = Object.assign({}, original);

// Method 2: New object with spread operator assignment.
var copy2 = {...original};

Old browsers may not support ECMAScript 2015. A common solution is to use a JavaScript-to-JavaScript compiler like Babel to output an ECMAScript 5 version of your JavaScript code.

As pointed out by @jim-hall, this is only a shallow copy. Properties of properties are copied as a reference: changing one would change the value in the other object/instance.

Barry Staes

Posted 2008-09-23T16:26:09.163

Reputation: 1 605

6

I use the npm clone library. Apparently it also works in the browser.

https://www.npmjs.com/package/clone

let a = clone(b)

user3071643

Posted 2008-09-23T16:26:09.163

Reputation: 596

5

Single-line ECMAScript 6 solution (special object types like Date/Regex not handled):

const clone = (o) =>
  typeof o === 'object' && o !== null ?      // only clone objects
  (Array.isArray(o) ?                        // if cloning an array
    o.map(e => clone(e)) :                   // clone each of its elements
    Object.keys(o).reduce(                   // otherwise reduce every key in the object
      (r, k) => (r[k] = clone(o[k]), r), {}  // and save its cloned value into a new object
    )
  ) :
  o;                                         // return non-objects as is

var x = {
  nested: {
    name: 'test'
  }
};

var y = clone(x);

console.log(x.nested !== y.nested);

Shishir Arora

Posted 2008-09-23T16:26:09.163

Reputation: 2 540

5

Lodash has a function that handles that for you like so.

var foo = {a: 'a', b: {c:'d', e: {f: 'g'}}};

var bar = _.cloneDeep(foo);
// bar = {a: 'a', b: {c:'d', e: {f: 'g'}}} 

Read the docs here.

Daniel Barde

Posted 2008-09-23T16:26:09.163

Reputation: 1 192

5

I am late to answer this question, but I have an another way of cloning the object:

   function cloneObject(obj) {
        if (obj === null || typeof(obj) !== 'object')
            return obj;
        var temp = obj.constructor(); // changed
        for (var key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                obj['isActiveClone'] = null;
                temp[key] = cloneObject(obj[key]);
                delete obj['isActiveClone'];
            }
        }
        return temp;
    }



var b = cloneObject({"a":1,"b":2});   // calling

which is much better and faster then:

var a = {"a":1,"b":2};
var b = JSON.parse(JSON.stringify(a));  

and

var a = {"a":1,"b":2};

// Deep copy
var newObject = jQuery.extend(true, {}, a);

I have bench-marked the code and you can test the results here:

and sharing the results: enter image description here References: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

Mayur Agarwal

Posted 2008-09-23T16:26:09.163

Reputation: 642

4

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 usable in production code just yet (unless you’re writing a Firefox extension of course).

Robin Whittleton

Posted 2008-09-23T16:26:09.163

Reputation: 4 172

3You probably meant obj2 = Object.assign({}, obj1). Your current code is equivalent to obj2 = obj1. – Oriol – 2015-01-25T11:10:53.900

5This is a shallow-clone. const o1 = { a: { deep: 123 } }; const o2 = Object.assign({}, o1); o2.a.deep = 456; now o1.a.deep === 456 too. – Josh from Qaribou – 2017-02-06T14:21:38.317

2Object.assign() is not for cloning nested objects. – Redu – 2017-04-05T14:31:34.073

3

Wow, another useless answer. Taken from MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign : Warning for Deep Clone - For deep cloning, we need to use other alternatives because Object.assign() copies property values. If the source value is a reference to an object, it only copies that reference value.

– Karl Morrison – 2017-04-21T10:04:31.197

4

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 two 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};

Steve Tomlin

Posted 2008-09-23T16:26:09.163

Reputation: 21

3

There are a lot of answers, but none of them gave the desired effect I needed. I wanted to utilize the power of jQuery's deep copy... However, when it runs into an array, it simply copies the reference to the array and deep copies the items in it. To get around this, I made a nice little recursive function that will create a new array automatically.

(It even checks for kendo.data.ObservableArray if you want it to! Though, make sure you make sure you call kendo.observable(newItem) if you want the Arrays to be observable again.)

So, to fully copy an existing item, you just do this:

var newItem = jQuery.extend(true, {}, oldItem);
createNewArrays(newItem);


function createNewArrays(obj) {
    for (var prop in obj) {
        if ((kendo != null && obj[prop] instanceof kendo.data.ObservableArray) || obj[prop] instanceof Array) {
            var copy = [];
            $.each(obj[prop], function (i, item) {
                var newChild = $.extend(true, {}, item);
                createNewArrays(newChild);
                copy.push(newChild);
            });
            obj[prop] = copy;
        }
    }
}

Daniel Lorenz

Posted 2008-09-23T16:26:09.163

Reputation: 1 980

3

This is my version of object cloner. This is a stand-alone version of the jQuery method, with only few tweaks and adjustments. Check out the fiddle. I've used a lot of jQuery until the day I realized that I'd use only this function most of the time x_x.

The usage is the same as described into the jQuery API:

  • Non-deep clone: extend(object_dest, object_source);
  • Deep clone: extend(true, object_dest, object_source);

One extra function is used to define if object is proper to be cloned.

/**
 * This is a quasi clone of jQuery's extend() function.
 * by Romain WEEGER for wJs library - www.wexample.com
 * @returns {*|{}}
 */
function extend() {
    // Make a copy of arguments to avoid JavaScript inspector hints.
    var to_add, name, copy_is_array, clone,

    // The target object who receive parameters
    // form other objects.
    target = arguments[0] || {},

    // Index of first argument to mix to target.
    i = 1,

    // Mix target with all function arguments.
    length = arguments.length,

    // Define if we merge object recursively.
    deep = false;

    // Handle a deep copy situation.
    if (typeof target === 'boolean') {
        deep = target;

        // Skip the boolean and the target.
        target = arguments[ i ] || {};

        // Use next object as first added.
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if (typeof target !== 'object' && typeof target !== 'function') {
        target = {};
    }

    // Loop trough arguments.
    for (false; i < length; i += 1) {

        // Only deal with non-null/undefined values
        if ((to_add = arguments[ i ]) !== null) {

            // Extend the base object.
            for (name in to_add) {

                // We do not wrap for loop into hasOwnProperty,
                // to access to all values of object.
                // Prevent never-ending loop.
                if (target === to_add[name]) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays.
                if (deep && to_add[name] && (is_plain_object(to_add[name]) || (copy_is_array = Array.isArray(to_add[name])))) {
                    if (copy_is_array) {
                        copy_is_array = false;
                        clone = target[name] && Array.isArray(target[name]) ? target[name] : [];
                    }
                    else {
                        clone = target[name] && is_plain_object(target[name]) ? target[name] : {};
                    }

                    // Never move original objects, clone them.
                    target[name] = extend(deep, clone, to_add[name]);
                }

                // Don't bring in undefined values.
                else if (to_add[name] !== undefined) {
                    target[name] = to_add[name];
                }
            }
        }
    }
    return target;
}

/**
 * Check to see if an object is a plain object
 * (created using "{}" or "new Object").
 * Forked from jQuery.
 * @param obj
 * @returns {boolean}
 */
function is_plain_object(obj) {
    // Not plain objects:
    // - Any object or value whose internal [[Class]] property is not "[object Object]"
    // - DOM nodes
    // - window
    if (obj === null || typeof obj !== "object" || obj.nodeType || (obj !== null && obj === obj.window)) {
        return false;
    }
    // Support: Firefox <20
    // The try/catch suppresses exceptions thrown when attempting to access
    // the "constructor" property of certain host objects, i.e. |window.location|
    // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
    try {
        if (obj.constructor && !this.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
            return false;
        }
    }
    catch (e) {
        return false;
    }

    // If the function hasn't returned already, we're confident that
    // |obj| is a plain object, created by {} or constructed with new Object
    return true;
}

weeger

Posted 2008-09-23T16:26:09.163

Reputation: 326

1You may want to add || typeof target[name] !== "undefined" when testing if (target === to_add[name]) { continue; } to not overwrite the existing members of target. For example var a={hello:"world", foo:"bar"}; var b={hello:"you"}; extend(b, a); we expect to find b =&gt; {hello:"you", foo:"bar"}, but with your code we find: b =&gt; {hello:"world", foo:"bar"} – AymKdn – 2017-05-26T10:05:43.890

In my case I expected indeed to overwrite existing members, so the current behaviour was the wright one for this usage. But thank you to add this helpful suggestion. – weeger – 2017-06-16T13:01:32.007

3

ES 2017 example:

let objectToCopy = someObj;
let copyOfObject = {};
Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(objectToCopy));
// copyOfObject will now be the same as objectToCopy

codeMonkey

Posted 2008-09-23T16:26:09.163

Reputation: 1 113

3

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);            
}

gion_13

Posted 2008-09-23T16:26:09.163

Reputation: 33 982

2

Use Object.create() to get the prototype and support for instanceof, and use a for() loop to get enumerable keys:

function cloneObject(source) {
    var key,value;
    var clone = Object.create(source);

    for (key in source) {
        if (source.hasOwnProperty(key) === true) {
            value = source[key];

            if (value!==null && typeof value==="object") {
                clone[key] = cloneObject(value);
            } else {
                clone[key] = value;
            }
        }
    }

    return clone;
}

Steven Vachon

Posted 2008-09-23T16:26:09.163

Reputation: 2 022

Great answer! I think this is the one of the only methods to keep setters and getter intact. This solved my problem. Thanks! (see: http://stackoverflow.com/questions/33207028/javascript-setter-and-getter-of-object-lost-after-angular-copy)

– lepe – 2015-10-20T05:54:47.833

Wouldn't you want to use clone = Object.create(Object.getPrototypeOf(source)), instead of inheriting properties which you also overwrite? – user – 2016-04-07T17:47:46.330

Interesting. Though, using getPrototypeOf on an Array turns its indexes into keys of a new Object. – Steven Vachon – 2016-04-11T16:47:55.107

2

Requires new-ish browsers, but...

Let's extend the native Object and get a real .extend();

Object.defineProperty(Object.prototype, 'extend', {
    enumerable: false,
    value: function(){
        var that = this;

        Array.prototype.slice.call(arguments).map(function(source){
            var props = Object.getOwnPropertyNames(source),
                i = 0, l = props.length,
                prop;

            for(; i < l; ++i){
                prop = props[i];

                if(that.hasOwnProperty(prop) && typeof(that[prop]) === 'object'){
                    that[prop] = that[prop].extend(source[prop]);
                }else{
                    Object.defineProperty(that, prop, Object.getOwnPropertyDescriptor(source, prop));
                }
            }
        });

        return this;
    }
});

Just pop that in prior to any code that uses .extend() on an object.

Example:

var obj1 = {
    node1: '1',
    node2: '2',
    node3: 3
};

var obj2 = {
    node1: '4',
    node2: 5,
    node3: '6'
};

var obj3 = ({}).extend(obj1, obj2);

console.log(obj3);
// Object {node1: "4", node2: 5, node3: "6"}

Tristian

Posted 2008-09-23T16:26:09.163

Reputation: 89

Mutating prototypes is generally considered bad practice, with the only exception being for shims. – Josh from Qaribou – 2017-02-06T14:23:40.793

2

As recursion is just too expensive for JavaScript, and most answers I have found are using recursion, while JSON approach will skip the non-JSON-convertible parts (Function, etc.). So I did a little research and found this trampoline technique to avoid it. Here's the code:

/*
 * Trampoline to avoid recursion in JavaScript, see:
 *     http://www.integralist.co.uk/posts/js-recursion.html
 */
function trampoline() {
    var func = arguments[0];
    var args = [];
    for (var i = 1; i < arguments.length; i++) {
        args[i - 1] = arguments[i];
    }

    var currentBatch = func.apply(this, args);
    var nextBatch = [];

    while (currentBatch && currentBatch.length > 0) {
        currentBatch.forEach(function(eachFunc) {
            var ret = eachFunc();
            if (ret && ret.length > 0) {
                nextBatch = nextBatch.concat(ret);
            }
        });

        currentBatch = nextBatch;
        nextBatch = [];
    }
};

/*
 *  Deep clone an object using the trampoline technique.
 *
 *  @param target {Object} Object to clone
 *  @return {Object} Cloned object.
 */
function clone(target) {
    if (typeof target !== 'object') {
        return target;
    }
    if (target == null || Object.keys(target).length == 0) {
        return target;
    }

    function _clone(b, a) {
        var nextBatch = [];
        for (var key in b) {
            if (typeof b[key] === 'object' && b[key] !== null) {
                if (b[key] instanceof Array) {
                    a[key] = [];
                }
                else {
                    a[key] = {};
                }
                nextBatch.push(_clone.bind(null, b[key], a[key]));
            }
            else {
                a[key] = b[key];
            }
        }
        return nextBatch;
    };

    var ret = target instanceof Array ? [] : {};
    (trampoline.bind(null, _clone))(target, ret);
    return ret;
};

Also see this gist: https://gist.github.com/SeanOceanHu/7594cafbfab682f790eb

Bodhi Hu

Posted 2008-09-23T16:26:09.163

Reputation: 123

2

This is a solution with recursion:

obj = {
  a: { b: { c: { d: ['1', '2'] } } },
  e: 'Saeid'
}
const Clone = function (obj) {
  
  const container = Array.isArray(obj) ? [] : {}
  const keys  = Object.keys(obj)
   
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    if(typeof obj[key] == 'object') {
      container[key] = Clone(obj[key])
    }
    else
      container[key] = obj[key].slice()
  }
  
  return container
}
 console.log(Clone(obj))

SAlidadi

Posted 2008-09-23T16:26:09.163

Reputation: 30

2

For future reference, one can use this code

ES6:

_clone: function(obj){
    let newObj = {};
    for(let i in obj){
        if(typeof(obj[i]) === 'object' && Object.keys(obj[i]).length){
            newObj[i] = clone(obj[i]);
        } else{
            newObj[i] = obj[i];
        }
    }
    return Object.assign({},newObj);
}

ES5:

function clone(obj){
let newObj = {};
for(let i in obj){
    if(typeof(obj[i]) === 'object' && Object.keys(obj[i]).length){
        newObj[i] = clone(obj[i]);
    } else{
        newObj[i] = obj[i];
    }
}
return Object.assign({},newObj);

}

E.g

var obj ={a:{b:1,c:3},d:4,e:{f:6}}
var xc = clone(obj);
console.log(obj); //{a:{b:1,c:3},d:4,e:{f:6}}
console.log(xc); //{a:{b:1,c:3},d:4,e:{f:6}}

xc.a.b = 90;
console.log(obj); //{a:{b:1,c:3},d:4,e:{f:6}}
console.log(xc); //{a:{b:90,c:3},d:4,e:{f:6}}

Ashutosh Jha

Posted 2008-09-23T16:26:09.163

Reputation: 101

2

class Handler {
  static deepCopy (obj) {
    if (Object.prototype.toString.call(obj) === '[object Array]') {
      const result = [];
      
      for (let i = 0, len = obj.length; i < len; i++) {
        result[i] = Handler.deepCopy(obj[i]);
      }
      return result;
    } else if (Object.prototype.toString.call(obj) === '[object Object]') {
      const result = {};
      for (let prop in obj) {
        result[prop] = Handler.deepCopy(obj[prop]);
      }
      return result;
    }
    return obj;
  }
}

Ihor Pavlyk

Posted 2008-09-23T16:26:09.163

Reputation: 319

2

There are so many ways to achieve this, but if you want to do this without any library, you can use the following:

const cloneObject = (oldObject) => {
  let newObject = oldObject;
  if (oldObject && typeof oldObject === 'object') {
    if(Array.isArray(oldObject)) {
      newObject = [];
    } else if (Object.prototype.toString.call(oldObject) === '[object Date]' && !isNaN(oldObject)) {
      newObject = new Date(oldObject.getTime());
    } else {
      newObject = {};
      for (let i in oldObject) {
        newObject[i] = cloneObject(oldObject[i]);
      }
    }

  }
  return newObject;
}

Let me know what you think.

shobhit1

Posted 2008-09-23T16:26:09.163

Reputation: 419

2

Here is my way of deep cloning a object with ES2015 default value and spread operator

 const makeDeepCopy = (obj, copy = {}) => {
  for (let item in obj) {
    if (typeof obj[item] === 'object') {
      makeDeepCopy(obj[item], copy)
    }
    if (obj.hasOwnProperty(item)) {
      copy = {
        ...obj
      }
    }
  }
  return copy
}

const testObj = {
  "type": "object",
  "properties": {
    "userId": {
      "type": "string",
      "chance": "guid"
    },
    "emailAddr": {
      "type": "string",
      "chance": {
        "email": {
          "domain": "fake.com"
        }
      },
      "pattern": "[email protected]"
    }
  },
  "required": [
    "userId",
    "emailAddr"
  ]
}

const makeDeepCopy = (obj, copy = {}) => {
  for (let item in obj) {
    if (typeof obj[item] === 'object') {
      makeDeepCopy(obj[item], copy)
    }
    if (obj.hasOwnProperty(item)) {
      copy = {
        ...obj
      }
    }
  }
  return copy
}

console.log(makeDeepCopy(testObj))

Julez

Posted 2008-09-23T16:26:09.163

Reputation: 350

2

What about asynchronous object cloning done by a Promise?

async function clone(thingy /**/)
{
    if(thingy instanceof Promise)
    {
        throw Error("This function cannot clone Promises.");
    }
    return thingy;
}

K._

Posted 2008-09-23T16:26:09.163

Reputation: 2 375

2

In my experience, a recursive version vastly outperforms JSON.parse(JSON.stringify(obj)). Here is a modernized recursive deep object copy function which can fit on a single line:

function deepCopy(obj) {
  return Object.keys(obj).reduce((v, d) => Object.assign(v, {
    [d]: (obj[d].constructor === Object) ? deepCopy(obj[d]) : obj[d]
  }), {});
}

This is performing around 40 times faster than the JSON.parse... method.

Parabolord

Posted 2008-09-23T16:26:09.163

Reputation: 89

1

Without touching the prototypical inheritance you may deep lone objects and arrays as follows;

function objectClone(o){
  var ot = Array.isArray(o);
  return o !== null && typeof o === "object" ? Object.keys(o)
                                                     .reduce((r,k) => o[k] !== null && typeof o[k] === "object" ? (r[k] = objectClone(o[k]),r)
                                                                                                                : (r[k] = o[k],r), ot ? [] : {})
                                             : o;
}
var obj = {a: 1, b: {c: 2, d: {e: 3, f: {g: 4, h: null}}}},
    arr = [1,2,[3,4,[5,6,[7]]]],
    nil = null,
  clobj = objectClone(obj),
  clarr = objectClone(arr),
  clnil = objectClone(nil);
console.log(clobj, obj === clobj);
console.log(clarr, arr === clarr);
console.log(clnil, nil === clnil);
clarr[2][2][2] = "seven";
console.log(arr, clarr);

Redu

Posted 2008-09-23T16:26:09.163

Reputation: 12 471

1

For a shallow copy there is a great, simple method introduced in ECMAScript2018 standard. It involves the use of Spread Operator :

let obj = {a : "foo", b:"bar" , c:10 , d:true , e:[1,2,3] };

let objClone = { ...obj };

I have tested it in Chrome browser, both objects are stored in different locations, so changing immediate child values in either will not change the other. Though (in the example) changing a value in e will effect both copies.

This technique is very simple and straight forward. I consider this a true Best Practice for this question once and for all.

Vikram K

Posted 2008-09-23T16:26:09.163

Reputation: 322

1

In JavaScript, you can write your deepCopy method like

function deepCopy(src) {
  let target = Array.isArray(src) ? [] : {};
  for (let prop in src) {
    let value = src[prop];
    if(value && typeof value === 'object') {
      target[prop] = deepCopy(value);
  } else {
      target[prop] = value;
  }
 }
    return target;
}

chandan gupta

Posted 2008-09-23T16:26:09.163

Reputation: 274

0

As this question is having lot of attention and answers with reference to inbuilt features such as Object.assign or custom code to deep clone, i would like to share some libraries to deep clone,

1. esclone

npm install --savedev esclone https://www.npmjs.com/package/esclone

Example use in ES6:

import esclone from "esclone";

const rockysGrandFather = {
  name: "Rockys grand father",
  father: "Don't know :("
};
const rockysFather = {
  name: "Rockys Father",
  father: rockysGrandFather
};

const rocky = {
  name: "Rocky",
  father: rockysFather
};

const rockyClone = esclone(rocky);

Example use in ES5:

var esclone = require("esclone")
var foo = new String("abcd")
var fooClone = esclone.default(foo)
console.log(fooClone)
console.log(foo === fooClone)

2. deep copy

npm install deep-copy https://www.npmjs.com/package/deep-copy

Example:

var dcopy = require('deep-copy')

// deep copy object 
var copy = dcopy({a: {b: [{c: 5}]}})

// deep copy array 
var copy = dcopy([1, 2, {a: {b: 5}}])

3. clone-deep

$ npm install --save clone-deep https://www.npmjs.com/package/clone-deep

Example:

var cloneDeep = require('clone-deep');

var obj = {a: 'b'};
var arr = [obj];

var copy = cloneDeep(arr);
obj.c = 'd';

console.log(copy);
//=> [{a: 'b'}] 

console.log(arr);

JTeam

Posted 2008-09-23T16:26:09.163

Reputation: 716

0

Looking through this long list of answers nearly all the solutions have been covered except one that I am aware of. This is the list of VANILLA JS ways of deep cloning an object.

  1. JSON.parse(JSON.stringify( obj ) );

  2. Through history.state with pushState or replaceState

  3. Web Notifications API but this has the downside of asking the user for permissions.

  4. Doing your own recursive loop through the object to copy each level.

  5. The answer I didn't see -> Using ServiceWorkers. The messages (objects) passed back and forth between the page and the ServiceWorker script will be deep clones of any object.

Steve Griffith

Posted 2008-09-23T16:26:09.163

Reputation: 41

0

Hope this helps.

function deepClone(obj) {
    /*
     * Duplicates an object 
     */

    var ret = null;
    if (obj !== Object(obj)) { // primitive types
        return obj;
    }
    if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) { // string objecs
        ret = obj; // for ex: obj = new String("Spidergap")
    } else if (obj instanceof Date) { // date
        ret = new obj.constructor();
    } else
        ret = Object.create(obj.constructor.prototype);

    var prop = null;
    var allProps = Object.getOwnPropertyNames(obj); //gets non enumerables also


    var props = {};
    for (var i in allProps) {
        prop = allProps[i];
        props[prop] = false;
    }

    for (i in obj) {
        props[i] = i;
    }

    //now props contain both enums and non enums 
    var propDescriptor = null;
    var newPropVal = null; // value of the property in new object
    for (i in props) {
        prop = obj[i];
        propDescriptor = Object.getOwnPropertyDescriptor(obj, i);

        if (Array.isArray(prop)) { //not backward compatible
            prop = prop.slice(); // to copy the array
        } else
        if (prop instanceof Date == true) {
            prop = new prop.constructor();
        } else
        if (prop instanceof Object == true) {
            if (prop instanceof Function == true) { // function
                if (!Function.prototype.clone) {
                    Function.prototype.clone = function() {
                        var that = this;
                        var temp = function tmp() {
                            return that.apply(this, arguments);
                        };
                        for (var ky in this) {
                            temp[ky] = this[ky];
                        }
                        return temp;
                    }
                }
                prop = prop.clone();

            } else // normal object 
            {
                prop = deepClone(prop);
            }

        }

        newPropVal = {
            value: prop
        };
        if (propDescriptor) {
            /*
             * If property descriptors are there, they must be copied
             */
            newPropVal.enumerable = propDescriptor.enumerable;
            newPropVal.writable = propDescriptor.writable;

        }
        if (!ret.hasOwnProperty(i)) // when String or other predefined objects
            Object.defineProperty(ret, i, newPropVal); // non enumerable

    }
    return ret;
}

https://github.com/jinujd/Javascript-Deep-Clone

Jinu Joseph Daniel

Posted 2008-09-23T16:26:09.163

Reputation: 1 842

0

When your object is nested and it contains data object, other structured object or some property object, etc then using JSON.parse(JSON.stringify(object)) or Object.assign({}, obj) or $.extend(true, {}, obj) will not work. In that case use lodash. It is simple and easy..

var obj = {a: 25, b: {a: 1, b: 2}, c: new Date(), d: anotherNestedObject };
var A = _.cloneDeep(obj);

Now A will be your new cloned of obj without any references..

Prasanth Jaya

Posted 2008-09-23T16:26:09.163

Reputation: 1 969

0

if you find yourself doing this type of thing regular ( eg- creating undo redo functionality ) it might be worth looking into Immutable.js

const map1 = Immutable.fromJS( { a: 1, b: 2, c: { d: 3 } } );
const map2 = map1.setIn( [ 'c', 'd' ], 50 );

console.log( `${ map1.getIn( [ 'c', 'd' ] ) } vs ${ map2.getIn( [ 'c', 'd' ] ) }` ); // "3 vs 50"

https://codepen.io/anon/pen/OBpqNE?editors=1111

shunryu111

Posted 2008-09-23T16:26:09.163

Reputation: 2 705

-1

function deepCloneObj(obj){
  let newObj = {},
      mapLike = Object.entries(obj),
      deepCloneMapLikeObj = JSON.parse(JSON.stringify(map));

  for(let [key,value] of deepCloneMapLikeObj){
     newObj[key] = value;
  }
  return newObj;
}

Now Try yourself

var obj = {a:1,b:{c:2,d:3}};
var deepClone = deepCloneObj(obj);
deepClone.b.c = 9;
deepClone.b.c === obj.b.c // 9 === 3

user8640104

Posted 2008-09-23T16:26:09.163

Reputation: 71

-2

For my purposes the most elegant way to create a new object from an existing one (clone) is to use JavaScript's 'assign' object function.

foo = {bar: 10, baz: {quox: 'batman'}};

clonedObject = Object.assign(foo);

clonedObject is now a copy of foo. I don't know the details in terms of how well it works, or what a 'deep' copy is, but I use it to also combine the attributes of an object with other objects. Seems to work for cloning as well.

Nick Res

Posted 2008-09-23T16:26:09.163

Reputation: 1 115

-2

Best and most up to date way to do this clone is as follows:

using the "..." ES6 spread operator Example:

var clonedObjArray = [...oldObjArray];

this way we spread the array into individual values and put it in a new array with the [] operator.

here is a longer example that shows the different ways it works

let objArray = [ {a:1} , {b:2} ];

let refArray = objArray; // this will just point to the objArray
let clonedArray = [...objArray]; // will clone the array

console.log( "before:" );
console.log( "obj array" , objArray );
console.log( "ref array" , refArray );
console.log( "cloned array" , clonedArray );

objArray[0] = {c:3};

console.log( "after:" );
console.log( "obj array" , objArray ); // [ {c:3} , {b:2} ]
console.log( "ref array" , refArray ); // [ {c:3} , {b:2} ]
console.log( "cloned array" , clonedArray ); // [ {a:1} , {b:2} ]

MennyMT

Posted 2008-09-23T16:26:09.163

Reputation: 1 151