|
|||||||
How do I correctly clone a JavaScript object?
Время создания: 13.07.2018 15:30
Текстовые метки: javascript clone object
Раздел: Javascript
Запись: Velonski/mytetra-database/master/base/1530785889zw71tqnzb9/text.html на raw.githubusercontent.com
|
|||||||
|
|||||||
I have an object, x. I'd like to copy it as object y, such that changes to y do not modify x. I realized that copying objects derived from built-in JavaScript objects will result in extra, unwanted properties. This isn't a problem, since I'm copying one of my own, literal-constructed objects. How do I correctly clone a JavaScript object? javascript clone shareimprove this question edited May 21 at 5:51 community wiki 13 revs, 8 users 30% mindeavor 28 See this question: stackoverflow.com/questions/122102/… – Niyaz Jun 21 '11 at 10:13 191 For JSON, I use mObj=JSON.parse(JSON.stringify(jsonObject)); – Lord Loh. Feb 2 '13 at 10:09 47 I really don't get why no one suggests Object.create(o), it does everything the author asks? – froginvasion Aug 8 '14 at 15:23 21 var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2; After doing this, y.deep.key will also be 2, hence Object.create CAN NOT BE USED for cloning... – Ruben Stolk Jul 4 '15 at 15:04 12 @r3wt that will not work... Please post only after doing basic test of the solution.. – akshay Feb 16 '16 at 18:54 show 16 more comments 54 Answers active oldest votes 1 2 next up vote 1287 down vote accepted Updated answer Just use Object.assign() as suggested here Outdated answer To do this for any object in JavaScript will not be simple or straightforward. You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance. If, for instance, you are adding a clone method to Object.prototype, as some answers depict, you will need to explicitly skip that attribute. But what if there are other additional methods added to Object.prototype, or other intermediate prototypes, that you don't know about? In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty method. In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties. For example, prototype is a hidden property of a function. Also, an object's prototype is referenced with the attribute __proto__, which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes. I think __proto__ might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically. Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly. If your source object's prototype is Object, then simply creating a new general object with {} will work, but if the source's prototype is some descendant of Object, then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty filter, or which were in the prototype, but weren't enumerable in the first place. One solution might be to call the source object's constructor property to get the initial copy object and then copy over the attributes, but then you still will not get non-enumerable attributes. For example, a Date object stores its data as a hidden member: function clone(obj) { if (null == obj || "object" != typeof obj) return obj; var copy = obj.constructor(); for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr]; } return copy; } var d1 = new Date(); /* Executes function after 5 seconds. */ setTimeout(function(){ var d2 = clone(d1); alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString()); }, 5000); The date string for d1 will be 5 seconds behind that of d2. A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class. I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong! When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object, Array, Date, String, Number, or Boolean. The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing. I further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list. This can be accomplished with code like the following: function clone(obj) { var copy; // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) { copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = clone(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]); } return copy; } throw new Error("Unable to copy obj! Its type isn't supported."); } The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure. That is, there isn't more than one reference to the same data in the object. For example: // This would be cloneable: var tree = { "left" : { "left" : null, "right" : null, "data" : 3 }, "right" : null, "data" : 8 }; // This would kind-of work, but you would get 2 copies of the // inner node instead of 2 references to the same copy var directedAcylicGraph = { "left" : { "left" : null, "right" : null, "data" : 3 }, "data" : 8 }; directedAcyclicGraph["right"] = directedAcyclicGraph["left"]; // Cloning this would cause a stack overflow due to infinite recursion: var cyclicGraph = { "left" : { "left" : null, "right" : null, "data" : 3 }, "data" : 8 }; cyclicGraph["right"] = cyclicGraph; It will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it. shareimprove this answer edited Nov 3 '17 at 11:22 community wiki 12 revs, 8 users 79% A. Levy 2 @javierfp: I think it is reachable. The instanceof operator works by checking the objects prototype chain (according to the Mozilla Javascript reference: developer.mozilla.org/en/JavaScript/Reference/Operators/Special/…). I suppose that someone could modify the prototype chain to no longer include Object. That would be unusual, but would cause the error to be thrown. – A. Levy Jan 21 '11 at 21:44 5 almost worked fine in a nodejs - just had to change the line for (var i = 0, var len = obj.length; i < len; ++i) { to for (var i = 0; i < obj.length; ++i) { – Trindaz Mar 28 '12 at 0:08 5 For future googlers: same deep copy, passing references recursively instead of using 'return' statements at gist.github.com/2234277 – Trindaz Mar 29 '12 at 6:48 3 Would nowadays JSON.parse(JSON.stringify([some object]),[some revirer function]) be a solution? – KooiInc May 20 '12 at 7:42 5 In the first snippet, are you sure it shouldn't be var cpy = new obj.constructor()? – cyon Nov 29 '14 at 3:29 show 22 more comments up vote 711 down vote With jQuery, you can shallow copy with extend: var copiedObject = jQuery.extend({}, originalObject) subsequent changes to the copiedObject will not affect the originalObject, and vice versa. Or to make a deep copy: var copiedObject = jQuery.extend(true, {}, originalObject) shareimprove this answer edited Aug 18 '16 at 13:28 community wiki 5 revs, 5 users 32% Pascal 159 or even: var copiedObject = jQuery.extend({},originalObject); – Grant McLean May 8 '11 at 2:11 80 Also useful to specify true as the first param for deep copy: jQuery.extend(true, {}, originalObject); – Will Shaver Jun 21 '11 at 0:19 5 Yes, I found this link helpful (same solution as Pascal) stackoverflow.com/questions/122102/… – Garry English Nov 16 '11 at 16:31 2 @Will Shaver - YES! That's it! Without option of deep copy it didn't work for me! – thorinkor Oct 17 '13 at 8:41 3 Just a note, this doesn't copy the proto constructor of the original object – Sam Jones May 30 '14 at 15:20 show 7 more comments up vote 681 down vote If you do not use functions within your object, a very simple one liner can be the following: var cloneOfA = JSON.parse(JSON.stringify(a)); This works for all kind of objects containing objects, arrays, strings, booleans and numbers. See also this article about the structured clone algorithm of browsers which is used when posting messages to and from a worker. It also contains a function for deep cloning. shareimprove this answer edited Jan 26 '16 at 14:44 community wiki 9 revs, 2 users 96% heinob 36 Note that this can only be used for testing. Firstly, it's far from optimal in terms of time and memory consumption. Secondly, not all browsers have this methods. – Nux Aug 12 '13 at 18:56 48 @Nux, Why not optimal in terms of time and memory? MiJyn says: "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)." stackoverflow.com/questions/122102/… – BeauCielBleu May 14 '14 at 15:27 9 I just want to add an update to this for Oct. 2014. Chrome 37+ is faster with JSON.parse(JSON.stringify(oldObject)); The benefit of using this is that it's very easy for a javascript engine to see and optimize into something better if it wants. – mirhagk Oct 7 '14 at 21:08 22 It would crap all over the JSON if the object has un-stringify-able things such as Infinity, undefined, etc. Try this object: a = { b: Infinity, c: undefined } – kumar_harsh Oct 8 '14 at 8:42 7 2016 update: This ought to now work in pretty much every browser being widely used. (see Can I use...) The main question now would be whether it is sufficiently performant. – James Foster Feb 10 '16 at 12:15 show 22 more comments up vote 487 down vote In ECMAScript 6 there is Object.assign method, which copies values of all enumerable own properties from one object to another. For example: var x = {myProp: "value"}; var y = Object.assign({}, x); But be aware that nested objects are still copied as reference. shareimprove this answer edited Oct 21 '16 at 11:14 community wiki 3 revs, 3 users 83% Vitalii Fedorenko Yeah, I believe that Object.assign is the way to go. It's easy to polyfill it too: gist.github.com/rafaelrinaldi/43813e707970bd2d77fa – Rafael Feb 11 '16 at 0:52 174 But be aware that this makes a shallow copy only. Nested objects are still copied as references! – ohager Mar 28 '16 at 12:30 15 Also be aware that this will copy over "methods" defined via object literals (since these are enumerable) but not methods defied via the "class" mechanism (since these aren't enumerable). – Marcus Junius Brutus Mar 29 '16 at 15:09 12 I think should be mentioned that this is not suported by IE except Edge. Some people still use this. – Saulius May 29 '16 at 15:09 1 This is the same as @EugeneTiurin his answer. – Wilt Jun 9 '16 at 10:07 show 2 more comments up vote 114 down vote There are many answers, but none that mentions Object.create from ECMAScript 5, which admittedly does not give you an exact copy, but sets the source as the prototype of the new object. Thus, this is not an exact answer to the question, but it is a one-line solution and thus elegant. And it works best for 2 cases: Where such inheritance is useful (duh!) Where the source object won't be modified, thus making the relation between the 2 objects a non issue. Example: var foo = { a : 1 }; var bar = Object.create(foo); foo.a; // 1 bar.a; // 1 foo.a = 2; bar.a; // 2 - prototype changed bar.a = 3; foo.a; // Still 2, since setting bar.a makes it an "own" property Why do I consider this solution to be superior? It's native, thus no looping, no recursion. However, older browsers will need a polyfill. shareimprove this answer answered Mar 19 '12 at 15:17 community wiki itpastorn Note: Object.create is not a deep copy (itpastorn mentioned no recursion). Proof: var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */; – zamnuts Apr 29 '13 at 8:07 In addition to the major identified issues, this can be dangerous in more subtile ways. One easy way to see this: Object.keys(foo) = ['a'] while Object.keys(bar) = [] (getOwnPropertyNames however does work as expected in this situation but hasOwnProperty is also incorrect). Even just using objects as maps, I would be careful using this approach. – Matt Bierner May 1 '13 at 2:51 78 This is prototypal inheritance, not cloning. These are completely different things. The new object doesn't have any of it's own properties, it just points to the prototype's properties. The point of cloning is to create a fresh new object that doesn't reference any properties in another object. – d13 Jan 16 '14 at 16:18 6 I agree with you completely. I do agree as well that this is not cloning as might be 'intended'. But come on people, embrace the nature of JavaScript instead of trying to find obscure solutions that are not standardized. Sure, you don't like prototypes and they are all "blah" to you, but they are in fact very useful if you know what you're doing. – froginvasion Aug 8 '14 at 15:26 4 @RobG:This article explains the difference between referencing and cloning: en.wikipedia.org/wiki/Cloning_(programming). Object.create points to the parent's properties through references.That means if the parent's property values change, the child's will also change. This has some surprising side-effects with nested arrays and objects that could lead to hard-to-find bugs in your code if you're not aware of them: jsbin.com/EKivInO/2. A cloned object is a completely new, independent object that has the same properties and values as the parent, but isn't connected to the parent. – d13 Dec 2 '14 at 14:08 show 10 more comments up vote 104 down vote An elegant way to clone a Javascript 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 answered Dec 15 '15 at 16:42 community wiki Eugene Tiurin Sorry for the dumb question, but why does Object.assign take two parameters when the value function in the polyfill takes only one parameter? – Qwertie May 31 '16 at 23:14 @Qwertie yesterday All the arguments are iterated and merged into one object, prioritizing properties from the last passed arg – Eugene Tiurin Jun 2 '16 at 10:56 Oh I see, thanks (I wasn't familiar with the arguments object before.) I'm having trouble finding Object() via Google... it's a typecast, isn't it? – Qwertie Jun 3 '16 at 15:20 23 this will only perform a shallow "cloning" – Marcus Junius Brutus Jul 25 '16 at 13:27 add a comment up vote 97 down vote Per MDN: If you want shallow copy, use Object.assign({}, a) For "deep" copy, use JSON.parse(JSON.stringify(a)) There is no need for external libraries but you need to check browser compatibility first. shareimprove this answer edited Mar 8 at 7:42 community wiki 3 revs, 2 users 95% Tareq 4 JSON.parse(JSON.stringify(a)) looks beautiful, but before using it I recommend benchmarking the time it takes for your desired collection. Depending on object size, this might not be the quickest option at all. – Edza Apr 5 '17 at 9:54 JSON method I noticed converts date objects to strings but not back to dates. Have to deal with the fun of time-zones in Javascript and manually fix any dates. May be similar cases for other types besides dates – Steve Seeger Apr 12 at 20:34 for Date, I would use moment.js since it has clone functionality. see more here momentjs.com/docs/#/parsing/moment-clone – Tareq Apr 12 at 21:34 add a comment up vote 69 down vote If you're okay with a shallow copy, the underscore.js library has a clone method. y = _.clone(x); or you can extend it like copiedObject = _.extend({},originalObject); shareimprove this answer edited Jan 19 '15 at 18:47 community wiki 2 revs, 2 users 50% dule 32 And lodash has a cloneDeep – dule Jun 4 '13 at 0:11 2 Thanks. Using this technique on a Meteor server. – Turbo Apr 1 '15 at 4:30 Merci man! that did the job for me lodash's amazing I've been using it in a while now. got stuck in a vue project as far as I know vue does not have a built in function to clone an object like angular does have (angular.copy) – Arnaud Bouchot May 16 at 10:08 add a comment up vote 62 down vote There are several issues with most solutions on the internet. So I decided to make a follow-up, which includes, why the accepted answer shouldn't be accepted. starting situation I want to deep-copy a Javascript Object with all of its children and their children and so on. But since I'm not kind of a normal developer, my Object has normal properties, circular structures and even nested objects. So let's create a circular structure and a nested object first. function Circ() { this.me = this; } function Nested(y) { this.y = y; } Let's bring everything together in an Object named a. var a = { x: 'a', circ: new Circ(), nested: new Nested('a') }; Next, we want to copy a into a variable named b and mutate it. var b = a; b.x = 'b'; b.nested.y = 'b'; You know what happened here because if not you wouldn't even land on this great question. console.log(a, b); a --> Object { x: "b", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } } b --> Object { x: "b", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } } Now let's find a solution. JSON The first attempt I tried was using JSON. var b = JSON.parse( JSON.stringify( a ) ); b.x = 'b'; b.nested.y = 'b'; Don't waste too much time on it, you'll get TypeError: Converting circular structure to JSON. Recursive copy (the accepted "answer") Let's have a look at the accepted answer. function cloneSO(obj) { // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { var copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = cloneSO(obj[i]); } return copy; } // Handle Object 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! Its type isn't supported."); } Looks good, heh? It's a recursive copy of the object and handles other types as well, like Date, but that wasn't a requirement. var b = cloneSO(a); b.x = 'b'; b.nested.y = 'b'; Recursion and circular structures doesn't work well together... RangeError: Maximum call stack size exceeded native solution After arguing with my co-worker, my boss asked us what happened, and he found a simple solution after some googling. It's called Object.create. var b = Object.create(a); b.x = 'b'; b.nested.y = 'b'; This solution was added to Javascript some time ago and even handles circular structure. console.log(a, b); a --> Object { x: "a", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } } b --> Object { x: "b", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } } ... and you see, it didn't work with the nested structure inside. polyfill for the native solution There's a polyfill for Object.create in the older browser just like the IE 8. It's something like recommended by Mozilla, and of course, it's not perfect and results in the same problem as the native solution. function F() {}; function clonePF(o) { F.prototype = o; return new F(); } var b = clonePF(a); b.x = 'b'; b.nested.y = 'b'; I've put F outside the scope so we can have a look at what instanceof tells us. console.log(a, b); a --> Object { x: "a", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } } b --> F { x: "b", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } } console.log(typeof a, typeof b); a --> object b --> object console.log(a instanceof Object, b instanceof Object); a --> true b --> true console.log(a instanceof F, b instanceof F); a --> false b --> true Same problem as the native solution, but a little bit worse output. the better (but not perfect) solution When digging around, I found a similar question (In Javascript, when performing a deep copy, how do I avoid a cycle, due to a property being "this"?) to this one, but with a way better solution. function cloneDR(o) { const gdcc = "__getDeepCircularCopy__"; if (o !== Object(o)) { return o; // primitive value } var set = gdcc in o, cache = o[gdcc], result; if (set && typeof cache == "function") { return cache(); } // else o[gdcc] = function() { return result; }; // overwrite if (o instanceof Array) { result = []; for (var i=0; i<o.length; i++) { result[i] = cloneDR(o[i]); } } else { result = {}; for (var prop in o) if (prop != gdcc) result[prop] = cloneDR(o[prop]); else if (set) result[prop] = cloneDR(cache); } if (set) { o[gdcc] = cache; // reset } else { delete o[gdcc]; // unset again } return result; } var b = cloneDR(a); b.x = 'b'; b.nested.y = 'b'; And let's have a look at the output... console.log(a, b); a --> Object { x: "a", circ: Object { me: Object { ... } }, nested: Object { y: "a" } } b --> Object { x: "b", circ: Object { me: Object { ... } }, nested: Object { y: "b" } } console.log(typeof a, typeof b); a --> object b --> object console.log(a instanceof Object, b instanceof Object); a --> true b --> true console.log(a instanceof F, b instanceof F); a --> false b --> false The requirements are matched, but there are still some smaller issues, including changing the instance of nested and circ to Object. The structure of trees that share a leaf won't be copied, they will become two independent leaves: [Object] [Object] / \ / \ / \ / \ |/_ _\| |/_ _\| [Object] [Object] ===> [Object] [Object] \ / | | \ / | | _\| |/_ \|/ \|/ [Object] [Object] [Object] conclusion The last solution using recursion and a cache, may not be the best, but it's a real deep-copy of the object. It handles simple properties, circular structures and nested object, but it will mess up the instance of them while cloning. http://jsfiddle.net/einfallstoll/N4mr2/ shareimprove this answer edited Nov 25 '17 at 10:44 community wiki 5 revs, 3 users 99% Fabio Poloni 5 so the conlcusion is to avoid that problem :) – mikus Oct 23 '14 at 11:39 @mikus until there's a real specification which covers more than just the basic use cases, yes. – Fabio Poloni Oct 23 '14 at 14:41 2 An okay analysis of the solutions provided above but the conclusion drawn by the author indicates that there is no solution to this question. – Amir Mog Aug 16 '16 at 16:53 1 It is shame that JS not includes native clone function. – l00k Nov 14 '16 at 14:53 Among all the top answers, I feel this is close to the correct one. – KTU May 15 '17 at 4:24 show 1 more comment up vote 34 down vote One particularly inelegant solution is to use JSON encoding to make deep copies of objects that do not have member methods. The methodology is to JSON encode your target object, then by decoding it, you get the copy you are looking for. You can decode as many times as you want to make as many copies as you need. Of course, functions do not belong in JSON, so this only works for objects without member methods. This methodology was perfect for my use case, since I'm storing JSON blobs in a key-value store, and when they are exposed as objects in a JavaScript API, each object actually contains a copy of the original state of the object so we can calculate the delta after the caller has mutated the exposed object. var object1 = {key:"value"}; var object2 = object1; object2 = JSON.stringify(object1); object2 = JSON.parse(object2); object2.key = "a change"; console.log(object1);// returns value shareimprove this answer edited Oct 28 '16 at 17:24 community wiki 2 revs, 2 users 60% Tim Why don't functions belong to JSON? I've seen them transfered as JSON more then once... – the_drow Oct 29 '09 at 20:37 3 Functions are not part of the JSON spec becuase they are not a secure (or smart) way to transfer data, which is what JSON was made for. I know the native JSON encoder in Firefox simply ignores functions passed to it, but I'm not sure about the behavior of others. – Kris Walker Oct 30 '09 at 10:27 1 IMHO the best answer, because the OP states "literal-constructed" objects – mark Oct 4 '11 at 11:35 29 There's nothing more inelegant than the use of the word unelegant. – Kevin Laity Jan 26 '12 at 20:18 1 @mark: { 'foo': function() { return 1; } } is a literal-constructed object. – abarnert Aug 14 '12 at 1:58 show 1 more comment up vote 23 down vote For those using AngularJS, there is also direct method for cloning or extending of the objects in this library. var destination = angular.copy(source); or angular.copy(source, destination); More in angular.copy documentation... shareimprove this answer answered Sep 3 '14 at 19:08 community wiki Lukas Jelinek 1 This is a deep copy FYI. – zamnuts Sep 19 '14 at 10:27 add a comment up vote 22 down vote You can simply use a spread property to copy an object without references. But be careful (see comments), the 'copy' is just on the lowest object/array level. Nested properties are still references! Complete clone: let x = {a: 'value1'} let x2 = {...x} // => mutate without references: x2.a = 'value2' console.log(x.a) // => 'value1' Clone with references on second level: const y = {a: {b: 'value3'}} const y2 = {...y} // => nested object is still a references: y2.a.b = 'value4' console.log(y.a.b) // => 'value4' JavaScript actually does not support deep clones natively. Use an utility function. For example Ramda: http://ramdajs.com/docs/#clone shareimprove this answer edited Apr 5 '17 at 12:30 community wiki 5 revs, 3 users 92% musemind 1 This not working... it would work probably when x will be an array for instance x= [ 'ab','cd',...] – Kamil Kiełczewski Apr 14 '16 at 8:23 2 This works, but bear in mind this is a SHALLOW copy, therefore any deep references to others objects remain references! – Bugs Bunny May 17 '16 at 13:01 add a comment up vote 21 down vote OK, imagine you have this object below and you want to clone it: let obj = {a:1, b:2, c:3}; //ES6 or var obj = {a:1, b:2, c:3}; //ES5 The answer is mainly depeneds on which ECMAscript you using, in ES6+, you can simply use Object.assign to do the clone: let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3}; or using spread operator like this: let cloned = {...obj}; //new {a:1, b:2, c:3}; But if you using ES5, you can use few methods, but the JSON.stringify, just make sure you not using for a big chunk of data to copy, but it could be one line handy way in many cases, something like this: let cloned = JSON.parse(JSON.stringify(obj)); //new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over shareimprove this answer edited Jul 6 '17 at 22:48 community wiki 2 revs Alireza add a comment up vote 19 down vote A.Levy's answer is almost complete, here is my little contribution: there is a way how to handle recursive references, see this line if(this[attr]==this) copy[attr] = copy; If the object is XML DOM element, we must use cloneNode instead if(this.cloneNode) return this.cloneNode(true); Inspired by A.Levy's exhaustive study and Calvin's prototyping approach, I offer this solution: Object.prototype.clone = function() { if(this.cloneNode) return this.cloneNode(true); var copy = this instanceof Array ? [] : {}; for(var attr in this) { if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone) copy[attr] = this[attr]; else if(this[attr]==this) copy[attr] = copy; else copy[attr] = this[attr].clone(); } return copy; } Date.prototype.clone = function() { var copy = new Date(); copy.setTime(this.getTime()); return copy; } Number.prototype.clone = Boolean.prototype.clone = String.prototype.clone = function() { return this; } See also Andy Burke's note in the answers. shareimprove this answer edited Dec 2 '12 at 20:49 community wiki Jan Turoň Does not work well with inheritance. – calbertts Jan 2 '14 at 18:52 2 Date.prototype.clone = function() {return new Date(+this)}; – RobG Dec 2 '14 at 12:20 add a comment up vote 18 down vote From this article: How to copy arrays and objects in Javascript by Brian Huisman: Object.prototype.clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (i == 'clone') continue; if (this[i] && typeof this[i] == "object") { newObj[i] = this[i].clone(); } else newObj[i] = this[i] } return newObj; }; shareimprove this answer edited Sep 22 '12 at 22:06 community wiki Calvin 4 This is close, but doesn't work for any object. Try cloning a Date object with this. Not all properties are enumerable, so they will not all show up in the for/in loop. – A. Levy Apr 8 '09 at 4:17 Adding to the object prototype like this broke jQuery for me. Even when I renamed to clone2. – iPadDeveloper2011 Aug 27 '12 at 23:19 2 @iPadDeveloper2011 The code above had a bug in it where it created a global variable called 'i' '(for i in this)', rather than '(for var i in this)'. I have enough karma to edit and it and fix it so I did. – mikemaccana Sep 22 '12 at 22:09 1 @Calvin: this should be created an a non-enumerable property, otherwise 'clone' will appear in 'for' loops. – mikemaccana Oct 1 '12 at 10:54 2 why isn't var copiedObj = Object.create(obj); a great way as well? – Dan P. Apr 12 '14 at 20:16 show 1 more comment up vote 18 down vote In ES-6 you can simply use Object.assign(...). Ex: let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); A good reference is here: https://googlechrome.github.io/samples/object-assign-es6/ shareimprove this answer answered Apr 13 '17 at 20:17 community wiki João Oliveira 8 It does not deep clone the object. – August Jun 1 '17 at 5:31 That's an assignment, not a copy. clone.Title = "just a clone" means that obj.Title = "just a clone". – HoldOffHunger Aug 16 '17 at 16:05 @HoldOffHunger You are mistaken. Check it in your browser's JS console ( let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";) – collapsar Sep 1 '17 at 11:00 @collapsar: That is precisely what I checked, then console.log(person) will be "Whazzup", not "Thor Odinson". See August's comment. – HoldOffHunger Sep 1 '17 at 11:40 @HoldOffHunger Does not happen in Chrome 60.0.3112.113 nor in Edge 14.14393; August's comment does not apply as the values of primitive types of obj's properties are indeed cloned. Property values that are Objects themselves will not be cloned. – collapsar Sep 1 '17 at 12:50 add a comment up vote 16 down vote Here is a function you can use. function clone(obj) { if(obj == null || typeof(obj) != 'object') return obj; var temp = new obj.constructor(); for(var key in obj) temp[key] = clone(obj[key]); return temp; } shareimprove this answer edited Feb 23 '12 at 20:04 community wiki picardo 8 This answer is pretty close, but not quite correct. If you try cloning a Date object, you will not get the same date because the call to the Date constructor function initializes the new Date with the current date/time. That value isn't enumerable and won't be copied by the for/in loop. – A. Levy Apr 8 '09 at 4:21 Not perfect, but nice for those basic cases. E.g. allowing simple cloning of an argument that can be a basic Object, Array or String. – james_womack Nov 1 '13 at 20:36 Upvoted for correctly calling the constructor using new. The accepted answer does not. – GetFree Jun 14 '15 at 6:29 works on node everything else ! still left reference links – user956584 Jul 15 '17 at 22:38 add a comment up vote 14 down vote You can clone an object and remove any reference from the previous one using a single line of code. Simply do: var obj1 = { text: 'moo1' }; var obj2 = Object.create(obj1); // Creates a new clone without references obj2.text = 'moo2'; // Only updates obj2's text property console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'} For browsers / engines that do not currently support Object.create you can use this polyfill: // Polyfill Object.create if it does not exist if (!Object.create) { Object.create = function (o) { var F = function () {}; F.prototype = o; return new F(); }; } shareimprove this answer edited Sep 16 '12 at 5:27 community wiki Rob Evans 1 +1 Object.create(...) seems definitely the way to go. – René Nyffenegger Jun 30 '14 at 14:49 Perfect answer. Maybe you could add an explanation for Object.hasOwnProperty? That way people know how to prevent searching the prototype link. – froginvasion Aug 9 '14 at 12:30 Works well but what browsers does the polyfill work in? – Ian Lunn Oct 9 '14 at 13:25 If there are objects or arrays in the original obj1, and they change the objects or arrays in the new obj2, it will be modified obj1's copies because they're stored by reference. Example: ` var obj1 = { thing: {} }; var obj2 = Object.create(obj1); obj2.thing.innerThing = 'test'; console.log(obj1) ` The output will be that obj1 and obj2 are now { thing: { innerThing: "test" } } – Quinthexadec Oct 17 '14 at 4:32 8 This is creating obj2 with a obj1 as it's prototype. It only works because you are shadowing the text member in obj2. You are not making a copy, just deferring to the prototype chain when a member is not found on obj2. – Nick Desaulniers Oct 31 '14 at 21:25 show 2 more comments up vote 11 down vote New answer to an old question! If you have the pleasure of having using ECMAScript 2016 (ES6) with Spread Syntax, it's easy. keepMeTheSame = {first: "Me!", second: "You!"}; cloned = {...keepMeTheSame} This provides a clean method for a shallow copy of an object. Making a deep copy, meaning makign a new copy of every value in every recursively nested object, requires on of the heavier solutions above. JavaScript keeps evolving. shareimprove this answer answered Dec 16 '16 at 11:34 community wiki Charles Merriam it doesn't work when you have functions defined on objects – Petr Marek Feb 5 '17 at 22:06 as far as I see spread operator only works with iterables - developer.mozilla.org says: var obj = {'key1': 'value1'}; var array = [...obj]; // TypeError: obj is not iterable – Oleh Apr 4 '17 at 8:12 @Oleh so use ` {... obj} instead of [...obj];` – manikant gautam Dec 5 '17 at 6:17 @manikantgautam I was using Object.assign() before, but now indeed object spread syntax is supported in latest Chrome, Firefox (still not in Edge and Safari). Its ECMAScript proposal... but Babel does support it as far as I can see, so probably its safe to use. – Oleh Dec 6 '17 at 15:11 add a comment up vote 10 down vote Using Lodash: var y = _.clone(x, true); shareimprove this answer answered Dec 13 '12 at 0:05 community wiki VaZaA 2 OMG it would be insane to reinvent cloning. This is the only sane answer. – Dan Ross Sep 11 '13 at 8:48 2 I prefer _.cloneDeep(x) as it essentially is the same thing as above, but reads better. – garbanzio Dec 18 '14 at 19:40 @DanRoss true that – Fahmi Apr 15 '17 at 11:29 add a comment up vote 10 down vote let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj) ES6 solution if you want to (shallow) clone a class instance and not just a property object. shareimprove this answer edited Jun 27 '17 at 12:57 community wiki 3 revs flori add a comment up vote 9 down vote Interested in cloning simple objects : JSON.parse(JSON.stringify(json_original)); Source : How to copy JavaScript object to new variable NOT by reference? shareimprove this answer edited May 23 '17 at 12:18 community wiki 2 revs Mohammed Akdim add a comment up vote 6 down vote Jan Turoň's answer above is very close, and may be the best to use in a browser due to compatibility issues, but it will potentially cause some strange enumeration issues. For instance, executing: for ( var i in someArray ) { ... } Will assign the clone() method to i after iterating through the elements of the array. Here's an adaptation that avoids the enumeration and works with node.js: Object.defineProperty( Object.prototype, "clone", { value: function() { if ( this.cloneNode ) { return this.cloneNode( true ); } var copy = this instanceof Array ? [] : {}; for( var attr in this ) { if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone ) { copy[ attr ] = this[ attr ]; } else if ( this[ attr ] == this ) { copy[ attr ] = copy; } else { copy[ attr ] = this[ attr ].clone(); } } return copy; } }); Object.defineProperty( Date.prototype, "clone", { value: function() { var copy = new Date(); copy.setTime( this.getTime() ); return copy; } }); Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } ); Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } ); Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } ); This avoids making the clone() method enumerable because defineProperty() defaults enumerable to false. shareimprove this answer answered Mar 30 '12 at 6:03 community wiki Andy Burke add a comment up vote 6 down vote This is an adaptation of A. Levy's code to also handle the cloning of functions and multiple/cyclic references - what this means is that if two properties in the tree which is cloned are references of the same object, the cloned object tree will have these properties point to one and the same clone of the referenced object. This also solves the case of cyclic dependencies which, if left unhandled, leads to an infinite loop. The complexity of the algorithm is O(n) function clone(obj){ var clonedObjectsArray = []; var originalObjectsArray = []; //used to remove the unique ids when finished var next_objid = 0; function objectId(obj) { if (obj == null) return null; if (obj.__obj_id == undefined){ obj.__obj_id = next_objid++; originalObjectsArray[obj.__obj_id] = obj; } return obj.__obj_id; } function cloneRecursive(obj) { if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj; // Handle Date if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { var copy = []; for (var i = 0; i < obj.length; ++i) { copy[i] = cloneRecursive(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { if (clonedObjectsArray[objectId(obj)] != undefined) return clonedObjectsArray[objectId(obj)]; var copy; if (obj instanceof Function)//Handle Function copy = function(){return obj.apply(this, arguments);}; else copy = {}; clonedObjectsArray[objectId(obj)] = copy; for (var attr in obj) if (attr != "__obj_id" && obj.hasOwnProperty(attr)) copy[attr] = cloneRecursive(obj[attr]); return copy; } throw new Error("Unable to copy obj! Its type isn't supported."); } var cloneObj = cloneRecursive(obj); //remove the unique ids for (var i = 0; i < originalObjectsArray.length; i++) { delete originalObjectsArray[i].__obj_id; }; return cloneObj; } Some quick tests var auxobj = { prop1 : "prop1 aux val", prop2 : ["prop2 item1", "prop2 item2"] }; var obj = new Object(); obj.prop1 = "prop1_value"; obj.prop2 = [auxobj, auxobj, "some extra val", undefined]; obj.nr = 3465; obj.bool = true; obj.f1 = function (){ this.prop1 = "prop1 val changed by f1"; }; objclone = clone(obj); //some tests i've made console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool)); objclone.f1(); console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1')); objclone.f1.prop = 'some prop'; console.log("test function cloning 2: " + (obj.f1.prop == undefined)); objclone.prop2[0].prop1 = "prop1 aux val NEW"; console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1)); console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1)); shareimprove this answer edited Jul 16 '12 at 9:23 community wiki Radu Simionescu 1 As of September 2016, this is the only correct solution to the question. – DomQ Sep 27 '16 at 20:18 well, thanks. feel free to vote it then – Radu Simionescu Sep 28 '16 at 17:58 add a comment up vote 5 down vote I just wanted to add to all the Object.create solutions in this post, that this does not work in the desired way with nodejs. In Firefox the result of var a = {"test":"test"}; var b = Object.create(a); console.log(b);´ is {test:"test"}. In nodejs it is {} shareimprove this answer answered Jun 3 '12 at 9:29 community wiki heinob No this is wrong. b.test returns test. – Shripad Krishna Oct 16 '13 at 20:15 This is prototypal inheritance, not cloning. – d13 Jan 16 '14 at 16:15 1 @d13 while your argument is valid, note that there is no standardized way in JavaScript to clone an object. This is prototypical inheritance, but it can be used as clones nevertheless if you understand the concepts. – froginvasion Aug 8 '14 at 15:28 @froginvasion. The only problem with using Object.create is that nested objects and arrays are just pointer references to the prototype's nested objects and arrays. jsbin.com/EKivInO/2/edit?js,console. Technically a "cloned" object should have its own unique properties that are not shared references to properties on other objects. – d13 Aug 8 '14 at 18:28 @d13 okay, I see your point now. But what I meant is that too many people are alienated with the concept of prototypical inheritance, and to me fail to learn how it works. If I'm not mistaken, your example can be fixed by just calling Object.hasOwnProperty to check whether you own the array or not. Yes this does add additional complexity to deal with prototypical inheritance. – froginvasion Aug 9 '14 at 10:13 add a comment up vote 5 down vote 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 31 '12 at 20:53 community wiki user1547016 Did this solution help you ? Let me know if u see any issues. – user1547016 Aug 1 '12 at 17:00 2 if(!src && typeof src != "object"){. I think that should be || not &&. – MikeM Apr 8 '13 at 9:34 A vote for knowing how to copy a Date in less than 3 lines of code. – RobG Dec 2 '14 at 12:32 add a comment up vote 5 down vote Since mindeavor stated that the object to be cloned is a 'literal-constructed' object, a solution might be to simply generate the object multiple times rather than cloning an instance of the object: function createMyObject() { var myObject = { ... }; return myObject; } var myObjectInstance1 = createMyObject(); var myObjectInstance2 = createMyObject(); shareimprove this answer edited May 23 '17 at 12:34 community wiki 2 revs Bert Regelink add a comment up vote 5 down vote I've written my own implementation. Not sure if it counts as a better solution: /* a function for deep cloning objects that contains other nested objects and circular structures. objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object. index (z) | | | | | | depth (x) |_ _ _ _ _ _ _ _ _ _ _ _ /_/_/_/_/_/_/_/_/_/ /_/_/_/_/_/_/_/_/_/ /_/_/_/_/_/_/...../ /................./ /..... / / / /------------------ object length (y) / */ Following is the implementation: function deepClone(obj) { var depth = -1; var arr = []; return clone(obj, arr, depth); } /** * * @param obj source object * @param arr 3D array to store the references to objects * @param depth depth of the current object relative to the passed 'obj' * @returns {*} */ function clone(obj, arr, depth){ if (typeof obj !== "object") { return obj; } var length = Object.keys(obj).length; // native method to get the number of properties in 'obj' var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object if(result instanceof Array){ result.length = length; } depth++; // depth is increased because we entered an object here arr[depth] = []; // this is the x-axis, each index here is the depth arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props) // start the depth at current and go down, cyclic structures won't form on depths more than the current one for(var x = depth; x >= 0; x--){ // loop only if the array at this depth and length already have elements if(arr[x][length]){ for(var index = 0; index < arr[x][length].length; index++){ if(obj === arr[x][length][index]){ return obj; } } } } arr[depth][length].push(obj); // store the object in the array at the current depth and length for (var prop in obj) { if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth); } return result; } shareimprove this answer edited Jul 29 '16 at 16:32 community wiki 3 revs, 2 users 70% yazjisuhail add a comment up vote 5 down vote Bellow is my version of deep cloning, covering functions and with handling for circular references. https://github.com/radsimu/UaicNlpToolkit/blob/master/Modules/GGS/GGSEngine/src/main/resources/ro/uaic/info/nlptools/ggs/engine/core/jsInitCode.js#L17 shareimprove this answer edited Feb 5 at 9:24 community wiki 4 revs Radu Simionescu Pretty sure the link is dead. – Patrick Roberts Feb 4 at 0:49 @PatrickRoberts - thanks for the heads up. I updated the answer with my version of clone that I eventually built at the time – Radu Simionescu Feb 5 at 9:25 add a comment up vote 4 down vote Consult http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data for the W3C's "Safe passing of structured data" algorithm, intended to be implemented by browsers for passing data to eg web workers. However, it has some limitations, in that it does not handle functions. See https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm for more information, including an alternative algorithm in JS which gets you part of the way there. |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|