Create a non-mutable recursive 'map' of an object [duplicate] - javascript

I am trying to create a deep copy map method for my Redux project that will work with objects rather than arrays. I read that in Redux each state should not change anything in the previous states.
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, {...object[key]});
return output;
},
{});
}
It works:
return mapCopy(state, e => {
if (e.id === action.id) {
e.title = 'new item';
}
return e;
})
However it does not deep copy inner items so I need to tweak it to:
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
let newObject = {...object[key]};
newObject.style = {...newObject.style};
newObject.data = {...newObject.data};
output[key] = callback.call(this, newObject);
return output;
}, {});
}
This is less elegant as it requires to know which objects are passed.
Is there a way in ES6 to use the spread syntax to deep copy an object?

Use JSON for deep copy
var newObject = JSON.parse(JSON.stringify(oldObject))
var oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
var newObject = JSON.parse(JSON.stringify(oldObject));
newObject.address.city = 'Delhi';
console.log('newObject');
console.log(newObject);
console.log('oldObject');
console.log(oldObject);

No such functionality is built-in to ES6. I think you have a couple of options depending on what you want to do.
If you really want to deep copy:
Use a library. For example, lodash has a cloneDeep method.
Implement your own cloning function.
Alternative Solution To Your Specific Problem (No Deep Copy)
However, I think, if you're willing to change a couple things, you can save yourself some work. I'm assuming you control all call sites to your function.
Specify that all callbacks passed to mapCopy must return new objects instead of mutating the existing object. For example:
mapCopy(state, e => {
if (e.id === action.id) {
return Object.assign({}, e, {
title: 'new item'
});
} else {
return e;
}
});
This makes use of Object.assign to create a new object, sets properties of e on that new object, then sets a new title on that new object. This means you never mutate existing objects and only create new ones when necessary.
mapCopy can be really simple now:
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, object[key]);
return output;
}, {});
}
Essentially, mapCopy is trusting its callers to do the right thing. This is why I said this assumes you control all call sites.

From MDN
Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).
Personally, I suggest using Lodash's cloneDeep function for multi-level object/array cloning.
Here is a working example:
const arr1 = [{ 'a': 1 }];
const arr2 = [...arr1];
const arr3 = _.clone(arr1);
const arr4 = arr1.slice();
const arr5 = _.cloneDeep(arr1);
const arr6 = [...{...arr1}]; // a bit ugly syntax but it is working!
// first level
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // false
console.log(arr1 === arr4); // false
console.log(arr1 === arr5); // false
console.log(arr1 === arr6); // false
// second level
console.log(arr1[0] === arr2[0]); // true
console.log(arr1[0] === arr3[0]); // true
console.log(arr1[0] === arr4[0]); // true
console.log(arr1[0] === arr5[0]); // false
console.log(arr1[0] === arr6[0]); // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

I often use this:
function deepCopy(obj) {
if(typeof obj !== 'object' || obj === null) {
return obj;
}
if(obj instanceof Date) {
return new Date(obj.getTime());
}
if(obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepCopy(item);
return arr;
}, []);
}
if(obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepCopy(obj[key]);
return newObj;
}, {})
}
}

You can use structuredClone() like the following:
const myOriginal = {
title: "Full Stack JavaScript Developer",
info: {
firstname: "Abolfazl",
surname: "Roshanzamir",
age: 34
}
};
const myDeepCopy = structuredClone(myOriginal);
structuredClone()
You can use structuredClone() that is a built-in function for deep-copying.
Structured cloning addresses many (although not all) shortcomings of the JSON.stringify() technique.
Structured cloning can handle cyclical data structures,
support many built-in data types, and is generally more robust and often faster.
However, it still has some limitations that may catch you off-guard:
1-Prototypes : If you use structuredClone() with a class instance,
you’ll get a plain object as the return value, as structured cloning discards the object’s prototype chain.
2-Functions: If your object contains functions, they will be quietly discarded.
3- Non-cloneables: Some values are not structured cloneable, most notably Error and DOM nodes. It will cause structuredClone() to throw.
const myDeepCopy = structuredClone(myOriginal);
JSON.stringify
If you simply want to deep copy the object to another object,
all you will need to do is JSON.stringify the object and parse it using JSON.parse afterward.
This will essentially perform deep copying of the object.
let user1 = {
name: 'Abolfazl Roshanzamir',
age: 34,
university: {
name: 'Shiraz Bahonar University'
}
};
let user2 = JSON.parse(JSON.stringify(user1));
user2.name = 'Andy Madadian';
user2.university.name = 'Kerman Bahonar University'
console.log(user2);
// { name: 'Andy Madadian', age: 33, university: { name: 'Kerman Bahonar University' } }
console.log(user1);
// { name: 'Abolfazl Roshanzamir', age: 33, university: { name: 'Shiraz Bahonar University' } }
Spread operator / Object.assign()
One way to create a shallow copy in JavaScript using the object spread operator ... or Object.assign() like the following:
const myShallowCopySpread = {...myOriginal};
const myShallowCopyObjectAssign=Object.assign({},obj)
Performance
When it comes to performance the creator Surma has pointed out that JSON.Parse() can be a bit faster for small objects. But when you have a large object, complex object
structuredClone() starts to get significantly faster.
Browser support is pretty fantastic And even is supported by Node.js.

const a = {
foods: {
dinner: 'Pasta'
}
}
let b = JSON.parse(JSON.stringify(a))
b.foods.dinner = 'Soup'
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Pasta
Using JSON.stringify and JSON.parse is the best way. Because by using the spread operator we will not get the efficient answer when the json object contains another object inside it. we need to manually specify that.

Here's my deep copy algorithm.
const DeepClone = (obj) => {
if(obj===null||typeof(obj)!=='object')return null;
let newObj = { ...obj };
for (let prop in obj) {
if (
typeof obj[prop] === "object" ||
typeof obj[prop] === "function"
) {
newObj[prop] = DeepClone(obj[prop]);
}
}
return newObj;
};

// use: clone( <thing to copy> ) returns <new copy>
// untested use at own risk
function clone(o, m){
// return non object values
if('object' !==typeof o) return o
// m: a map of old refs to new object refs to stop recursion
if('object' !==typeof m || null ===m) m =new WeakMap()
var n =m.get(o)
if('undefined' !==typeof n) return n
// shallow/leaf clone object
var c =Object.getPrototypeOf(o).constructor
// TODO: specialize copies for expected built in types i.e. Date etc
switch(c) {
// shouldn't be copied, keep reference
case Boolean:
case Error:
case Function:
case Number:
case Promise:
case String:
case Symbol:
case WeakMap:
case WeakSet:
n =o
break;
// array like/collection objects
case Array:
m.set(o, n =o.slice(0))
// recursive copy for child objects
n.forEach(function(v,i){
if('object' ===typeof v) n[i] =clone(v, m)
});
break;
case ArrayBuffer:
m.set(o, n =o.slice(0))
break;
case DataView:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.byteLength))
break;
case Map:
case Set:
m.set(o, n =new (c)(clone(Array.from(o.entries()), m)))
break;
case Int8Array:
case Uint8Array:
case Uint8ClampedArray:
case Int16Array:
case Uint16Array:
case Int32Array:
case Uint32Array:
case Float32Array:
case Float64Array:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.length))
break;
// use built in copy constructor
case Date:
case RegExp:
m.set(o, n =new (c)(o))
break;
// fallback generic object copy
default:
m.set(o, n =Object.assign(new (c)(), o))
// recursive copy for child objects
for(c in n) if('object' ===typeof n[c]) n[c] =clone(n[c], m)
}
return n
}

Here is the deepClone function which handles all primitive, array, object, function data types
function deepClone(obj){
if(Array.isArray(obj)){
var arr = [];
for (var i = 0; i < obj.length; i++) {
arr[i] = deepClone(obj[i]);
}
return arr;
}
if(typeof(obj) == "object"){
var cloned = {};
for(let key in obj){
cloned[key] = deepClone(obj[key])
}
return cloned;
}
return obj;
}
console.log( deepClone(1) )
console.log( deepClone('abc') )
console.log( deepClone([1,2]) )
console.log( deepClone({a: 'abc', b: 'def'}) )
console.log( deepClone({
a: 'a',
num: 123,
func: function(){'hello'},
arr: [[1,2,3,[4,5]], 'def'],
obj: {
one: {
two: {
three: 3
}
}
}
}) )

function deepclone(obj) {
let newObj = {};
if (typeof obj === 'object') {
for (let key in obj) {
let property = obj[key],
type = typeof property;
switch (type) {
case 'object':
if( Object.prototype.toString.call( property ) === '[object Array]' ) {
newObj[key] = [];
for (let item of property) {
newObj[key].push(this.deepclone(item))
}
} else {
newObj[key] = deepclone(property);
}
break;
default:
newObj[key] = property;
break;
}
}
return newObj
} else {
return obj;
}
}

const cloneData = (dataArray) => {
newData= []
dataArray.forEach((value) => {
newData.push({...value})
})
return newData
}
a = [{name:"siva"}, {name:"siva1"}] ;
b = myCopy(a)
b === a // false`

I myself landed on these answers last day, trying to find a way to deep copy complex structures, which may include recursive links. As I wasn't satisfied with anything being suggested before, I implemented this wheel myself. And it works quite well. Hope it helps someone.
Example usage:
OriginalStruct.deep_copy = deep_copy; // attach the function as a method
TheClone = OriginalStruct.deep_copy();
Please look at https://github.com/latitov/JS_DeepCopy for live examples how to use it, and also deep_print() is there.
If you need it quick, right here's the source of deep_copy() function:
function deep_copy() {
'use strict'; // required for undef test of 'this' below
// Copyright (c) 2019, Leonid Titov, Mentions Highly Appreciated.
var id_cnt = 1;
var all_old_objects = {};
var all_new_objects = {};
var root_obj = this;
if (root_obj === undefined) {
console.log(`deep_copy() error: wrong call context`);
return;
}
var new_obj = copy_obj(root_obj);
for (var id in all_old_objects) {
delete all_old_objects[id].__temp_id;
}
return new_obj;
//
function copy_obj(o) {
var new_obj = {};
if (o.__temp_id === undefined) {
o.__temp_id = id_cnt;
all_old_objects[id_cnt] = o;
all_new_objects[id_cnt] = new_obj;
id_cnt ++;
for (var prop in o) {
if (o[prop] instanceof Array) {
new_obj[prop] = copy_array(o[prop]);
}
else if (o[prop] instanceof Object) {
new_obj[prop] = copy_obj(o[prop]);
}
else if (prop === '__temp_id') {
continue;
}
else {
new_obj[prop] = o[prop];
}
}
}
else {
new_obj = all_new_objects[o.__temp_id];
}
return new_obj;
}
function copy_array(a) {
var new_array = [];
if (a.__temp_id === undefined) {
a.__temp_id = id_cnt;
all_old_objects[id_cnt] = a;
all_new_objects[id_cnt] = new_array;
id_cnt ++;
a.forEach((v,i) => {
if (v instanceof Array) {
new_array[i] = copy_array(v);
}
else if (v instanceof Object) {
new_array[i] = copy_object(v);
}
else {
new_array[i] = v;
}
});
}
else {
new_array = all_new_objects[a.__temp_id];
}
return new_array;
}
}
Cheers#!

I would suggest using the spread operator. You'll need to spread a second time if you need to update the second level. Attempting to update the newObject using something like newObject.address.city will throw an error if address did not already exist in oldObject.
const oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
const newObject = {
...oldObject,
address: {
...oldObject.address,
city: 'Delhi'
}
}
console.log(newObject)

This is a very old question but I think in 2022 there are many ways to solve this. However, if you want a simple, fast and vanilla JS solution check this out:
const cloner = (o) => {
let idx = 1
const isArray = (a) => a instanceof Array
const isObject = (o) => o instanceof Object
const isUndefined = (a) => a === undefined
const process = v => {
if (isArray(v)) return cloneArray(v)
else if (isObject(v)) return cloneObject(v)
else return v
}
const register = (old, o) => {
old.__idx = idx
oldObjects[idx] = old
newObjects[idx] = o
idx++
}
const cloneObject = o => {
if (!isUndefined(o.__idx)) return newObjects[o.__idx]
const obj = {}
for (const prop in o) {
if (prop === '__idx') continue
obj[prop] = process(o[prop])
}
register(o, obj)
return obj
}
const cloneArray = a => {
if (!isUndefined(a.__idx)) return newObjects[a.__idx]
const arr = a.map((v) => process(v))
register(a, arr)
return arr
}
const oldObjects = {}
const newObjects = {}
let tmp
if (isArray(o)) tmp = cloneArray(o)
else if (isObject(o)) tmp = cloneObject(o)
else return o
for (const id in oldObjects) delete oldObjects[id].__idx
return tmp
}
const c = {
id: 123,
label: "Lala",
values: ['char', 1, {flag: true}, [1,2,3,4,5], ['a', 'b']],
name: undefined
}
const d = cloner(c)
d.name = "Super"
d.values[2].flag = false
d.values[3] = [6,7,8]
console.log({ c, d })
It's recursive and self-contained, all the functions needed are defined in the function cloner().
In this snippet we are handling Array and Object types if you want to add more handlers you can add specify handlers like Date and clone it like new Date(v.getTime())
For me Array and Object are the types that I use the most in my implementations.

Related

Trying to change properties in copied javascript array [duplicate]

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?
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.
If you do not use Dates, 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.
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.
An elegant way to clone a Javascript object in one line of code
An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need.
var clone = Object.assign({}, obj);
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.
Read more...
The polyfill to support older browsers:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
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.
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.
jsfiddle
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);
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
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.
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
You can simply use a spread property to copy an object without references. But be careful (see comments), the 'copy' is just on the lowest object/array level. Nested properties are still references!
Complete clone:
let x = {a: 'value1'}
let x2 = {...x}
// => mutate without references:
x2.a = 'value2'
console.log(x.a) // => 'value1'
Clone with references on second level:
const y = {a: {b: 'value3'}}
const y2 = {...y}
// => nested object is still a references:
y2.a.b = 'value4'
console.log(y.a.b) // => 'value4'
JavaScript actually does not support deep clones natively. Use an utility function. For example Ramda:
http://ramdajs.com/docs/#clone
const objClone = { ...obj };
Be aware that nested objects are still copied as a reference.
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;
};
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...
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;
}
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.
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/lodash#4.17.15/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
Using Lodash:
var y = _.clone(x, true);
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/
Interested in cloning simple objects:
JSON.parse(JSON.stringify(json_original));
Source : How to copy JavaScript object to new variable NOT by reference?
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.
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.
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();
};
}
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.
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.
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}}
(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 )?(\*)?/, "$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
})
}
// 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() | MDN
Object.defineProperties() | MDN
Enumerability and ownership of properties | MDN
TypeError: cyclic object value | MDN
Language tricks used
Conditionally add prop to object
Use lodash _.cloneDeep().
Shallow Copy: lodash _.clone()
A shallow copy can be made by simply copying the reference.
let obj1 = {
a: 0,
b: {
c: 0,
e: {
f: 0
}
}
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;
console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}
console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}
Deep Copy: lodash _.cloneDeep()
fields are dereferenced: rather than references to objects being copied
let obj1 = {
a: 0,
b: {
c: 0,
e: {
f: 0
}
}
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;
console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}
console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}

How can I pass by value a nested Javascript object? [duplicate]

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?
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.
If you do not use Dates, 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.
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.
An elegant way to clone a Javascript object in one line of code
An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need.
var clone = Object.assign({}, obj);
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.
Read more...
The polyfill to support older browsers:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
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.
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.
jsfiddle
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);
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
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.
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
You can simply use a spread property to copy an object without references. But be careful (see comments), the 'copy' is just on the lowest object/array level. Nested properties are still references!
Complete clone:
let x = {a: 'value1'}
let x2 = {...x}
// => mutate without references:
x2.a = 'value2'
console.log(x.a) // => 'value1'
Clone with references on second level:
const y = {a: {b: 'value3'}}
const y2 = {...y}
// => nested object is still a references:
y2.a.b = 'value4'
console.log(y.a.b) // => 'value4'
JavaScript actually does not support deep clones natively. Use an utility function. For example Ramda:
http://ramdajs.com/docs/#clone
const objClone = { ...obj };
Be aware that nested objects are still copied as a reference.
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;
};
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...
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;
}
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.
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/lodash#4.17.15/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
Using Lodash:
var y = _.clone(x, true);
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/
Interested in cloning simple objects:
JSON.parse(JSON.stringify(json_original));
Source : How to copy JavaScript object to new variable NOT by reference?
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.
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.
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();
};
}
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.
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.
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}}
(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 )?(\*)?/, "$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
})
}
// 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() | MDN
Object.defineProperties() | MDN
Enumerability and ownership of properties | MDN
TypeError: cyclic object value | MDN
Language tricks used
Conditionally add prop to object
Use lodash _.cloneDeep().
Shallow Copy: lodash _.clone()
A shallow copy can be made by simply copying the reference.
let obj1 = {
a: 0,
b: {
c: 0,
e: {
f: 0
}
}
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;
console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}
console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}
Deep Copy: lodash _.cloneDeep()
fields are dereferenced: rather than references to objects being copied
let obj1 = {
a: 0,
b: {
c: 0,
e: {
f: 0
}
}
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;
console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}
console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}

javascript: Strange behavior when setting null for value of one object [duplicate]

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?
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.
If you do not use Dates, 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.
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.
An elegant way to clone a Javascript object in one line of code
An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need.
var clone = Object.assign({}, obj);
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.
Read more...
The polyfill to support older browsers:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
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.
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.
jsfiddle
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);
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
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.
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
You can simply use a spread property to copy an object without references. But be careful (see comments), the 'copy' is just on the lowest object/array level. Nested properties are still references!
Complete clone:
let x = {a: 'value1'}
let x2 = {...x}
// => mutate without references:
x2.a = 'value2'
console.log(x.a) // => 'value1'
Clone with references on second level:
const y = {a: {b: 'value3'}}
const y2 = {...y}
// => nested object is still a references:
y2.a.b = 'value4'
console.log(y.a.b) // => 'value4'
JavaScript actually does not support deep clones natively. Use an utility function. For example Ramda:
http://ramdajs.com/docs/#clone
const objClone = { ...obj };
Be aware that nested objects are still copied as a reference.
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;
};
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...
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;
}
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.
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/lodash#4.17.15/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
Using Lodash:
var y = _.clone(x, true);
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/
Interested in cloning simple objects:
JSON.parse(JSON.stringify(json_original));
Source : How to copy JavaScript object to new variable NOT by reference?
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.
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.
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();
};
}
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.
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.
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}}
(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 )?(\*)?/, "$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
})
}
// 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() | MDN
Object.defineProperties() | MDN
Enumerability and ownership of properties | MDN
TypeError: cyclic object value | MDN
Language tricks used
Conditionally add prop to object
Use lodash _.cloneDeep().
Shallow Copy: lodash _.clone()
A shallow copy can be made by simply copying the reference.
let obj1 = {
a: 0,
b: {
c: 0,
e: {
f: 0
}
}
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;
console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}
console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}
Deep Copy: lodash _.cloneDeep()
fields are dereferenced: rather than references to objects being copied
let obj1 = {
a: 0,
b: {
c: 0,
e: {
f: 0
}
}
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;
console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}
console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}

Defaultdict equivalent in javascript

In python you can have a defaultdict(int) which stores int as values. And if you try to do a 'get' on a key which is not present in the dictionary you get zero as default value.
Can you do the same in javascript/jquery
You can build one using a JavaScript Proxy
var defaultDict = new Proxy({}, {
get: (target, name) => name in target ? target[name] : 0
})
This lets you use the same syntax as normal objects when accessing properties.
defaultDict.a = 1
console.log(defaultDict.a) // 1
console.log(defaultDict.b) // 0
To clean it up a bit, you can wrap this in a constructor function, or perhaps use the class syntax.
class DefaultDict {
constructor(defaultVal) {
return new Proxy({}, {
get: (target, name) => name in target ? target[name] : defaultVal
})
}
}
const counts = new DefaultDict(0)
console.log(counts.c) // 0
EDIT: The above implementation only works well with primitives. It should handle objects too by taking a constructor function for the default value. Here is an implementation that should work with primitives and constructor functions alike.
class DefaultDict {
constructor(defaultInit) {
return new Proxy({}, {
get: (target, name) => name in target ?
target[name] :
(target[name] = typeof defaultInit === 'function' ?
new defaultInit().valueOf() :
defaultInit)
})
}
}
const counts = new DefaultDict(Number)
counts.c++
console.log(counts.c) // 1
const lists = new DefaultDict(Array)
lists.men.push('bob')
lists.women.push('alice')
console.log(lists.men) // ['bob']
console.log(lists.women) // ['alice']
console.log(lists.nonbinary) // []
Check out pycollections.js:
var collections = require('pycollections');
var dd = new collections.DefaultDict(function(){return 0});
console.log(dd.get('missing')); // 0
dd.setOneNewValue(987, function(currentValue) {
return currentValue + 1;
});
console.log(dd.items()); // [[987, 1], ['missing', 0]]
I don't think there is the equivalent but you can always write your own. The equivalent of a dictionary in javascript would be an object so you can write it like so
function defaultDict() {
this.get = function (key) {
if (this.hasOwnProperty(key)) {
return key;
} else {
return 0;
}
}
}
Then call it like so
var myDict = new defaultDict();
myDict[1] = 2;
myDict.get(1);
A quick dirty hack can be constructed using Proxy
function dict(factory, origin) {
return new Proxy({ ...origin }, {
get(dict, key) {
// Ensure that "missed" keys are set into
// The dictionary with default values
if (!dict.hasOwnProperty(key)) {
dict[key] = factory()
}
return dict[key]
}
})
}
So the following code:
n = dict(Number, [[0, 1], [1, 2], [2, 4]])
// Zero is the default value mapped into 3
assert(n[3] == 0)
// The key must be present after calling factory
assert(Object.keys(n).length == 4)
Proxies definitely make the syntax most Python-like, and there's a library called defaultdict2 that offers what seems like a pretty crisp and thorough proxy-based implementation that supports nested/recursive defaultdicts, something I really value and am missing in the other answers so far in this thread.
That said, I tend to prefer keeping JS a bit more "vanilla"/"native" using a function-based approach like this proof-of-concept:
class DefaultMap {
constructor(defaultFn) {
this.defaultFn = defaultFn;
this.root = new Map();
}
put(...keys) {
let map = this.root;
for (const key of keys.slice(0, -1)) {
map.has(key) || map.set(key, new Map());
map = map.get(key);
}
const key = keys[keys.length-1];
map.has(key) || map.set(key, this.defaultFn());
return {
set: setterFn => map.set(key, setterFn(map.get(key))),
mutate: mutationFn => mutationFn(map.get(key)),
};
}
get(...keys) {
let map = this.root;
for (const key of keys) {
map = map?.get(key);
}
return map;
}
}
// Try it:
const dm = new DefaultMap(() => []);
dm.put("foo").mutate(v => v.push(1, 2, 3));
dm.put("foo").mutate(v => v.push(4, 5));
console.log(dm.get("foo")); // [1, 2, 3, 4, 5]
dm.put("bar", "baz").mutate(v => v.push("a", "b"));
console.log(dm.get("bar", "baz")); // ["a", "b"]
dm.put("bar", "baz").set(v => 42);
console.log(dm.get("bar", "baz")); // 42
dm.put("bar", "baz").set(v => v + 1);
console.log(dm.get("bar", "baz")); // 43
The constructor of DefaultMap accepts a function that returns a default value for leaf nodes. The basic operations for the structure are put and get, the latter of which is self-explanatory. put generates a chain of nested keys and returns a pair of functions that let you mutate or set the leaf node at the end of these keys. Accessing .root gives you the underlying Map structure.
Feel free to leave a comment if I've overlooked any bugs or miss useful features and I'll toss it in.
Inspired by #Andy Carlson's answer, here's an implementation that works in a slightly more Pythonic way:
class DefaultDict {
constructor(defaultVal) {
return new Proxy(
{},
{
get: (target, name) => {
if (name == '__dict__') {
return target;
} else if (name in target) {
return target[name];
} else {
target[name] = defaultVal;
return defaultVal;
}
},
}
);
}
}
Basically, it also lets you retrieve all the gotten and set values of the "target", similar to how collections.defaultdict works in Python. This allows us to do things like:
const myDict = new DefaultDict(0);
myDict['a'] += 1;
myDict['b'] += 2;
myDict['c'] += 3;
myDict['whatever'];
console.log(myDict.__dict__);
// {'a': 1, 'b': 2, 'c': 3, 'whatever': 0}
To add to Andy Carlson's answer
If you default dict an array, you'll get a toJSON field in the resulting object. You can get rid of it by deconstructing to a new object.
const dd = new DefaultDict(Array);
//...populate the dict
return {...dd};
The original answer does not seem to work on the nested cases. I made some modifications to make it work:
class DefaultDict {
constructor(defaultInit) {
this.original = defaultInit;
return new Proxy({}, {
get: function (target, name) {
if (name in target) {
return target[name];
} else {
if (typeof defaultInit === "function") {
target[name] = new defaultInit().valueOf();
} else if (typeof defaultInit === "object") {
if (typeof defaultInit.original !== "undefined") {
target[name] = new DefaultDict(defaultInit.original);
} else {
target[name] = JSON.parse(JSON.stringify(defaultInit));
}
} else {
target[name] = defaultInit;
}
return target[name];
}
}
});
}
}
var a = new DefaultDict(Array);
a["banana"].push("ya");
var b = new DefaultDict(new DefaultDict(Array));
b["orange"]["apple"].push("yo");
var c = new DefaultDict(Number);
c["banana"] = 1;
var d = new DefaultDict([2]);
d["banana"].push(1);
var e = new DefaultDict(new DefaultDict(2));
e["orange"]["apple"] = 3;
var f = new DefaultDict(1);
f["banana"] = 2;
The difference is that if defaultInit is an object, we need to return a deep copy of the object, instead of the original one.

Access JavaScript property case-insensitively?

Assume I have an object:
var obj = {
foo:"bar",
fizz:"buzz"
};
I need to access a property of that object dynamically like so:
var objSetter = function(prop,val){
obj[prop] = val;
}
No problems there, except for that prop needs to be case insensitive in case the property name is passed into the function as, say, Foo instead of foo.
So how can I point to an object's property by name without regard to case? I would like to avoid iterating the entire object if possible.
Try this:
var myObject = { "mIxeDCaSEKeY": "value" };
var searchKey = 'mixedCaseKey';
var asLowercase = searchKey.toLowerCase();
myObject[Object.keys(myObject).find(key => key.toLowerCase() === asLowercase)];
You can alternatively already provide the searchKey in lowercase.
If you want it as a function:
/**
* #param {Object} object
* #param {string} key
* #return {any} value
*/
function getParameterCaseInsensitive(object, key) {
const asLowercase = key.toLowerCase();
return object[Object.keys(object)
.find(k => k.toLowerCase() === asLowercase)
];
}
If the key can't be found, then it'll return undefined, just like normal.
If you need to support older browsers, then you can use filter instead:
function getParameterCaseInsensitive(object, key) {
const asLowercase = key.toLowercase();
return object[Object.keys(object).filter(function(k) {
return k.toLowerCase() === asLowercase;
})[0]];
}
I suggest using the polyfills for Object.keys() and Array.filter() if you need even older support.
Note: If you want to also check non-enumerable keys, use Object.getOwnPropertyNames() instead of Object.keys().
Nerdy Note: This assumes your Object doesn't have a key undefined (eg: const foo = {[undefined]: 'bar'};). That's just weird.
Compare all the properties of obj with prop.
var objSetter = function(prop,val){
prop = (prop + "").toLowerCase();
for(var p in obj){
if(obj.hasOwnProperty(p) && prop == (p+ "").toLowerCase()){
obj[p] = val;
break;
}
}
}
For this, I prefer using the prototype over a standalone function just for ease of use and expressiveness. I just don't like funneling objects into functions if I don't have to.
Also, while the accepted answer works, I wanted a more comprehensive solution for both getting and setting that would behave as much like the native dot notation or bracket notation as possible.
With that in mind, I created a couple prototype functions for setting/getting an object property without regard to case. You have to remember to be VERY responsible when adding to the Object prototype. Especially when using JQuery and other libraries. Object.defineProperty() with enumerable set to false was used specifically to avoid conflict with JQuery. I also didn't bother naming the functions anything that indicates they are case-insensitive, but you certainly could. I like shorter names.
Here's the getter:
Object.defineProperty(Object.prototype, "getProp", {
value: function (prop) {
var key,self = this;
for (key in self) {
if (key.toLowerCase() == prop.toLowerCase()) {
return self[key];
}
}
},
//this keeps jquery happy
enumerable: false
});
Here's the setter:
Object.defineProperty(Object.prototype, "setProp", {
value: function (prop, val) {
var key,self = this;
var found = false;
if (Object.keys(self).length > 0) {
for (key in self) {
if (key.toLowerCase() == prop.toLowerCase()) {
//set existing property
found = true;
self[key] = val;
break;
}
}
}
if (!found) {
//if the property was not found, create it
self[prop] = val;
}
return val;
},
//this keeps jquery happy
enumerable: false
});
Now that we've created those functions, our code is super clean and concise and just works.
Case-insensitive getting:
var obj = {foo: 'bar', camelCase: 'humpy'}
obj.getProp("FOO"); //returns 'bar'
obj.getProp("fOO"); //returns 'bar'
obj.getProp("CAMELCASE"); //returns 'humpy'
obj.getProp("CamelCase"); //returns 'humpy'
Case-insensitive setting:
var obj = {foo: 'bar', camelCase: 'humpy'}
obj.setProp('CAmelCasE', 'super humpy'); //sets prop 'camelCase' to 'super humpy'
obj.setProp('newProp', 'newval'); //creates prop 'newProp' and sets val to 'newval'
obj.setProp('NewProp', 'anotherval'); //sets prop 'newProp' to 'anotherval'
Yet another variation on those already presented which pushes the iteration down into the Underscore/Lodash findKey function:
var _ = require('underscore');
var getProp = function (obj, name) {
var realName = _.findKey(obj, function (value, key) {
return key.toLowerCase() === name.toLowerCase();
});
return obj[realName];
};
For example:
var obj = { aa: 1, bB: 2, Cc: 3, DD: 4 };
getProp(obj, 'aa'); // 1
getProp(obj, 'AA'); // 1
getProp(obj, 'bb'); // 2
getProp(obj, 'BB'); // 2
getProp(obj, 'cc'); // 3
getProp(obj, 'CC'); // 3
getProp(obj, 'dd'); // 4
getProp(obj, 'DD'); // 4
getProp(obj, 'EE'); // undefined
This answer requires ES6.
const x = { 'aB': 1, 'X-Total-Count': 10, y3: 2 }
console.log(x[Object.keys(x).find(key=>{return key.match(/^ab$/i)})])
console.log(x[Object.keys(x).find(key=>{return key.match(/^x-total-count$/i)})])
console.log(x[Object.keys(x).find(key=>{return key.match(/^y3$/i)})])
It seems to me like a good candidate for Proxy with traps to convert string keys to either upper case or lower case and behaving like a regular object.
This works with either notation: dots or braquets
Here is the code:
'use strict';
function noCasePropObj(obj)
{
var handler =
{
get: function(target, key)
{
//console.log("key: " + key.toString());
if (typeof key == "string")
{
var uKey = key.toUpperCase();
if ((key != uKey) && (key in target))
return target[key];
return target[uKey];
}
return target[key];
},
set: function(target, key, value)
{
if (typeof key == "string")
{
var uKey = key.toUpperCase();
if ((key != uKey) && (key in target))
target[key] = value;
target[uKey] = value;
}
else
target[key] = value;
},
deleteProperty: function(target, key)
{
if (typeof key == "string")
{
var uKey = key.toUpperCase();
if ((key != uKey) && (key in target))
delete target[key];
if (uKey in target)
delete target[uKey];
}
else
delete target[key];
},
};
function checkAtomic(value)
{
if (typeof value == "object")
return new noCasePropObj(value); // recursive call only for Objects
return value;
}
var newObj;
if (typeof obj == "object")
{
newObj = new Proxy({}, handler);
// traverse the Original object converting string keys to upper case
for (var key in obj)
{
if (typeof key == "string")
{
var objKey = key.toUpperCase();
if (!(key in newObj))
newObj[objKey] = checkAtomic(obj[key]);
}
}
}
else if (Array.isArray(obj))
{
// in an array of objects convert to upper case string keys within each row
newObj = new Array();
for (var i = 0; i < obj.length; i++)
newObj[i] = checkAtomic(obj[i]);
}
return newObj; // object with upper cased keys
}
// Use Sample:
var b = {Name: "Enrique", last: "Alamo", AdDrEsS: {Street: "1233 Main Street", CITY: "Somewhere", zip: 33333}};
console.log("Original: " + JSON.stringify(b)); // Original: {"Name":"Enrique","last":"Alamo","AdDrEsS":{"Street":"1233 Main Street","CITY":"Somewhere","zip":33333}}
var t = noCasePropObj(b);
console.log(JSON.stringify(t)); // {"NAME":"Enrique","LAST":"Alamo","ADDRESS":{"STREET":"1233 Main Street","CITY":"Somewhere","ZIP":33333}}
console.log('.NaMe:' + t.NaMe); // .NaMe:Enrique
console.log('["naME"]:' + t["naME"]); // ["naME"]:Enrique
console.log('.ADDreSS["CitY"]:' + t.ADDreSS["CitY"]); // .ADDreSS["CitY"]:Somewhere
console.log('check:' + JSON.stringify(Object.getOwnPropertyNames(t))); // check:["NAME","LAST","ADDRESS"]
console.log('check2:' + JSON.stringify(Object.getOwnPropertyNames(t['AddresS']))); // check2:["STREET","CITY","ZIP"]
You could do this in order to "normalize" prop
var normalizedProp = prop.toLowerCase();
obj[normalizedProp] = val;
const getPropertyNoCase = (obj, prop) => obj[Object.keys(obj).find(key => key.toLowerCase() === prop.toLowerCase() )];
or
const getPropertyNoCase = (obj, prop) => {
const lowerProp = prop.toLowerCase(obj[Object.keys(obj).find(key => key.toLowerCase() === prop.toLowerCase() )];
}
The ES6 example posted by #nilloc is incorrect and will break in use.
Here is a working example:
const x = {'first':5,'X-Total-Count':10,'third':20};
console.log(x[Object.keys(x).reduce((result,key)=>{
if (!result) {
return key.match(/x-total-count/i)
} else {
return result;
}
},null)]);
or better yet, it should return undefined if the key doesn't exist:
const x = {'first':5,'X-Total-Count':10,'third':20};
console.log(x[Object.keys(x).reduce((result,key)=>{
if (!result) {
return key.match(/x-total-count/i) || undefined
} else {
return result;
}
},undefined)]);
One consideration is that the above example will return the last matching key in the object if there are multiple keys that match.
Here is an example with the code made into a function:
/**
* #param {Object} object
* #param {string} key
* #return {string||undefined} value || undefined
*/
function getKeyCase(obj,key) {
const re = new RegExp(key,"i");
return Object.keys(obj).reduce((result,key)=>{
if (!result) {
return key.match(re) || undefined
} else {
return result;
}
},undefined);
const x = {'first':5,'X-Total-Count':10,'third':20};
console.log(x[getKeyCase(x,"x-total-count")]);
Its really sad that the iteration can't be skipped as it seems. For me what is acceptable but may not be for everyone is to shape the object one time via iteration and then use it in regular hashmap fashion.
const hashmap = {
'FOO': 'foo as in function programming',
'bar': 'bar is in baz',
};
const shapedmap = Object.entries(hashmap).reduce(
(acc, [key, val]) => (acc[key.toUpperCase()] = val, acc), {}
);
for (const term of ['foo', 'bar', 'baz']) {
const match = shapedmap[term.toUpperCase()]
match && console.log('awesome, we got the term.', match);
};
Even if it just one time lookup has to be performed, it shouldn't less performant as any other iteration solution since after 1 pass, the lookup speed is constant. (I guess).
This is an old question, but it was the first one I found.
As #ZachSmith says, you can use a Proxy.
Here's some example code:
function lowercase(oldKey) {
// Check that it's a string.
return typeof oldKey === 'string' ? oldKey.toLowerCase() : oldKey;
}
const propertiesMap = new Map(
Object.keys(obj).map(propKey => [lowercase(propKey), obj[propKey]])
);
const caseInsensitiveGetHandler = {
get: function(target, property, receiver) {
return propertiesMap.get(lowercase(property));
}
};
obj = new Proxy(obj, caseInsensitiveGetHandler);
For my use case, I only needed to proxy the object's getter, but you may need to implement more of the Proxy methods.
There is no need for any iteration. Since prop might not be a string, it should be coerced to a string first where appropriate since that's what objects do natively. A simple getter function is:
function objGetter(prop) {
return obj[String(prop).toLowerCase()];
}
If there is a requirement is to restring access to own properties:
function objGetter(prop) {
prop = String(prop).toLowerCase();
if (obj.hasOwnProperty(prop)) {
return obj.prop;
}
}
and a setter:
function objSetter(prop, val) {
obj[String(prop).toLowerCase()] = val;
}
Heres a very simple code to do this
Assuming that data is the array of objects like
data=[{"A":"bc","B":"nn"}]
var data=data.reduce(function(prev, curr) {
var cc = curr; // current value
var K = Object.keys(cc); // get all keys
var n = {};
for (var i = 0; i < K.length; i++) {
var key = K[i];//get hte key
n[key.toLowerCase()] = cc[key] // convert to lowercase and assign
}
prev.push(n) // push to array
return prev;
}, [])
Output will be
data=[{"a":"bc","b":"nn"}]
You might only need to do case-insensitive matching (usually expensive because of object iteration) IF a case-sensitive match (cheap and quick) fails.
Say you have:
var your_object = { "Chicago" : 'hi' , "deTroiT" : 'word' , "atlanta" : 'get r dun' } ;
And you have, for whatever reason, the_value, Detroit:
if( your_object.hasOwnProperty( the_value ) )
{
// do what you need to do here
}
else
{ // since the case-sensitive match did not succeed,
// ... Now try a the more-expensive case-insensitive matching
for( let lvs_prop in your_object )
{ if( the_value.toLowerCase() == lvs_prop.toLowerCase() )
{
// do what you need to do here
break ;
} ;
}
} ;
why would we do it that complicated when we simply can make it all lower case:
var your_object = {
"chickago" : 'hi' ,
"detroit" : 'word',
"atlanta" : 'get r dun',
GetName: function (status) {
return this[status].name;
} };
to call it: your_object.GetName(your_var.toLowerCase());
Another simple way:
function getVal(obj, prop){
var val;
prop = (prop + "").toLowerCase();
for(var p in obj){
if(obj.hasOwnProperty(p) && prop == (p+ "").toLowerCase()){
val = obj[p]
break;
}
}
return val;
}
Use it like this:
var obj = {
foo:"bar",
fizz:"buzz"
};
getVal(obj,"FoO") -> returns "bar"
Here is a nice recursive function that allows you to traverse a javascript object in a case-insensitive way:
let testObject = {'a': {'B': {'cC': [1,2,3]}}}
let testSeq = ['a','b','cc']
function keySequence(o, kseq) {
if(kseq.length==0){ return o; }
let validKeys = Object.keys(o).filter(k=>k.toLowerCase()==kseq[0].toLowerCase());
if(validKeys.length==0) { return `Incorrect Key: ${kseq[0]}` }
return keySequence(o[validKeys[0]], kseq.slice(1))
}
keySequence(testObject, testSeq); //returns [1,2,3]
This will convert everything to lowercase, but in a bind this could help if you are not concerned with retaining case.
var somedata = {
"MixEdCase": 1234
}
var temp = JSON.parse(JSON.stringify(somedata).toLowerCase());
console.log(temp.mixedcase);
// or
console.log(temp["mixedcase"]);
So, you will need to get the object key that matches the case of the existing object, then use this to do your object update.
const obj = {
foo:"bar",
fizz:"buzz"
};
// to get obj.foo or obj.FOO or obj.foO returning "bar"
// create regex expression of case insensitive version of the key string
const regex=passedKey=> new RegExp(`^${passedKey}$`,'gi');
// find the key that matches the string you are passing
const formattedKey=passedKey=>Object.keys(obj).find(key=>regex(passedKey).test(key));
formattedKey('Foo'); // returns foo
formattedKey('FoO'); // returns foo
// consequently you can can use it like wise
obj[formattedKey('Foo')] // returns bar
obj[formattedKey('FoO')] // returns bar
obj[formattedKey('foo')] // returns bar

Categories

Resources