Reputation: 40336
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?
Upvotes: 3801
Views: 2482060
Reputation: 1146
I saw plenty answers using for ( var i in o )
or Object.keys()
to extract an object property. Also, most of the solutions do a shallow clone of an object. Because of this I'll contribute my solution and thoughts here.
IMPORTANT:
for ( var i in o )
just extract the enumerable properties. Ignores all symbol-keys
.Object.keys()
just extract string-keys
that are enumerable in their property descriptor. Ignores all symbol-keys
.Object.entries()
just extract the same as Object.keys()
but return the result as a pair like [string-key, value]
.The complete solution that I actually do myself is the following:
function PerfectClone ( Subject, IncludePrototypes = false )
{
// For some odd reason `null` is an object that not strictly
// resolves into false (it's a falsy though).
if ( Subject === null ) return null;
// EDIT: this trick will allow to clone even primitives...
Subject = Object(Subject);
// Creating an empty clean and writable clone.
const Clone = Object.create(null);
// Filter the only types that can have properties to clone.
if ( ( typeof Subject == 'function' ) || ( typeof Subject == 'object' ) )
{
// Shallow clone the descriptors of `Subject` to `Clone`.
// Includes `number-keys`, `string-keys` and `symbol-keys`
// including non-enumerables and getters/setters too.
Object.defineProperties(Clone, Object.getOwnPropertyDescriptors(Subject));
}
// We must also perfectly clone the prototype chain...
let Prototype = Object.getPrototypeOf(Subject);
// Only objects that are not functions need the following...
if ( typeof Subject == 'object' )
{
// We need to extract the object constructor's prototype...
const ConstructorPrototype = Subject.constructor.prototype;
// Is the constructor's prototype and the subject's the same?
// Clone the constructor's prototype only if `IncludePrototypes`
// is truthy.
if ( IncludePrototypes || ! Object.is(ConstructorPrototype, Prototype) )
{
// Replace the prototype to attach with the cloned prototype.
Prototype = PerfectClone(Prototype, IncludePrototypes);
}
}
// When the prototype is properly parsed and evaluated, then
// we attach it to the clone...
Object.setPrototypeOf(Clone, Prototype);
// Return the completed clone.
return Clone;
}
// Example
//////////
class MyStuff
{
// These are enumerables by default.
A = 1;
B = 2;
// This is enumerable, but can't be called through (new MyStuff).C().
// Can be called if using `((new MyStuff).C)()`.
C = function Stuff () {};
// This is not enumerable by default.
D ()
{
return this;
}
// This is not enumerable by default...
[ Symbol('Anything') ] = 'not enumerable';
// Getters/Setters are actually not enumerable. So...
// This is not enumerable by default...
get Name ()
{
return MyStuff.name;
}
// This is not enumerable by default...
get Type ()
{
return this.constructor;
}
}
class MyExtendedStuff extends MyStuff
{
// This is not enumerable by default...
GetMyType ()
{
return this.constructor;
}
// This is not enumerable by default...
get Name ()
{
// This `super.Name` will fail if the prototype chain is
// not cloned properly.
return super.Name + '.' + MyExtendedStuff.name;
}
}
const MyEnhancedStuff = Object.create
(
new MyExtendedStuff,
{
// Symbolic keys are not enumerables by default.
[ Symbol('Enhanced') ]:
{
// So we purposely make it enumerable.
// Still not shown in default `console.log()` call.
enumerable: true,
get: () => MyEnhancedStuff,
},
// Making an enumerable method...
MyMethod:
{
enumerable: true,
value: function MyMethod ()
{
return this;
},
},
}
);
const MyClone = PerfectClone(MyEnhancedStuff, true);
let Subject = MyClone;
do
{
// Will not shown hidden keys (symbols, methods, getters/setters
// and any other not enumerable property)
console.log(Subject);
// Node.js debug will show hidden keys perfectly:
// console.log(( await import('node:util') ).inspect(Subject, true, 3, true));
}
while ( ( Subject = Object.getPrototypeOf(Subject) ) !== null )
NOTE: this will produce a full, complete, clone (not a deep clone) of any kind of object, corretly assigning properties in all the object prototype chain.
If you also clone the "class/function" prototypes, then the output in the console might look creepy, but it's fully functional.
ATENTION:
If you use PerfectClone
with any "class/function" prototype as the Subject
parameter it will be cloned regardless of the IncludePrototypes
parameter. If IncludePrototypes
parameter is falsy then just the prototype directly at the Subject
parameter will be cloned and any other lower prototype will remain in the chain as they are only if they're "class/function" prototype. Else they'll be cloned.
NOTE: It's actually not very interesting to clone "class/function" prototypes because they're globally available in any object from that "class/function". But there's some use cases that need this, so I'm leaving the feature here...
Upvotes: -2
Reputation: 4129
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;
}
});
}
Upvotes: 153
Reputation: 170
Simplest way to do is:
let newUser={...user};
Here, 'newUser' is a clone of the 'user' object.
Upvotes: 7
Reputation: 1349
To add
For comparing the result of
structuredClone() -- deep copy
& Object.assign() -- shallow copy
(&
Object.keys() -- shallow copy
)
-- to see which level of the object are copied
here is a demo test case -- with a depth of 4 lv::
@code-ex::
// lv4
class Material {
/** @type {String} */ name;
constructor(name) { this.name = name; } // prettier-ignore
}
// lv3
class Ball {
/** @type {String} */ name;
constructor(name) { this.name = name; } // prettier-ignore
/** @type {Material} */ material;
}
// lv2
class NodeX {
/** @type {String} */ name;
constructor(name) { this.name = name; } // prettier-ignore
/** @type {Ball} */ ball;
/** @type {NodeX} */ node_next;
}
// lv1
class ListX {
/** @type {NodeX} */ node_head;
/** @type {Number} */ length = 0;
}
const material_a = new Material('wood');
const material_b = new Material('stone');
const ball_a = new Ball('Ball_I');
const ball_b = new Ball('Ball_II');
ball_a.material = material_a;
ball_b.material = material_b;
const node_a = new NodeX('Alpha');
const node_b = new NodeX('Beta');
node_a.ball = ball_a;
node_b.ball = ball_b;
const list = new ListX();
list.node_head = node_a;
list.length++;
node_a.node_next = node_b;
list.length++;
// #>> structuredClone() -- deep copy
const list_clone = structuredClone(list); // https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object
test_DeepCopy_compare(list_clone, list);
// #>>#
function test_DeepCopy_compare(list_clone, list) {
expect(list_clone === list).toEqual(false); // the list is cloned - lv1
expect(list_clone.length === list.length).toEqual(true);
expect(list_clone.node_head === list.node_head).toEqual(false); // the node (list es property) is also cloned -- deep copy - lv2
const node_a_clone = list_clone.node_head;
expect(node_a_clone === node_a).toEqual(false); // ~rp
expect(node_a_clone.name === node_a.name).toEqual(true);
expect(node_a_clone.node_next === node_a.node_next).toEqual(false); // the node_next (node es property) is also cloned -- deep copy - lv3
const node_b_clone = node_a_clone.node_next;
expect(node_b_clone === node_b).toEqual(false); // ~rp
expect(node_b_clone.name === node_b.name).toEqual(true);
expect(node_b_clone.node_next === undefined && node_b.node_next === undefined).toEqual(true);
expect(node_a_clone.ball === node_a.ball).toEqual(false); // aga, the ball (node es property) is also cloned -- deep copy - lv3
expect(node_b_clone.ball === node_b.ball).toEqual(false);
const ball_a_clone = node_a_clone.ball;
const ball_b_clone = node_b_clone.ball;
expect(ball_a_clone.material === ball_a.material).toEqual(false); // aga, the material (ball es property) is also cloned -- deep copy - lv4
expect(ball_b_clone.material === ball_b.material).toEqual(false);
}
// #>> Object.assign() -- shallow copy
const list_clone_ScOa = Object.create(Object.getPrototypeOf(list)); // const list_clone_ScOa = {}; // https://stackoverflow.com/a/43935938/9549068
Object.assign(list_clone_ScOa, list); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#return_value
test_ShallowCopy_compare(list_clone_ScOa, list); // Object.assign() & Object.keys() produce same test result
// #>> Object.keys() -- shallow copy
function shallowCopy_ObjectKeys(obj) {
const obj_clone = Object.create(Object.getPrototypeOf(obj)); // const obj_clone = {};
for (const propName of Object.keys(obj)) {
obj_clone[propName] = obj[propName];
}
return obj_clone;
}
const list_clone_ScOk = shallowCopy_ObjectKeys(list);
test_ShallowCopy_compare(list_clone_ScOk, list);
// #>>#
function test_ShallowCopy_compare(list_clone_ScOkOa, list) {
expect(list_clone_ScOkOa === list).toEqual(false); // the list is cloned - lv1
expect(list_clone_ScOkOa.length === list.length).toEqual(true);
expect(list_clone_ScOkOa.node_head === list.node_head).toEqual(true); // the node (list es property) is NOT cloned -- shallow copy - lv2
const node_a_clone_ScOkOa = list_clone_ScOkOa.node_head;
expect(node_a_clone_ScOkOa === node_a).toEqual(true); // ~rp
expect(node_a_clone_ScOkOa.name === node_a.name).toEqual(true);
expect(node_a_clone_ScOkOa.node_next === node_a.node_next).toEqual(true); // the node_next (node es property) is NOT cloned -- shallow copy - lv3
const node_b_clone_ScOk = node_a_clone_ScOkOa.node_next;
expect(node_b_clone_ScOk === node_b).toEqual(true); // ~rp
expect(node_a_clone_ScOkOa.ball === node_a.ball).toEqual(true); // aga, the ball (node es property) is NOT cloned -- shallow copy - lv3
expect(node_b_clone_ScOk.ball === node_b.ball).toEqual(true);
expect(list_clone_ScOkOa instanceof ListX).toEqual(true);
}
@comment::
for Es6 class, seems private / static members
are not copied.
maybe build yourself a own clone()
method for your Es6 class is the best way
For more, see: // https://stackoverflow.com/a/57546221/9549068 // https://stackoverflow.com/a/65024713/9549068
Upvotes: 0
Reputation: 41
I found a way to clone object with functions (break multiple lines to easier understand):
const clone = Object.assign(
Object.create(
Object.getPrototypeOf(originalObject)
),
dataObject
);
Upvotes: 0
Reputation: 30566
There's a new JS standard called structured cloning. It works in many browsers (see Can I Use).
const clone = structuredClone(object);
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.
Upvotes: 2047
Reputation: 671
I've had an issue when copying objects. This is because when you do following, you're only making a 'reference' to the object and when the source object value is updated later, the copy object that was cloned also changes value because it was merely a 'reference' and hence you see multiple values of the last changes to the source object.
let x = { a: 1 };
let y = x; // y is a reference to x, so if x changes y also changes and v/v
So, to tackle this issue you do the following:
let y = JSON.parse(JSON.stringify(x)); //see Note below
The other way to prevent references is by doing the following:
let x = { a: 1 };
let y = Object.assign({}, x); // Object.assign(target, ...sources)
y.a = 2;
console.log(x); // { a: 1 }
console.log(y); // { a: 2 }
Upvotes: 9
Reputation: 6524
The different
Only copy top level: {...object}
and Object.assign({}, object)
let objA = {
a: "keyA",
b: {
c: "keyC",
}
}
let objB = Object.assign({}, objA); // or {...objB}
// change objB
objB.a = "Change objA.a (top)"
console.log("objA.a (top) No Change:\n" + JSON.stringify(objA, false, 2));
objB.b.c = "change should be only for objB.b.c but it in objA.b.c"
console.log("objA.a.c second level has Change:\n" + JSON.stringify(objA, false, 2));
for deep copy use structuredClone()
2022 or JSON.parse(JSON.stringify(object))
for old browser, easy without hack.
let objA = {
a: "keyA",
b: {
c: "keyC",
}
}
let objB = typeof structuredClone == 'function' ?
structuredClone(objA) : JSON.parse(JSON.stringify(objA));
// change objB
objB.a = "Change objA.a (top)"
objB.b.c = "change should be only for objB.c but it in objA.c"
console.log("objA has no Change:\n" + JSON.stringify(objA, false, 2));
Upvotes: 2
Reputation: 129715
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.
Upvotes: 21
Reputation: 17936
Using the spread syntax performs a shallow copy of the object. This means that none of the nested object instances are cloned as you can see in the following example with the nested object child
const user1 = {
name: 'Alex',
address: '15th Park Avenue',
age: 43,
child:{
name: 'John'
}
}
const user2 = {...user1};
user1.child.name = 'chris';
console.log(user1);
console.log(user2);
To solve this nested object problem and perform a deep copy we can use JSON.parse(JSON.stringify(someObject))
const user1 = {
name: 'Alex',
address: '15th Park Avenue',
age: 43,
child:{
name: 'John'
}
}
const user2 = JSON.parse(JSON.stringify(user1));
user1.child.name = 'chris';
console.log(user1);
console.log(user2);
Upvotes: 2
Reputation: 2033
Short and sweet:
let clone = Object.fromEntries(Object.entries(obj));
Demo:
let obj = {a: 'b'};
let clone = Object.fromEntries(Object.entries(obj));
clone.a = 'c';
console.log(obj, clone);
Upvotes: 1
Reputation: 22304
Native JS:
const shallowClone = {...originalObj};
const deepClone = JSON.parse(JSON.stringify(originalObj));
Using Libraries:
// Lodash
const shallowClone = _.clone(originalObj);
const deepClone = _. cloneDeep(originalObj);
// JQuery
const shallowClone = jQuery.extend({}, originalObj);
const deepClone = jQuery.extend(true, {}, originalObj);
// Angular
const deepClone = angular.copy(originalObj);
Upvotes: 6
Reputation: 24886
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;
}
Upvotes: 27
Reputation: 5537
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.
Upvotes: 62
Reputation: 2731
const objClone = { ...obj };
Be aware that nested objects are still copied as a reference.
Upvotes: 41
Reputation: 641
I have gone through all above solutions and they are quite well. However, there is another approach that you can use to clone object (with values not reference). Object.assign
let x = {
a: '1',
b: '2'
}
let y = Object.assign({}, x)
y.a = "3"
console.log(x)
The output will be
{ a: '1', b: '2' }
Moreover, you can also clone array with the same approach.
clonedArray = Object.assign([], array)
Upvotes: 3
Reputation: 598
This makes new copy of your obj
(not just reference).
let myCopy = JSON.parse(JSON.stringify(obj));
..Works much efficiently then the _.cloneDeep(obj)
.
Upvotes: 1
Reputation: 261
(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 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 )?(\*)?/, "$1function$2 ") : 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
})
}
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)
)
Object.create()
| MDNObject.defineProperties()
| MDNUpvotes: 11
Reputation: 53
You can use rest operator to clone arrays or objects
let myObj = {1: 100, 'a': 200};
let clone = {...myObj};
clone.a = 300;
console.log(clone.a) // Output :- 300
console.log(myObj.a) // Output :- 200
Upvotes: 1
Reputation: 1798
Ways to Copy Objects in JavaScript
...
) syntaxObject.assign()
methodJSON.stringify()
and JSON.parse()
methodsconst person = {
firstName: 'John',
lastName: 'Doe'
};
// using spread ...
let p1 = {
...person
};
// using Object.assign() method
let p2 = Object.assign({}, person);
// using JSON
let p3 = JSON.parse(JSON.stringify(person));
Upvotes: 2
Reputation: 1086
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.
Upvotes: 10
Reputation: 114400
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.
Upvotes: 850
Reputation: 92347
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.
{...obj}
(A) is fastest on chrome and firefox and medium fast on safariObject.assign
(B) is fast on all browsersJSON.parse/stringify
(K) is quite slowJSON.parse/stringify
(K) is quite slowFor 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
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
Upvotes: 27
Reputation: 730
Just as this link says use this code:
let clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj));
Upvotes: 4
Reputation: 4619
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;
};
Upvotes: 30
Reputation: 147
The solution JSON.parse(JSON.stringify(orig_obj)
as stated by many peers here for deep_cloning has several issues which I found, and they are listed below:
undefined
in the original object,Infinity
, NaN
etc, they will be converted into null
while copying,Date
type in the original object, it will be stringified in the cloned object (typeof date_entry --> string
). Found an effective way for cloning an object, and it worked well for me in all sort of scenarios. Please have a look at below code, as it has resolved all above mentioned pitfalls of JSON.parse(...)
, yet resulting in proper deep-cloning:
var orig_obj = {
string: 'my_str',
number: 123,
bool: false,
nul: null,
nested : {
value : true
},
nan : NaN,
date: new Date(),
undef: undefined,
inf: Infinity,
}
console.log("original_obj before modification: ", orig_obj, "\n");
console.log(typeof orig_obj.date, "\n");
var clone_obj = Object.assign({}, orig_obj);
//this below loop will help in deep cloning and solving above issues
for(let prop in orig_obj) {
if(typeof orig_obj[prop] === "object") {
if(orig_obj[prop] instanceof Date)
clone_obj[prop] = orig_obj[prop];
else {
clone_obj[prop] = JSON.parse(JSON.stringify(orig_obj[prop]));
}
}
}
console.log("cloned_obj before modification: ", orig_obj, "\n");
clone_obj.bool = true;
clone_obj.nested.value = "false";
console.log("original_obj post modification: ", orig_obj, "\n");
console.log("cloned_obj post modification: ", clone_obj, "\n");
console.log(typeof clone_obj.date);
Upvotes: 3
Reputation: 159
Simple recursive method to clone an object. Also could use lodash.clone.
let clone = (obj) => {
let obj2 = Array.isArray(obj) ? [] : {};
for(let k in obj) {
obj2[k] = (typeof obj[k] === 'object' ) ? clone(obj[k]) : obj[k];
}
return obj2;
}
let w = { name: "Apple", types: ["Fuji", "Gala"]};
let x = clone(w);
w.name = "Orange";
w.types = ["Navel"];
console.log(x);
console.log(w);
Upvotes: 5
Reputation: 19464
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.
Upvotes: 1174
Reputation: 1623
var x = {'e': 2, 'd': 8, 'b': 5};
const y = {};
for(let key in x) {
y[key] = x[key];
}
console.log(y); // =>>> {e: 2, d: 8, b: 5}
const z = {};
Object.keys(x).forEach(key => {
z[key] = x[key];
});
console.log(z); // =>>> {e: 2, d: 8, b: 5}
const w = {};
for(let i = 0; i < Object.keys(x).length; i++) {
w[Object.keys(x)[i]] = x[Object.keys(x)[i]];
}
console.log(w); // =>>> {e: 2, d: 8, b: 5}
const v = {};
for(let key of Object.keys(x)) {
v[key] = x[key];
}
console.log(v); // =>>> {e: 2, d: 8, b: 5}
x['q'] = 100; // Altering x will not affect the other objects
console.log(x); // =>>> {e: 2, d: 8, b: 5, q: 100}
console.log(y); // =>>> {e: 2, d: 8, b: 5}
console.log(z); // =>>> {e: 2, d: 8, b: 5}
console.log(w); // =>>> {e: 2, d: 8, b: 5}
console.log(v); // =>>> {e: 2, d: 8, b: 5}
Upvotes: 1
Reputation: 355
Object copy using ( ... )
//bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2,c: 3 }
//good
const originalObj = { id: 5, name: 'San Francisco'};
const copyObject = {...originalObj, pincode: 4444};
console.log(copyObject) //{ id: 5, name: 'San Francisco', pincode: 4444 }
Same can be use for copying array from one to other
const itemsCopy = [...items];
Upvotes: 5