|
|||||||
What is the most efficient way to deep clone an object in JavaScript?
Время создания: 13.07.2018 15:30
Текстовые метки: javascript efficient way clone object
Раздел: Javascript
Запись: Velonski/mytetra-database/master/base/1530785819wuxcc39euv/text.html на raw.githubusercontent.com
|
|||||||
|
|||||||
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. javascript object clone shareimprove this question edited Mar 22 '17 at 16:17 community wiki 26 revs, 21 users 25% jschrab 517 Eval 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 Mar 22 '12 at 14:08 12 Cloning 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 Mar 11 '13 at 22:25 6 eval() 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 Sep 8 '14 at 13:37 46 Wow, a lot of answers for something really simple. If you can use ES5 (IE9+) then do var obj2 = Object.create(obj1); if not, then use the answer from @protonfish. jsFiddle: jsfiddle.net/dotnetCarpenter/yufzc1jt – dotnetCarpenter Oct 22 '14 at 18:17 33 @dotnetCarpenter your solution would work only with simple objects, but the prototype object is the same, and the new object is using the original references, so if you have inner objects in your original object your solution fails: jsfiddle.net/rahpuser/yufzc1jt/2 – rahpuser Nov 25 '14 at 21:46 show 10 more comments 60 Answers active oldest votes 1 2 next up vote 4041 down vote accepted 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. shareimprove this answer edited Mar 24 at 15:17 community wiki 11 revs, 8 users 39% Nidhin David 41 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 Sep 2 '12 at 20:11 6 @ThiefMaster 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 Mar 27 '13 at 8:16 6 Here's the JS code behind the jQuery deep copy, for anyone interested: github.com/jquery/jquery/blob/master/src/core.js#L265-327 – Alex W Apr 11 '13 at 14:24 157 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: 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 Jan 21 '14 at 3:37 34 How would one do this without using jQuery? – Awesomeness01 Apr 30 '15 at 2:22 show 28 more comments up vote 1835 down vote 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. shareimprove this answer edited Apr 7 at 1:18 community wiki 13 revs, 10 users 68% Corban Brook 4 @trysis Object.create is not cloning the object, is using the prototype object... jsfiddle.net/rahpuser/yufzc1jt/2 – rahpuser Nov 25 '14 at 21:37 61 This method will also remove the keys from your object, which have functions as their values, because the JSON doesn't support functions. – Karlen Kishmiryan May 29 '15 at 9:52 19 Also 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 Jul 30 '15 at 21:37 16 JSON approach also chokes on circular references. – rich remer Feb 13 '16 at 5:25 10 @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 Nov 3 '16 at 18:00 show 22 more comments up vote 395 down vote Assuming that you have only variables and not any functions in your object, you can just use: var newObject = JSON.parse(JSON.stringify(oldObject)); shareimprove this answer edited Oct 23 '14 at 23:47 community wiki 3 revs, 3 users 63% Sultan Shakir 80 the 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 Jan 17 '13 at 11:00 30 @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: jsperf.com/cloning-an-object/79). – MiJyn Jul 4 '13 at 7:42 33 JSON.stringify({key: undefined}) //=> "{}" – Web_Designer Apr 30 '14 at 6:24 27 this technique is going to destroy also all Date objects that are stored inside the object, converting them to string form. – fstab Aug 21 '14 at 12:51 10 It will fail to copy anything that is not part of the JSON spec (json.org) – cdmckay Feb 9 '16 at 15:07 show 6 more comments up vote 271 down vote 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; } Run code snippetExpand snippet shareimprove this answer edited Feb 16 '16 at 4:57 community wiki 9 revs, 7 users 36% ConroyP 17 The 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 Sep 23 '08 at 17:23 5 This 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 Oct 4 '09 at 22:06 3 Andrew, 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 Sep 14 '11 at 15:53 1 Similar to limscoder's answer, see my answer below on how to do this without calling the constructor: stackoverflow.com/a/13333781/560114 – Matt Browne Nov 11 '12 at 17:55 2 For 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 Nov 11 '13 at 8:34 show 5 more comments up vote 269 down vote +50 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: Show code snippet 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: Show code snippet shareimprove this answer edited Jan 28 at 21:07 answered Jun 6 '12 at 14:59 Jeremy 71.5k66270312 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. – Jeremy May 6 '13 at 3:11 32 This is just so wrong! That API is not meant to be used this way. – Fardin Jul 31 '14 at 23:34 151 As the guy who implemented pushState in Firefox, I feel an odd mix of pride and revulsion at this hack. Well done, guys. – Justin L. Aug 14 '14 at 18:37 add a comment up vote 132 down vote 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; } }); } shareimprove this answer edited Feb 14 '17 at 8:46 community wiki 3 revs, 3 users 95% Eugene Tiurin 54 This doesn't recursively copy so doesn't really offer a solution to the problem of cloning an object. – mwhite Mar 8 '16 at 19:56 4 This method worked, although 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 May 9 '16 at 19:57 10 @mwhite there is a difference between clone and deep-clone. This answer does in fact clone, but it doesn't deep-clone. – Meirion Hughes Jun 8 '16 at 12:08 29 the op asked for deep clone. this doesn't do deep clone. – user566245 Jul 31 '16 at 3:49 1 wow so many Object.assign responses without even reading the op's question... – martin8768 Dec 29 '17 at 10:02 show 3 more comments up vote 88 down vote 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); shareimprove this answer edited Mar 26 '11 at 14:20 community wiki 2 revs Kamarey 3 what about var obj = {} and obj.a = obj – neaumusic May 5 '15 at 22:40 5 I 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 && Date != Array)? – Adam McKee Sep 1 '15 at 2:10 @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: jsfiddle.net/zqv9q9c6. – brichins Jun 14 '16 at 20:39 *I meant "parameter assignment" (within a function body), not "variable assignment". Although it helps to understand both thoroughly. – brichins Jun 15 '16 at 16:42 1 @NickSweeting: Try - may be it works. If not - fix it and update the answer. That's how it works here in community:) – Kamarey Jul 10 '17 at 7:20 show 2 more comments up vote 80 down vote 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; } shareimprove this answer edited Jul 16 '13 at 16:37 community wiki 3 revs, 3 users 69% Alan 8 This does not seem right. cloneObject({ name: null }) => {"name":{}} – Niyaz Feb 27 '13 at 14:36 11 This is due to another dumb thing in javascript typeof null > "object" but Object.keys(null) > TypeError: Requested keys of a value that is not an object. change the condition to if(typeof(obj[i])=="object" && obj[i]!=null) – Vitim.us Apr 16 '13 at 16:42 This will assign inherited enumerable properties of obj directly to the clone, and assumes that obj is a plain Object. – RobG Feb 1 '16 at 22:58 This also messes up arrays, which get converted to objects with numeric keys. – blade Nov 3 '16 at 12:29 Not a problem if you don't use null. – Jorge Bucaran Mar 1 '17 at 6:17 add a comment up vote 64 down vote 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: http://jsperf.com/js-deep-copy/2 http://jsperf.com/jquery-extend-vs-json-parse/2 shareimprove this answer edited Mar 20 at 15:11 community wiki 12 revs, 2 users 93% tfmontague 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 May 9 '16 at 19:56 @NicoDurand - Are your performance tests online? – tfmontague May 17 '16 at 13:36 2 All 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 Apr 21 '17 at 10:02 1 A 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 Apr 21 '17 at 15:42 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" && v !== null) ? copy(v) : v; – josi Feb 1 at 16:53 show 2 more comments up vote 55 down vote 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}); shareimprove this answer edited Feb 21 '13 at 15:01 community wiki 5 revs, 4 users 31% Zibri add a comment up vote 49 down vote 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); shareimprove this answer edited Mar 17 '15 at 1:18 community wiki 2 revs Joe 14 This 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 May 8 '12 at 9:57 9 It'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 Mar 6 '13 at 23:31 6 Sorry 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 Aug 18 '14 at 12:51 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: jsfiddle.net/jzumbrun/42xejnbx – Jon Sep 3 '16 at 13:59 I think this is good advice, generally, since instead of using const defaultFoo = { a: { b: 123 } }; you can go const defaultFoo = () => ({ 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 Feb 6 '17 at 14:25 add a comment up vote 46 down vote 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.) shareimprove this answer edited Mar 27 '13 at 8:04 community wiki 3 revs pvorb 2 npm clone has been invaluable to me for cloning arbitrarily nested objects. This is the right answer. – Andy Ray Sep 10 '15 at 3:21 what is the performance of your lib compared to let's say JSON.parse(JSON.stringify(obj))? – pkyeck Dec 1 '16 at 16:03 Here's a library that states that there are faster options. Haven't tested though. – pvorb Dec 1 '16 at 21:07 add a comment up vote 45 down vote If you're using it, the Underscore.js library has a clone method. var newObject = _.clone(oldObject); shareimprove this answer edited Jul 22 '16 at 17:26 community wiki 2 revs, 2 users 83% itsadok 22 lodash has a cloneDeep method, it also support another param to clone to make it deep: lodash.com/docs#clone and lodash.com/docs#cloneDeep – opensas Mar 2 '13 at 17:14 10 @opensas agreed. Lodash is generally superior to underscore – nha Jul 31 '14 at 12:12 3 I 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. – Jeremy Apr 7 '16 at 17:25 add a comment up vote 43 down vote 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... shareimprove this answer edited Feb 24 at 17:35 community wiki 11 revs, 2 users 92% Alireza 2 clone in underscore is not a deep clone in current version – Rogelio Apr 3 '17 at 16:19 Thanks. yes as new doc for Underscore... clone_.clone(object) Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated. _.clone({name: 'moe'}); => {name: 'moe'}; – Alireza Apr 4 '17 at 1:00 52 Object.assign does not deep copy. Example: var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d". If this was deep copy, y.a.b would still be c, but it's now d. – kba May 10 '17 at 15:52 7 Object.assign() only clones the first level of properties! – haemse Jul 21 '17 at 10:13 3 what is cloneSO() function? – pastorello Aug 7 '17 at 16:55 show 3 more comments up vote 33 down vote 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(); }; } shareimprove this answer edited Mar 12 '17 at 18:02 community wiki 9 revs Matt Browne add a comment up vote 30 down vote 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)); shareimprove this answer edited Dec 1 '16 at 16:06 community wiki 3 revs, 3 users 71% nathan rogers 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 Sep 24 '15 at 17:15 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 Sep 25 '15 at 15:34 4 For 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 Dec 8 '15 at 23:10 1 You 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 Dec 19 '16 at 20:20 2 Also, 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 Aug 11 '17 at 22:11 show 2 more comments up vote 20 down vote function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; } shareimprove this answer answered Sep 23 '08 at 16:45 community wiki Mark Cidade 14 The 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 Jun 25 '09 at 7:46 1 just make it recursive so the sub objects will be cloned deeply. – fiatjaf Jan 25 '13 at 22:38 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 Feb 4 '13 at 9:42 3 Yes. This is just a shallow copy, so the clone will point to the exact same objects pointed-to by the original object. – Mark Cidade Feb 4 '13 at 10:18 add a comment up vote 19 down vote 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 shareimprove this answer edited Mar 12 '16 at 14:59 community wiki 2 revs, 2 users 50% protonfish 9 correct me if I am wrong, but isn't that Crockford's beget function for prototypal inheritance? How does it apply to clone? – Alex Nolasco Oct 6 '10 at 15:17 3 Yes, 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 Oct 6 '10 at 19:51 49 Difference 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 Dec 6 '10 at 13:13 13 This will break the hasOwnProperty check so its a pretty hacky way to clone an object and will give you unexpected results. – Corban Brook Mar 16 '11 at 18:17 var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; }; ... davidshariff.com/blog/javascript-inheritance-patterns – Cody Apr 24 '14 at 18:32 show 1 more comment up vote 18 down vote 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 shareimprove this answer edited Jul 22 '16 at 17:27 community wiki 3 revs, 2 users 82% Maël Nison 6 This 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 Nov 23 '13 at 17:51 If you copy the prototype too, you'd be missing only non-enumerables and property descriptors, yes? Pretty good. :) – sam Jun 8 '14 at 6:24 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 Mar 17 '16 at 13:56 add a comment up vote 18 down vote Lodash has a nice _.cloneDeep(value) method: var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false shareimprove this answer edited Dec 31 '17 at 12:59 community wiki 2 revs, 2 users 67% opensas 2 I 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. – Jeremy Apr 7 '16 at 17:23 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 Feb 27 '17 at 0:48 3 Google 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 Feb 27 '17 at 0:49 In Node 9, JSON.parse(JSON.stringify(arrayOfAbout5KFlatObjects)) is a lot faster than _.deepClone(arrayOfAbout5KFlatObjects). – Dan Dascalescu Dec 31 '17 at 13:07 add a comment up vote 15 down vote 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. shareimprove this answer edited Oct 15 '16 at 18:38 community wiki 3 revs, 2 users 62% Dan Atkinson or it might be used the same way as jQiery extend: angular.extend({},obj); – Galvani Sep 21 '16 at 9:07 2 @Galvani: It should be noted that jQuery.extend and angular.extend are both shallow copies. angular.copy is a deep copy. – Dan Atkinson Oct 15 '16 at 18:41 add a comment up vote 13 down vote 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) shareimprove this answer edited Dec 13 '13 at 19:00 community wiki 3 revs, 2 users 98% Page Notes 14 as 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 Mar 3 '11 at 21:27 JSON.stringify not work with functions – Vlada Mar 12 '16 at 15:08 add a comment up vote 13 down vote 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. shareimprove this answer edited Jul 22 '16 at 17:33 community wiki 2 revs, 2 users 81% Michael Uzquiano add a comment up vote 11 down vote 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 shareimprove this answer answered Apr 3 '11 at 2:08 community wiki neatonk add a comment up vote 9 down vote AngularJS Well if you're using angular you could do this too var newObject = angular.copy(oldObject); shareimprove this answer answered Sep 14 '16 at 13:26 community wiki azerafati add a comment up vote 9 down vote 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. Jsperf ranks it number one here: https://jsperf.com/deep-copy-vs-json-stringify-json-parse/5 Jsben from the answer above updated to show that a recursive deep clone beats all the others mentioned: http://jsben.ch/13YKQ 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 } shareimprove this answer answered Jun 18 '17 at 6:34 community wiki prograhammer 1 I liked this approach but it doesn't handle dates properly; consider adding something like if(o instanceof Date) return new Date(o.valueOf()); after checking for null ` – Luis Aug 21 '17 at 22:53 Crashes on circular references. – Harry Mar 18 at 5:19 add a comment up vote 8 down vote // 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; }; shareimprove this answer edited Jan 17 at 16:27 community wiki 2 revs, 2 users 70% Dima add a comment up vote 7 down vote 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; }; shareimprove this answer edited Jul 22 '16 at 17:31 community wiki 4 revs, 4 users 59% user1547016 It converts primitives into wrapper objects, not a good solution in most cases. – Danubian Sailor Aug 1 '14 at 9:58 @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 Feb 2 '16 at 18:06 add a comment up vote 6 down vote 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! shareimprove this answer edited Aug 12 '14 at 18:44 community wiki 2 revs Cody 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 May 3 '14 at 10:03 That's cool and all but suppose o has a property a. Now does oo.hasOwnProperty('a')? – user420667 Mar 29 '16 at 23:54 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 Mar 30 '16 at 2:30 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 Jun 22 at 1:07 add a comment up vote 6 down vote 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; } |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|