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?
83 Answers 83
2022 update
There's a new JS standard called structured cloning. It works in many browsers (see Can I Use).
const clone = structuredClone(object);
Old 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.
-
This is missing symbol keys and symbol values. Nowadays, using
Object.getOwnPropertyDescriptors
is better.Sebastian Simon– Sebastian Simon2021年07月10日 15:33:53 +00:00Commented Jul 10, 2021 at 15:33 -
structuredClone is only 75% compatible globallyJoshuaDavid– JoshuaDavid2022年05月17日 21:27:04 +00:00Commented May 17, 2022 at 21:27
-
2Update on @JoshuaDavid, currently supported in 82.57% of all browsers.Kay Angevare– Kay Angevare2022年06月15日 14:23:48 +00:00Commented Jun 15, 2022 at 14:23
-
2what if I want to
clone
theobject
and also want toappend
something to it?Azhar Uddin Sheikh– Azhar Uddin Sheikh2022年09月06日 14:38:58 +00:00Commented Sep 6, 2022 at 14:38 -
1Ran into issues using this function on mobile devices.. - iOS 11,12,13,14, not working on Safari and Google Chrome. (works on iOS 15,16) - Android 12, 11, 10, 9 on SamsungInternet browser (chrome works).jfxninja– jfxninja2022年09月21日 14:49:58 +00:00Commented Sep 21, 2022 at 14:49
If you do not use Date
s, functions, undefined, regExp or Infinity within your object, a very simple one liner is JSON.parse(JSON.stringify(object))
:
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()
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.
-
1sometimes the best answers are the simplest. genius.dewd– dewd2021年11月23日 16:30:31 +00:00Commented Nov 23, 2021 at 16:30
-
Helpful, but when comparing objects which contains other objects, I ran through unexpected behaviour when two exactly equal objects where not taken as equals. used JSON.stringify(x) == JSON.stringify(JSON.parse(JSON.stringify(a))) to fix it. For some reason, comparing as strings works perfectly as expected when comparing, could not match otherwise.Agustin L. Lacuara– Agustin L. Lacuara2022年04月28日 13:36:25 +00:00Commented Apr 28, 2022 at 13:36
-
@AgustinL.Lacuara You cannot compare complex datatypes in JS.
a={};b={}; a==b
isfalse
. But aftera=b
it becomestrue
, because it is not only identical but it is the same object.heinob– heinob2022年04月28日 18:47:32 +00:00Commented Apr 28, 2022 at 18:47 -
3does the job, but, this is goes against any good programming practice. In brazil, we call that a 'Gambiarra'Diego Favero– Diego Favero2022年07月05日 19:03:39 +00:00Commented Jul 5, 2022 at 19:03
-
This is the easiest solution if you don't mind poor performance caused by encoding to string and decoding the data to create new object. The global function
structuredClone()
is much better if the user base has modern enough JavaScript engines.Mikko Rantalainen– Mikko Rantalainen2024年08月23日 12:42:15 +00:00Commented Aug 23, 2024 at 12:42
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 this is a shallow copy - nested objects are still copied as reference.
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)
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.
-
The problem happens when you have functions in your object JSON.parse(JSON.stringify(a))Tosh– Tosh2022年07月29日 12:20:25 +00:00Commented Jul 29, 2022 at 12:20
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.
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;
}
});
}
-
60this will only perform a shallow "cloning"Marcus Junius Brutus– Marcus Junius Brutus2016年07月25日 13:27:52 +00:00Commented Jul 25, 2016 at 13:27
-
I learned the hard way that
objA = objB;
causes all kinds of headaches. This seems to have solved the problem, at least for now...WinEunuuchs2Unix– WinEunuuchs2Unix2022年04月17日 23:38:08 +00:00Commented Apr 17, 2022 at 23:38
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.
-
117This 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– d132014年01月16日 16:18:16 +00:00Commented Jan 16, 2014 at 16:18
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.
-
13so the conlcusion is to avoid that problem :)mikus– mikus2014年10月23日 11:39:20 +00:00Commented Oct 23, 2014 at 11:39
-
@mikus until there's a real specification which covers more than just the basic use cases, yes.Fabio Poloni– Fabio Poloni2014年10月23日 14:41:32 +00:00Commented Oct 23, 2014 at 14:41
-
2An 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– Amir Mog2016年08月16日 16:53:51 +00:00Commented Aug 16, 2016 at 16:53
-
2It is shame that JS not includes native clone function.l00k– l00k2016年11月14日 14:53:48 +00:00Commented Nov 14, 2016 at 14:53
-
1Among all the top answers, I feel this is close to the correct one.KTU– KTU2017年05月15日 04:24:50 +00:00Commented May 15, 2017 at 4:24
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);
-
To get started quickly with lodash, I'd recommend learning npm, Browserify, as well as lodash. I got clone to work with 'npm i --save lodash.clone' and then 'var clone = require('lodash.clone');' To get require to work, you need something like browserify. Once you install it and learn how it works, you’ll use 'browserify yourfile.js > bundle.js;start chrome index.html' every time you run your code (instead of going into Chrome directly). This gathers your file and all the files you required from the npm module into bundle.js. You can probably save time and automate this step with Gulp though.user11104582– user111045822019年04月09日 19:43:49 +00:00Commented Apr 9, 2019 at 19:43
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
-
Can you please give example of what
big chunk of data
would equate to? 100kb? 100MB? Thanks!user1063287– user10632872018年09月14日 11:04:47 +00:00Commented Sep 14, 2018 at 11:04 -
Yes, @user1063287, that basically the bigger data, the performnce worse... so it really depends, not a kb, mb or gb, it's more about how many times you wanna do that also... Also it won't work for functions and other stuffs...Alireza– Alireza2019年03月14日 09:58:57 +00:00Commented Mar 14, 2019 at 9:58
-
4
Object.assign
makes a shallow copy (just as the spread, @Alizera)Bogdan D– Bogdan D2019年05月29日 13:42:54 +00:00Commented May 29, 2019 at 13:42 -
You can't use let in es5 :^) @AlirezaWomble– Womble2020年05月01日 14:56:45 +00:00Commented May 1, 2020 at 14:56
Update 06 July 2020
There are three (3) ways to clone objects in JavaScript. As objects in JavaScript are reference values, you can't simply just copy using the =.
The ways are:
const food = { food: 'apple', drink: 'milk' }
// 1. Using the "Spread"
// ------------------
{ ...food }
// 2. Using "Object.assign"
// ------------------
Object.assign({}, food)
// 3. "JSON"
// ------------------
JSON.parse(JSON.stringify(food))
// RESULT:
// { food: 'apple', drink: 'milk' }
This can be used as a reference summary.
-
And this adds what new/unique information to this question?Andreas– Andreas2020年07月06日 08:24:45 +00:00Commented Jul 6, 2020 at 8:24
-
12The
JSON
approach would remove any methods of the objectAndreas– Andreas2020年07月06日 08:25:18 +00:00Commented Jul 6, 2020 at 8:25 -
3To create a string from an object and then parsing that string into another object just to copy the object is a kind of Monty Python's style of programming :-DJaromír Adamec– Jaromír Adamec2021年01月10日 14:24:11 +00:00Commented Jan 10, 2021 at 14:24
-
1This only works for object literals and objects that can be represented as such, but not for generic "objects" like you encounter in OO languages. This seems to be what the OP asked for thus that's OK, but it' not a universal solution for every kind of object.bart– bart2021年01月31日 12:34:17 +00:00Commented Jan 31, 2021 at 12:34
-
4The spread operator and Object.assign fail for objects with a hierarchy, ie. nested objects. JSON.parse/stringify works, but as mentioned does not copy methods.iCode– iCode2021年03月31日 17:40:15 +00:00Commented Mar 31, 2021 at 17:40
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
-
Why don't functions belong to JSON? I've seen them transfered as JSON more then once...the_drow– the_drow2009年10月29日 20:37:17 +00:00Commented Oct 29, 2009 at 20:37
-
6Functions 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– Kris Walker2009年10月30日 10:27:00 +00:00Commented Oct 30, 2009 at 10:27
-
1@mark:
{ 'foo': function() { return 1; } }
is a literal-constructed object.abarnert– abarnert2012年08月14日 01:58:39 +00:00Commented Aug 14, 2012 at 1:58 -
@abarnert functions are not data. "Function literals" is a misnomer - since functions can contain arbitrary code, including assignments and all sort of "non-serializable" things.deprecated– deprecated2013年04月30日 10:38:32 +00:00Commented Apr 30, 2013 at 10:38
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:
-
1This not working... it would work probably when x will be an array for instance x= [ 'ab','cd',...]Kamil Kiełczewski– Kamil Kiełczewski2016年04月14日 08:23:15 +00:00Commented Apr 14, 2016 at 8:23
-
4This works, but bear in mind this is a SHALLOW copy, therefore any deep references to others objects remain references!Bugs Bunny– Bugs Bunny2016年05月17日 13:01:04 +00:00Commented May 17, 2016 at 13:01
-
A partial clone can also happen in this way:
const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Christian Vincenzo Traina– Christian Vincenzo Traina2018年08月13日 17:37:39 +00:00Commented Aug 13, 2018 at 17:37
const objClone = { ...obj };
Be aware that nested objects are still copied as a reference.
-
2Thanks for the hint that nested objects are still copied as a reference! I almost got crazy when debugging my code because I modified nested properties on the "clone" but the original got modified.Benny Neugebauer– Benny Neugebauer2019年01月02日 23:32:29 +00:00Commented Jan 2, 2019 at 23:32
-
This is ES2016, not 2018, and this answer was given two years earlier.Dan Dascalescu– Dan Dascalescu2019年06月13日 23:37:24 +00:00Commented Jun 13, 2019 at 23:37
-
so what if i want copy of nested property as wellCode Guru– Code Guru2019年09月04日 08:12:03 +00:00Commented Sep 4, 2019 at 8:12
-
3@SunilGarg To copy nested property as well you can use
const objDeepClone = JSON.parse(JSON.stringify(obj));
Pavan Garre– Pavan Garre2019年09月06日 13:45:31 +00:00Commented Sep 6, 2019 at 13:45
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;
};
-
4This 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– A. Levy2009年04月08日 04:17:18 +00:00Commented Apr 8, 2009 at 4:17
-
Adding to the object prototype like this broke jQuery for me. Even when I renamed to clone2.iPadDeveloper2011– iPadDeveloper20112012年08月27日 23:19:56 +00:00Commented Aug 27, 2012 at 23:19
-
3@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– mikemaccana2012年09月22日 22:09:36 +00:00Commented Sep 22, 2012 at 22:09
-
2@Calvin: this should be created an a non-enumerable property, otherwise 'clone' will appear in 'for' loops.mikemaccana– mikemaccana2012年10月01日 10:54:23 +00:00Commented Oct 1, 2012 at 10:54
-
3why isn't
var copiedObj = Object.create(obj);
a great way as well?Dan P.– Dan P.2014年04月12日 20:16:53 +00:00Commented Apr 12, 2014 at 20:16
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...
-
3This is a deep copy FYI.zamnuts– zamnuts2014年09月19日 10:27:07 +00:00Commented Sep 19, 2014 at 10:27
Performance
Today 2020年04月30日 I perform tests of chosen solutions on Chrome v81.0, Safari v13.1 and Firefox v75.0 on MacOs High Sierra v10.13.6.
I focus on speed of copy DATA (object with simple type fields, not methods etc.). The solutions A-I can make only shallow copy, solutions J-U can make deep copy.
Results for shallow copy
- solution
{...obj}
(A) is fastest on chrome and firefox and medium fast on safari - solution based on
Object.assign
(B) is fast on all browsers - jQuery (E) and lodash (F,G,H) solutions are medium/quite fast
- solution
JSON.parse/stringify
(K) is quite slow - solutions D and U are slow on all browsers
Results for deep copy
- solution Q is fastest on all browsers
- jQuery (L) and lodash (J) are medium fast
- solution
JSON.parse/stringify
(K) is quite slow - solution U is slowest on all browsers
- lodash (J) and solution U crash on Chrome for 1000 level deep object
Details
For choosen solutions: A B C(my) D E F G H I J K L M N O P Q R S T U, I perform 4 tests
- shallow-small: object with 10 non-nested fields - you can run it HERE
- shallow-big: object with 1000 non-nested fields - you can run it HERE
- deep-small: object with 10 levels-nested fields - you can run it HERE
- deep-big: object with 1000 levels-nested fields - you can run it HERE
Objects used in tests are show in below snippet
let obj_ShallowSmall = {
field0: false,
field1: true,
field2: 1,
field3: 0,
field4: null,
field5: [],
field6: {},
field7: "text7",
field8: "text8",
}
let obj_DeepSmall = {
level0: {
level1: {
level2: {
level3: {
level4: {
level5: {
level6: {
level7: {
level8: {
level9: [[[[[[[[[['abc']]]]]]]]]],
}}}}}}}}},
};
let obj_ShallowBig = Array(1000).fill(0).reduce((a,c,i) => (a['field'+i]=getField(i),a) ,{});
let obj_DeepBig = genDeepObject(1000);
// ------------------
// Show objects
// ------------------
console.log('obj_ShallowSmall:',JSON.stringify(obj_ShallowSmall));
console.log('obj_DeepSmall:',JSON.stringify(obj_DeepSmall));
console.log('obj_ShallowBig:',JSON.stringify(obj_ShallowBig));
console.log('obj_DeepBig:',JSON.stringify(obj_DeepBig));
// ------------------
// HELPERS
// ------------------
function getField(k) {
let i=k%10;
if(i==0) return false;
if(i==1) return true;
if(i==2) return k;
if(i==3) return 0;
if(i==4) return null;
if(i==5) return [];
if(i==6) return {};
if(i>=7) return "text"+k;
}
function genDeepObject(N) {
// generate: {level0:{level1:{...levelN: {end:[[[...N-times...['abc']...]]] }}}...}}}
let obj={};
let o=obj;
let arr = [];
let a=arr;
for(let i=0; i<N; i++) {
o['level'+i]={};
o=o['level'+i];
let aa=[];
a.push(aa);
a=aa;
}
a[0]='abc';
o['end']=arr;
return obj;
}
Below snippet presents tested solutions and shows differences between them
function A(obj) {
return {...obj}
}
function B(obj) {
return Object.assign({}, obj);
}
function C(obj) {
return Object.keys(obj).reduce( (a,c) => (a[c]=obj[c], a), {})
}
function D(obj) {
let copyOfObject = {};
Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(obj));
return copyOfObject;
}
function E(obj) {
return jQuery.extend({}, obj) // shallow
}
function F(obj) {
return _.clone(obj);
}
function G(obj) {
return _.clone(obj,true);
}
function H(obj) {
return _.extend({},obj);
}
function I(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;
}
function J(obj) {
return _.cloneDeep(obj,true);
}
function K(obj) {
return JSON.parse(JSON.stringify(obj));
}
function L(obj) {
return jQuery.extend(true, {}, obj) // deep
}
function M(obj) {
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = new obj.constructor();
for(var key in obj)
temp[key] = M(obj[key]);
return temp;
}
function N(obj) {
let EClone = function(obj) {
var newObj = (obj instanceof Array) ? [] : {};
for (var i in obj) {
if (i == 'EClone') continue;
if (obj[i] && typeof obj[i] == "object") {
newObj[i] = EClone(obj[i]);
} else newObj[i] = obj[i]
} return newObj;
};
return EClone(obj);
};
function O(obj) {
if (obj == null || typeof obj != "object") return obj;
if (obj.constructor != Object && obj.constructor != Array) return obj;
if (obj.constructor == Date || obj.constructor == RegExp || obj.constructor == Function ||
obj.constructor == String || obj.constructor == Number || obj.constructor == Boolean)
return new obj.constructor(obj);
let to = new obj.constructor();
for (var name in obj)
{
to[name] = typeof to[name] == "undefined" ? O(obj[name], null) : to[name];
}
return to;
}
function P(obj) {
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;
}
return clone({},obj);
}
function Q(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] = Q(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = Q(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
function R(obj) {
const gdcc = "__getDeepCircularCopy__";
if (obj !== Object(obj)) {
return obj; // primitive value
}
var set = gdcc in obj,
cache = obj[gdcc],
result;
if (set && typeof cache == "function") {
return cache();
}
// else
obj[gdcc] = function() { return result; }; // overwrite
if (obj instanceof Array) {
result = [];
for (var i=0; i<obj.length; i++) {
result[i] = R(obj[i]);
}
} else {
result = {};
for (var prop in obj)
if (prop != gdcc)
result[prop] = R(obj[prop]);
else if (set)
result[prop] = R(cache);
}
if (set) {
obj[gdcc] = cache; // reset
} else {
delete obj[gdcc]; // unset again
}
return result;
}
function S(obj) {
const cache = new WeakMap(); // Map of old - new references
function copy(object) {
if (typeof object !== 'object' ||
object === null ||
object instanceof HTMLElement
)
return object; // primitive value or HTMLElement
if (object instanceof Date)
return new Date().setTime(object.getTime());
if (object instanceof RegExp)
return new RegExp(object.source, object.flags);
if (cache.has(object))
return cache.get(object);
const result = object instanceof Array ? [] : {};
cache.set(object, result); // store reference to object before the recursive starts
if (object instanceof Array) {
for(const o of object) {
result.push(copy(o));
}
return result;
}
const keys = Object.keys(object);
for (const key of keys)
result[key] = copy(object[key]);
return result;
}
return copy(obj);
}
function T(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;
}
function U(obj) {
/*
Deep copy objects by value rather than by reference,
exception: `Proxy`
*/
const seen = new WeakMap()
return clone(obj)
function defineProp(object, key, descriptor = {}, copyFrom = {}) {
const { configurable: _configurable, writable: _writable }
= Object.getOwnPropertyDescriptor(object, key)
|| { configurable: true, writable: true }
const test = _configurable // Can redefine property
&& (_writable === undefined || _writable) // Can assign to property
if (!test || arguments.length <= 2) return test
const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
|| { configurable: true, writable: true } // Custom...
|| {}; // ...or left to native default settings
["get", "set", "value", "writable", "enumerable", "configurable"]
.forEach(attr =>
descriptor[attr] === undefined &&
(descriptor[attr] = basisDesc[attr])
)
const { get, set, value, writable, enumerable, configurable }
= descriptor
return Object.defineProperty(object, key, {
enumerable, configurable, ...get || set
? { get, set } // Accessor descriptor
: { value, writable } // Data descriptor
})
}
function clone(object) {
if (object !== Object(object)) return object /*
—— Check if the object belongs to a primitive data type */
if (object instanceof Node) return object.cloneNode(true) /*
—— Clone DOM trees */
let _object // The clone of object
switch (object.constructor) {
case Array:
case Object:
_object = cloneObject(object)
break
case Date:
_object = new Date(+object)
break
case Function:
const fnStr = String(object)
_object = new Function("return " +
(/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr)
? "function " : ""
) + fnStr
)()
copyPropDescs(_object, object)
break
case RegExp:
_object = new RegExp(object)
break
default:
switch (Object.prototype.toString.call(object.constructor)) {
// // Stem from:
case "[object Function]": // `class`
case "[object Undefined]": // `Object.create(null)`
_object = cloneObject(object)
break
default: // `Proxy`
_object = object
}
}
return _object
}
function cloneObject(object) {
if (seen.has(object)) return seen.get(object) /*
—— Handle recursive references (circular structures) */
const _object = Array.isArray(object)
? []
: Object.create(Object.getPrototypeOf(object)) /*
—— Assign [[Prototype]] for inheritance */
seen.set(object, _object) /*
—— Make `_object` the associative mirror of `object` */
Reflect.ownKeys(object).forEach(key =>
defineProp(_object, key, { value: clone(object[key]) }, object)
)
return _object
}
function copyPropDescs(target, source) {
Object.defineProperties(target,
Object.getOwnPropertyDescriptors(source)
)
}
}
// ------------------------
// Test properties
// ------------------------
console.log(` shallow deep func circ undefined date RegExp bigInt`)
log(A);
log(B);
log(C);
log(D);
log(E);
log(F);
log(G);
log(H);
log(I);
log(J);
log(K);
log(L);
log(M);
log(N);
log(O);
log(P);
log(Q);
log(R);
log(S);
log(T);
log(U);
console.log(` shallow deep func circ undefined date RegExp bigInt
----
LEGEND:
shallow - solution create shallow copy
deep - solution create deep copy
func - solution copy functions
circ - solution can copy object with circular references
undefined - solution copy fields with undefined value
date - solution can copy date
RegExp - solution can copy fields with regular expressions
bigInt - solution can copy BigInt
`)
// ------------------------
// Helper functions
// ------------------------
function deepCompare(obj1,obj2) {
return JSON.stringify(obj1)===JSON.stringify(obj2);
}
function getCase() { // pure data case
return {
undef: undefined,
bool: true, num: 1, str: "txt1",
e1: null, e2: [], e3: {}, e4: 0, e5: false,
arr: [ false, 2, "txt3", null, [], {},
[ true,4,"txt5",null, [], {}, [true,6,"txt7",null,[],{} ],
{bool: true,num: 8, str: "txt9", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
],
{bool: true,num: 10, str: "txt11", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
],
obj: {
bool: true, num: 12, str: "txt13",
e1: null, e2: [], e3: {}, e4: 0, e5: false,
arr: [true,14,"txt15",null,[],{} ],
obj: {
bool: true, num: 16, str: "txt17",
e1: null, e2: [], e3: {}, e4: 0, e5: false,
arr: [true,18,"txt19",null,[],{} ],
obj: {bool: true,num: 20, str: "txt21", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
}
}
};
}
function check(org, copy, field, newValue) {
copy[field] = newValue;
return deepCompare(org,copy);
}
function testFunc(f) {
let o = { a:1, fun: (i,j)=> i+j };
let c = f(o);
let val = false
try{
val = c.fun(3,4)==7;
} catch(e) { }
return val;
}
function testCirc(f) {
function Circ() {
this.me = this;
}
var o = {
x: 'a',
circ: new Circ(),
obj_circ: null,
};
o.obj_circ = o;
let val = false;
try{
let c = f(o);
val = (o.obj_circ == o) && (o.circ == o.circ.me);
} catch(e) { }
return val;
}
function testRegExp(f) {
let o = {
re: /a[0-9]+/,
};
let val = false;
try{
let c = f(o);
val = (String(c.re) == String(/a[0-9]+/));
} catch(e) { }
return val;
}
function testDate(f) {
let o = {
date: new Date(),
};
let val = false;
try{
let c = f(o);
val = (+new Date(c.date) == +new Date(o.date));
} catch(e) { }
return val;
}
function testBigInt(f) {
let val = false;
try{
let o = {
big: 123n,
};
let c = f(o);
val = o.big == c.big;
} catch(e) { }
return val;
}
function log(f) {
let o = getCase(); // orginal object
let oB = getCase(); // "backup" used for shallow valid test
let c1 = f(o); // copy 1 for reference
let c2 = f(o); // copy 2 for test shallow values
let c3 = f(o); // copy 3 for test deep values
let is_proper_copy = deepCompare(c1,o); // shoud be true
// shallow changes
let testShallow =
[ ['bool',false],['num',666],['str','xyz'],['arr',[]],['obj',{}] ]
.reduce((acc,curr)=> acc && check(c1,c2,curr[0], curr[1]), true );
// should be true (original object shoud not have changed shallow fields)
let is_valid = deepCompare(o,oB);
// deep test (intruduce some change)
if (c3.arr[6]) c3.arr[6][7].num = 777;
let diff_shallow = !testShallow; // shoud be true (shallow field was copied)
let diff_deep = !deepCompare(c1,c3); // shoud be true (deep field was copied)
let can_copy_functions = testFunc(f);
let can_copy_circular = testCirc(f);
let can_copy_regexp = testRegExp(f);
let can_copy_date = testDate(f);
let can_copy_bigInt = testBigInt(f);
let has_undefined = 'undef' in c1; // field with undefined value is copied?
let is_ok = is_valid && is_proper_copy;
let b=(bool) => (bool+'').padEnd(5,' '); // bool value to formated string
testFunc(f);
if(is_ok) {
console.log(`${f.name} ${b(diff_shallow)} ${b(diff_deep)} ${b(can_copy_functions)} ${b(can_copy_circular)} ${b(has_undefined)} ${b(can_copy_date)} ${b(can_copy_regexp)} ${b(can_copy_bigInt)}`)
} else {
console.log(`${f.name}: INVALID ${is_valid} ${is_proper_copy}`,{c1})
}
}
<script src="https://code.jquery.com/jquery-3.5.0.min.js" integrity="sha256-xNzN2a4ltkB44Mc/Jz3pT4iU1cmeR0FkXs4pru/JxaQ=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
This snippet only presents tested solutions and show differences between them (but it no make performence tests)
Below there are example results for Chrome for shallow-big object
-
What if the object contains window/globalThis or a reference to itself, or contains a Proxy? structuredClone - crashes if the object contains a Proxy, a window or a reference to itself. Everything else - crashes if the object contains window or a reference to itself.weroro– weroro2024年02月15日 07:07:49 +00:00Commented Feb 15, 2024 at 7:07
-
@weroro partial solution: for objects with circular references use THIS to serialise object to string and deserialise it (however I see some bugs with deserialisation for "windows" object - not all path-keys are proprely replaced in result... feel free to improve that solution). However - in that approach only data will be copied - not method/functions etc.Kamil Kiełczewski– Kamil Kiełczewski2024年02月15日 09:49:52 +00:00Commented Feb 15, 2024 at 9:49
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;
}
-
11This 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– A. Levy2009年04月08日 04:21:44 +00:00Commented Apr 8, 2009 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– james_womack2013年11月01日 20:36:32 +00:00Commented Nov 1, 2013 at 20:36
-
Upvoted for correctly calling the constructor using
new
. The accepted answer does not.GetFree– GetFree2015年06月14日 06:29:22 +00:00Commented Jun 14, 2015 at 6:29 -
works on node everything else ! still left reference linksuser956584– user9565842017年07月15日 22:38:52 +00:00Commented Jul 15, 2017 at 22:38
-
The recursive thought is great.But If the value is array,it will work?Q10Viking– Q10Viking2019年12月30日 11:32:18 +00:00Commented Dec 30, 2019 at 11:32
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.
-
3
Date.prototype.clone = function() {return new Date(+this)};
RobG– RobG2014年12月02日 12:20:43 +00:00Commented Dec 2, 2014 at 12:20
Using Lodash:
var y = _.clone(x, true);
-
6OMG it would be insane to reinvent cloning. This is the only sane answer.Dan Ross– Dan Ross2013年09月11日 08:48:59 +00:00Commented Sep 11, 2013 at 8:48
-
7I prefer
_.cloneDeep(x)
as it essentially is the same thing as above, but reads better.garbanzio– garbanzio2014年12月18日 19:40:17 +00:00Commented Dec 18, 2014 at 19:40
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/
-
12It does not deep clone the object.August– August2017年06月01日 05:31:46 +00:00Commented Jun 1, 2017 at 5:31
-
That's an assignment, not a copy. clone.Title = "just a clone" means that obj.Title = "just a clone".HoldOffHunger– HoldOffHunger2017年08月16日 16:05:02 +00:00Commented Aug 16, 2017 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– collapsar2017年09月01日 11:00:04 +00:00Commented Sep 1, 2017 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– HoldOffHunger2017年09月01日 11:40:11 +00:00Commented Sep 1, 2017 at 11:40
-
1@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– collapsar2017年09月01日 12:50:01 +00:00Commented Sep 1, 2017 at 12:50
Structured Cloning
2022 update: The structuredClone()
global function is already available in Node 17, Deno 1.14, and most major browsers (see Can I Use).
You can use the same structured clone mechanism that the HTML standard includes for sending data between realms.
const clone = structuredClone(original);
See the other answer for more details.
-
2+1 for giving an idea in what form it might become builtin, eventually—even if unusable right now.Beni Cherniavsky-Paskin– Beni Cherniavsky-Paskin2014年01月14日 06:14:26 +00:00Commented Jan 14, 2014 at 6:14
Interested in cloning simple objects:
JSON.parse(JSON.stringify(json_original));
Source : How to copy JavaScript object to new variable NOT by reference?
-
-
@MattH: this answer was already given in 2012. did you see it? Mohammed, did you check for existing answers before duplicating one of them?Dan Dascalescu– Dan Dascalescu2019年06月13日 23:40:03 +00:00Commented Jun 13, 2019 at 23:40
-
well thats one way. ty never thought of thatp3nGu1nZz– p3nGu1nZz2020年04月08日 21:53:12 +00:00Commented Apr 8, 2020 at 21:53
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.
-
How this is different from
let cloned = Object.assign({}, obj)
?ceztko– ceztko2019年05月21日 11:25:25 +00:00Commented May 21, 2019 at 11:25 -
@ceztko When
obj
is a class instance,Object.assign()
does not clone e.g. class methods (because they are not enumerable).flori– flori2021年01月02日 19:58:14 +00:00Commented Jan 2, 2021 at 19:58 -
Surprised more answers don't mention this. Without using
getPrototypeOf
, I couldn't call member functions on my shallow copied object.Inertial Ignorance– Inertial Ignorance2025年07月01日 20:50:08 +00:00Commented Jul 1 at 20:50
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();
};
}
-
1+1
Object.create(...)
seems definitely the way to go.René Nyffenegger– René Nyffenegger2014年06月30日 14:49:24 +00:00Commented Jun 30, 2014 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– froginvasion2014年08月09日 12:30:18 +00:00Commented Aug 9, 2014 at 12:30 -
Works well but what browsers does the polyfill work in?Ian Lunn– Ian Lunn2014年10月09日 13:25:01 +00:00Commented Oct 9, 2014 at 13:25
-
12This 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– Nick Desaulniers2014年10月31日 21:25:59 +00:00Commented Oct 31, 2014 at 21:25 -
2This does NOT create it "without references", it just moves the reference to the prototype. It's still a reference. If a property changes in the original so will the prototype property in the "clone". It's not a clone at all.Jimbo Jonny– Jimbo Jonny2016年05月09日 00:01:31 +00:00Commented May 9, 2016 at 0:01
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.
-
2it doesn't work when you have functions defined on objectsPetr Marek– Petr Marek2017年02月05日 22:06:54 +00:00Commented Feb 5, 2017 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– Oleh2017年04月04日 08:12:43 +00:00Commented Apr 4, 2017 at 8:12 -
@Oleh so use ` {... obj} instead of [...obj];`Manikant Gautam– Manikant Gautam2017年12月05日 06:17:40 +00:00Commented Dec 5, 2017 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– Oleh2017年12月06日 15:11:11 +00:00Commented Dec 6, 2017 at 15:11
I think there is a simple and working answer. In deep copying there are two concerns:
- Keep properties independent to each other.
- And keep the methods alive on cloned object.
So I think one simple solution will be to first serialize and deserialize and then do an assign on it to copy functions too.
let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);
Although this question has many answers, I hope this one helps too.
-
Although if I am permitted to import lodash, I prefer using lodash
cloneDeep
.ConductedClever– ConductedClever2019年01月13日 06:01:47 +00:00Commented Jan 13, 2019 at 6:01 -
2I'm using JSON.parse(JSON.stringify(source)). Always working.Misha– Misha2019年02月22日 15:50:24 +00:00Commented Feb 22, 2019 at 15:50
-
2@Misha, this way you will miss the functions. The term 'works' has many meanings.ConductedClever– ConductedClever2019年02月23日 05:26:59 +00:00Commented Feb 23, 2019 at 5:26
-
And keep in mind that, the way I have mentioned, only the functions of the first layer will be copied. So If we have some objects inside each other, then the only way is to copy field by field recursively.ConductedClever– ConductedClever2019年02月23日 05:29:43 +00:00Commented Feb 23, 2019 at 5:29
For a deep copy and clone, JSON.stringify then JSON.parse the object:
obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
-
pretty clever... any downsides to this approach?Aleks– Aleks2019年07月21日 01:56:13 +00:00Commented Jul 21, 2019 at 1:56
The most correct to copy object is use Object.create
:
Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
Such notation will make identically the same object with correct prototype and hidden properties.
-
yes but depends what you want (you might want to plain "copy" the prop values, not the actual prop descriptor refs...), and depending on the source obj you might need an
Object.assign
on top of this to get enumarable properties in as well (i.e plain old key-value pairs set on the object, unrelated to prototype and "dynamic" described props.Tchakabam– Tchakabam2021年06月04日 10:27:25 +00:00Commented Jun 4, 2021 at 10:27
(The following was mainly an integration of @Maciej Bukowski, @A. Levy, @Jan Turoň, @Redu's answers, and @LeviRoberts, @RobG's comments, many thanks to them!!!)
Deep copy? — YES! (mostly);
Shallow copy? — NO! (except Proxy
).
I sincerely welcome everyone to test clone()
.
In addition, defineProp()
is designed to easily and quickly (re)define or copy any type of descriptor.
Function
function clone(object) {
/*
Deep copy objects by value rather than by reference,
exception: `Proxy`
*/
const seen = new WeakMap()
return clone(object)
function clone(object) {
if (object !== Object(object)) return object /*
—— Check if the object belongs to a primitive data type */
if (object instanceof Node) return object.cloneNode(true) /*
—— Clone DOM trees */
let _object // The clone of object
switch (object.constructor) {
case Array:
case Object:
_object = cloneObject(object)
break
case Date:
_object = new Date(+object)
break
case Function:
_object = copyFn(object)
break
case RegExp:
_object = new RegExp(object)
break
default:
switch (Object.prototype.toString.call(object.constructor)) {
// // Stem from:
case "[object Function]":
switch (object[Symbol.toStringTag]) {
case undefined:
_object = cloneObject(object) // `class`
break
case "AsyncFunction":
case "GeneratorFunction":
case "AsyncGeneratorFunction":
_object = copyFn(object)
break
default:
_object = object
}
break
case "[object Undefined]": // `Object.create(null)`
_object = cloneObject(object)
break
default:
_object = object // `Proxy`
}
}
return _object
}
function cloneObject(object) {
if (seen.has(object)) return seen.get(object) /*
—— Handle recursive references (circular structures) */
const _object = Array.isArray(object)
? []
: Object.create(Object.getPrototypeOf(object)) /*
—— Assign [[Prototype]] for inheritance */
seen.set(object, _object) /*
—— Make `_object` the associative mirror of `object` */
Reflect.ownKeys(object).forEach(key =>
defineProp(_object, key, { value: clone(object[key]) }, object)
)
return _object
}
}
function copyPropDescs(target, source) {
Object.defineProperties(target,
Object.getOwnPropertyDescriptors(source)
)
}
function convertFnToStr(fn) {
let fnStr = String(fn)
if (fn.name.startsWith("[")) // isSymbolKey
fnStr = fnStr.replace(/\[Symbol\..+?\]/, '')
fnStr = /^(?!(async )?(function\b|[^{]+?=>))[^(]+?\(/.test(fnStr)
? fnStr.replace(/^(async )?(\*)?/, "1ドルfunction2ドル ") : fnStr
return fnStr
}
function copyFn(fn) {
const newFn = new Function(`return ${convertFnToStr(fn)}`)()
copyPropDescs(newFn, fn)
return newFn
}
function defineProp(object, key, descriptor = {}, copyFrom = {}) {
const { configurable: _configurable, writable: _writable }
= Object.getOwnPropertyDescriptor(object, key)
|| { configurable: true, writable: true }
const test = _configurable // Can redefine property
&& (_writable === undefined || _writable) // Can assign to property
if (!test || arguments.length <= 2) return test
const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
|| { configurable: true, writable: true } // Custom...
|| {}; // ...or left to native default settings
["get", "set", "value", "writable", "enumerable", "configurable"]
.forEach(attr =>
descriptor[attr] === undefined &&
(descriptor[attr] = basisDesc[attr])
)
const { get, set, value, writable, enumerable, configurable }
= descriptor
return Object.defineProperty(object, key, {
enumerable, configurable, ...get || set
? { get, set } // Accessor descriptor
: { value, writable } // Data descriptor
})
}
// Tests
const obj0 = {
u: undefined,
nul: null,
t: true,
num: 9,
str: "",
sym: Symbol("symbol"),
[Symbol("e")]: Math.E,
arr: [[0], [1, 2]],
d: new Date(),
re: /f/g,
get g() { return 0 },
o: {
n: 0,
o: { f: function (...args) { } }
},
f: {
getAccessorStr(object) {
return []
.concat(...
Object.values(Object.getOwnPropertyDescriptors(object))
.filter(desc => desc.writable === undefined)
.map(desc => Object.values(desc))
)
.filter(prop => typeof prop === "function")
.map(String)
},
f0: function f0() { },
f1: function () { },
f2: a => a / (a + 1),
f3: () => 0,
f4(params) { return param => param + params },
f5: (a, b) => ({ c = 0 } = {}) => a + b + c
}
}
defineProp(obj0, "s", { set(v) { this._s = v } })
defineProp(obj0.arr, "tint", { value: { is: "non-enumerable" } })
obj0.arr[0].name = "nested array"
let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", { set(v) { this._s = v + 1 } })
defineProp(obj1.re, "multiline", { value: true })
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Routinely")
console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()
console.log("obj0\n ",
".arr.tint:", obj0.arr.tint, "\n ",
".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
".arr.tint:", obj1.arr.tint, "\n ",
".arr[0].name:", obj1.arr[0].name
)
console.log()
console.log("Accessor-type descriptor\n ",
"of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
"of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
"set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
" → (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)
console.log("—— obj0 has not been interfered.")
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - More kinds of functions")
const fnsForTest = {
f(_) { return _ },
func: _ => _,
aFunc: async _ => _,
async function() { },
async asyncFunc() { },
aFn: async function () { },
*gen() { },
async *asyncGen() { },
aG1: async function* () { },
aG2: async function* gen() { },
*[Symbol.iterator]() { yield* Object.keys(this) }
}
console.log(Reflect.ownKeys(fnsForTest).map(k =>
`${String(k)}:
${fnsForTest[k].name}-->
${String(fnsForTest[k])}`
).join("\n"))
const normedFnsStr = `{
f: function f(_) { return _ },
func: _ => _,
aFunc: async _ => _,
function: async function() { },
asyncFunc: async function asyncFunc() { },
aFn: async function () { },
gen: function* gen() { },
asyncGen: async function* asyncGen() { },
aG1: async function* () { },
aG2: async function* gen() { },
[Symbol.iterator]: function* () { yield* Object.keys(this) }
}`
const copiedFnsForTest = clone(fnsForTest)
console.log("fnsForTest:", fnsForTest)
console.log("fnsForTest (copied):", copiedFnsForTest)
console.log("fnsForTest (normed str):", eval(`(${normedFnsStr})`))
console.log("Comparison of fnsForTest and its clone:",
Reflect.ownKeys(fnsForTest).map(k =>
[k, fnsForTest[k] === copiedFnsForTest[k]]
)
)
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Circular structures")
obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr
obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log("Clear obj0's recursion:",
obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
"obj0\n ",
".o.r:", obj0.o.r, "\n ",
".arr:", obj0.arr
)
console.log(
"obj1\n ",
".o.r:", obj1.o.r, "\n ",
".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Classes")
class Person {
constructor(name) {
this.name = name
}
}
class Boy extends Person { }
Boy.prototype.sex = "M"
const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }
const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"
boy0.name = "one"
boy1.name = "neo"
console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)
References
Object.create()
| MDNObject.defineProperties()
| MDN- Enumerability and ownership of properties | MDN
- TypeError: cyclic object value | MDN
Language tricks used
-
Since
Symbol("a") === Symbol("a")
isfalse
, shouldn’tclone(Symbol("a"))
useSymbol(object.description)
to create a new symbol? Or would this have too weird of an impact on well-known symbols?Sebastian Simon– Sebastian Simon2021年07月10日 15:39:57 +00:00Commented Jul 10, 2021 at 15:39 -
@SebastianSimon 👍 Your consideration is very comprehensive! And your last sentence is more correct, e.g.
(new Map)[Symbol.iterator]
vs(new Map)[Symbol(Symbol.iterator.description)]
.ooo– ooo2022年06月01日 05:31:16 +00:00Commented Jun 1, 2022 at 5:31
Explore related questions
See similar questions with these tags.
mObj=JSON.parse(JSON.stringify(jsonObject));
Object.create(o)
, it does everything the author asks?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...