我如何正确地克隆一个JavaScript对象?
我有一个对象, x
。 我想将它复制为对象y
,这样对y
更改不会修改x
。 我意识到复制从内置JavaScript对象派生的对象将导致额外的不需要的属性。 这不是一个问题,因为我正在复制我自己的,字面构造的对象之一。
我如何正确地克隆一个JavaScript对象?
更新了答案
只要使用Object.assign()按照这里所build议的
过时的答案
对于JavaScript中的任何对象来说,这不是简单或直接的。 您将遇到错误地从对象的原型中拾取属性的问题,这些属性应该保留在原型中,而不是复制到新的实例中。 例如,如果您正在将一个clone
方法添加到Object.prototype
,如某些答案所示,您将需要显式跳过该属性。 但是,如果还有其他额外的方法添加到Object.prototype
或其他中间原型中,那么您不知道该怎么办? 在这种情况下,您将复制您不应该的属性,因此您需要使用hasOwnProperty
方法检测不可预见的非本地属性。
除了不可枚举的属性之外,当您尝试复制具有隐藏属性的对象时,您将遇到一个更加棘手的问题。 例如, prototype
是一个函数的隐藏属性。 另外,一个对象的原型被引用了属性__proto__
,该属性也是隐藏的,并且不会被for / in循环复制到源对象的属性上。 我认为__proto__
可能是Firefox的JavaScript解释器所特有的,在其他浏览器中可能会有所不同,但是您可以看到。 并非一切都是可以枚举的。 如果你知道它的名字,你可以复制一个隐藏的属性,但我不知道有什么办法自动发现它。
寻求一个优雅的解决scheme的另一个障碍是正确设置原型inheritance的问题。 如果你的源对象的原型是Object
,那么简单地用{}
创build一个新的通用对象就可以工作,但是如果源的原型是Object
后代,那么你会错过使用hasOwnProperty
filter,或者是在原型中,但不是首先枚举。 一种解决scheme可能是调用源对象的constructor
属性来获取初始复制对象,然后复制属性,但是仍然不会获得不可枚举的属性。 例如, Date
对象将其数据存储为隐藏成员:
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);
d1
的datestring将比d2
的datestring晚5秒。 使一个Date
与另一个Date
相同的方法是调用setTime
方法,但这是特定于Date
类的。 我不认为这个问题有一个防弹的通用解决scheme,虽然我会很高兴做错了!
当我不得不实现一般的深层复制时,我最终只能假设我只需要复制一个普通的Object
, Array
, Date
, String
, Number
或者Boolean
。 最后3种types是不可变的,所以我可以执行浅拷贝而不用担心它的变化。 我还进一步假定Object
或Array
包含的任何元素也是该列表中的6个简单types之一。 这可以用下面的代码来完成:
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."); }
只要对象和数组中的数据形成一个树形结构,上面的函数对于我提到的6种简单types就可以正常工作。 也就是说,对象中的相同数据不多于一个引用。 例如:
// 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;
它将无法处理任何JavaScript对象,但只要您不假定它只适用于任何对象,就可能用于多种目的。
使用jQuery,你可以使用extend进行浅拷贝 :
var copiedObject = jQuery.extend({}, originalObject)
复制对象的后续更改不会影响originalObject,反之亦然。
或者做一个深层复制 :
var copiedObject = jQuery.extend(true, {}, originalObject)
如果你不使用你的对象中的函数,一个非常简单的一个class轮可以是以下内容:
var cloneOfA = JSON.parse(JSON.stringify(a));
这适用于包含对象,数组,string,布尔值和数字的所有types的对象。
另请参阅本文有关在向工作人员发布消息时使用的浏览器的结构化克隆algorithm 。 它还包含深层克隆的function。
在ECMAScript 6中有Object.assign方法,它将所有可枚举属性的值从一个对象复制到另一个对象。 例如:
var x = {myProp: "value"}; var y = Object.assign({}, x);
但请注意,嵌套对象仍作为参考复制。
有很多答案,但没有一个提到ECMAScript 5中的Object.create ,但是它并没有给你一个确切的副本,而是将源代码设置为新对象的原型。
因此,这不是问题的确切答案,而是一个单线解决scheme,因此是优雅的。 最适合2种情况:
- 如果这种inheritance是有用的(杜!)
- 源对象不会被修改,从而使两个对象之间的关系不成问题。
例:
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
为什么我认为这个解决scheme是优越的? 它是本地的,因此没有循环,没有recursion。 不过,旧版本的浏览器需要使用polyfill。
在一行代码中克隆Javascript对象的一种优雅的方式
Object.assign
方法是ECMAScript 2015(ES6)标准的一部分,完全符合您的需求。
var clone = Object.assign({}, obj);
Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。
阅读更多…
支持较旧浏览器的polyfill :
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; } }); }
如果您可以使用浅拷贝,那么underscore.js库就有一个克隆方法。
y = _.clone(x);
或者你可以像这样扩展它
copiedObject = _.extend({},originalObject);
每MDN :
- 如果你想浅拷贝,使用
Object.assign({}, a)
- 对于“深层”复制,请使用
JSON.parse(JSON.stringify(a))
而不需要外部的库,但你需要首先检查浏览器的兼容性 。
互联网上的大多数解决scheme都有几个问题。 所以我决定做一个跟进,其中包括为什么接受的答案不应该被接受。
开始的情况
我想深入复制一个JavaScript Object
与其所有的孩子和他们的孩子,等等。 但由于我不是一个正常的开发人员,我的Object
具有正常的 properties
, circular structures
甚至nested objects
。
所以让我们先创build一个circular structure
和一个nested object
。
function Circ() { this.me = this; } function Nested(y) { this.y = y; }
让我们把所有东西放在一个名为a
的Object
。
var a = { x: 'a', circ: new Circ(), nested: new Nested('a') };
接下来我们要复制a
名为b
的variables并对其进行变异。
var b = a; bx = 'b'; b.nested.y = 'b';
你知道这里发生了什么,因为如果不是,你甚至不会落在这个重大的问题上。
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" } }
现在让我们find一个解决scheme。
JSON
我尝试的第一次尝试是使用JSON
。
var b = JSON.parse( JSON.stringify( a ) ); bx = 'b'; b.nested.y = 'b';
不要浪费太多时间,你会得到TypeError: Converting circular structure to JSON
。
recursion拷贝(被接受的“答案”)
让我们来看看接受的答案。
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."); }
看起来不错,哈? 它是对象的recursion副本,并处理其他types,如Date
,但这不是必需的。
var b = cloneSO(a); bx = 'b'; b.nested.y = 'b';
recursion和circular structures
不能很好地协同工作RangeError: Maximum call stack size exceeded
原生解决scheme
在和我的同事争论之后,我的老板问我们发生了什么事,他找了一个简单的解决办法 。 它被称为Object.create
。
var b = Object.create(a); bx = 'b'; b.nested.y = 'b';
这个解决scheme前一段时间被添加到Javascript,甚至处理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" } }
…你看,它不适用于里面的嵌套结构。
本地解决scheme的polyfill
像IE8一样,在旧浏览器中有一个Object.create
的polyfill,就像Mozilla推荐的那样,当然这不是完美的,会导致与本地解决scheme相同的问题。
function F() {}; function clonePF(o) { F.prototype = o; return new F(); } var b = clonePF(a); bx = 'b'; b.nested.y = 'b';
我已经把F
放在范围之外,所以我们可以看看instanceof
告诉我们什么。
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
与本机解决scheme相同的问题,但输出稍差一些。
更好的(但不是完美的)解决scheme
在挖掘时,我发现了一个类似的问题( 在Javascript中,当执行深层复制时,如何避免一个周期,因为属性是“this”? ),但有一个更好的解决scheme。
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); bx = 'b'; b.nested.y = 'b';
让我们看看输出…
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
这些需求是相匹配的,但是仍然有一些小问题,包括将nested
和circ
的instance
更改为Object
。
分享树叶的树木的结构不会被复制,它们将成为两个独立的树叶:
[Object] [Object] / \ / \ / \ / \ |/_ _\| |/_ _\| [Object] [Object] ===> [Object] [Object] \ / | | \ / | | _\| |/_ \|/ \|/ [Object] [Object] [Object]
结论
最后一个使用recursion和caching的解决scheme可能不是最好的,但它是对象的一个真正的深层副本。 它处理简单的properties
, circular structures
和nested object
,但是在克隆的时候会弄乱它们的实例。
一个特别不雅的解决scheme是使用JSON编码来创build没有成员方法的对象的深层副本。 该方法是JSON编码你的目标对象,然后解码它,你得到你正在寻找的副本。 您可以根据需要多次解码,以便制作尽可能多的副本。
当然,函数不属于JSON,所以这只适用于没有成员方法的对象。
这个方法对于我的用例是完美的,因为我将JSON blob存储在一个键值存储中,当它们作为JavaScript API中的对象公开时,每个对象实际上都包含对象的原始状态的副本,所以我们可以在调用者突变暴露的对象之后计算增量。
var object1 = {key:"value"}; var object2 = object1; object2 = JSON.stringify(object1); object2 = JSON.parse(object2); object2.key = "a change"; console.log(object1);// returns value
对于那些使用AngularJS的人来说,还有直接的方法来克隆或扩展这个库中的对象。
var destination = angular.copy(source);
要么
angular.copy(source, destination);
更多在angular.copy 文件 …
A.Levy的答案差不多完成了,下面是我的一点贡献: 有一种方法如何处理recursion引用 ,看到这一行
if(this[attr]==this) copy[attr] = copy;
如果对象是XML DOM元素,则必须改用cloneNode
if(this.cloneNode) return this.cloneNode(true);
受到A.Levy的详尽研究和Calvin原型方法的启发,我提供了这个解决scheme:
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; }
请参阅Andy Burke的答案。
从这篇文章: 如何通过布赖恩Huisman 在Javascript中复制数组和对象 :
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; };
好吧,想象下面有这个对象,你想克隆它:
let obj = {a:1, b:2, c:3}; //ES6
要么
var obj = {a:1, b:2, c:3}; //ES5
答案主要依赖于你使用的ECMAscript ,在ES6+
,你可以简单地使用Object.assign
来做这个clone:
let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};
或者使用这样的扩展运算符:
let cloned = {...obj}; //new {a:1, b:2, c:3};
但是如果你使用ES5
,你可以使用很less的方法,但是使用JSON.stringify
,只要确保你不使用大块的数据来复制,但是在许多情况下它可能是一个方便的方法,像这样:
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
这是一个你可以使用的function。
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; }
您可以简单地使用spread属性来复制一个没有引用的对象。 但要小心(见注释),“复制”只是在最低的对象/数组级别上。 嵌套属性仍然是参考!
完整的克隆:
let x = {a: 'value1'} let x2 = {...x} // => mutate without references: x2.a = 'value2' console.log(xa) // => 'value1'
在第二级克隆与参考:
const y = {a: {b: 'value3'}} const y2 = {...y} // => nested object is still a references: y2.ab = 'value4' console.log(yab) // => 'value4'
JavaScript实际上本身不支持深克隆。 使用实用function。 例如拉姆达:
您可以克隆一个对象,并使用一行代码从前一个对象中删除任何引用。 简单地做:
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'}
对于目前不支持Object.create的浏览器/引擎,你可以使用这个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(); }; }
In ES-6 you can simply use Object.assign(…). 例如:
let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj);
A good reference is here: https://googlechrome.github.io/samples/object-assign-es6/
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.
Using Lodash:
var y = _.clone(x, true);
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.
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.
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));
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);´
是
{test:"test"}
.
In nodejs it is
{}
One thing is certain… there is no elegant way. My contribution here is this link http://oranlooney.com/deep-copy-javascript/
I think this library is the most comprehensive and elegant solution. It is particularly focused on the prototype chain of the cloned objects. It includes a mechanism to easy describe custom cloning behavior for particular classes. The author explains that most of the time the default cloning mechanism which is provided works fine.
It also clones functions and handles cycles or multiple references.
I have recently suggested the author a solution to optimize the mechanism which handles cycles, from O(n^2) to O(n).
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; };
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();
Interested in cloning simple objects :
JSON.parse(JSON.stringify(json_original));
Source : How to copy JavaScript object to new variable NOT by reference?
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; }
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.