How to Deep clone in javascript
Asked Answered
E

27

175

How do you deep clone a JavaScript object?

I know there are various functions based on frameworks like JSON.parse(JSON.stringify(o)) and $.extend(true, {}, o) but I don't want to use a framework like that.

What is the most elegant or efficient way to create a deep clone.

We do care about edge cases like cloning array's. Not breaking prototype chains, dealing with self reference.

We don't care about supporting copying of DOM objects and like because .cloneNode exists for that reason.

As I mainly want to use deep clones in node.js using ES5 features of the V8 engine is acceptable.

[Edit]

Before anyone suggests let me mention there is a distinct difference between creating a copy by prototypically inheriting from the object and cloning it. The former makes a mess of the prototype chain.

[Further Edit]

After reading your answer I came to the annoying discovery that cloning entire objects is a very dangerous and difficult game. Take for example the following closure based object

var o = (function() {
     var magic = 42;

     var magicContainer = function() {
          this.get = function() { return magic; };
          this.set = function(i) { magic = i; };
     }

      return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

Is there any way to write a clone function that clones the object, has the same state at time of cloning but cannot alter the state of o without writing a JS parser in JS.

There should be no real world need for such a function anymore. This is mere academic interest.

Evulsion answered 16/12, 2010 at 10:49 Comment(8)
Before it gets marked duplicate, I looked at #122602 and did not find any answer that dealt with all edge cases.Evulsion
The requirement in the "further edit" section is impossible to achieve without "help" from the object itself, since such private variables are truly private, and by consequence not accessible by a generic clone function. The object in question should expose its own tailor-made clone method.Platysma
I've been reading about this a bunch tonight, and among the resources I found was this ugly-looking blog post that includes a couple of hacks for accessing the structured clone algorithm in the browser: dassur.ma/things/deep-copyIllegible
Does this answer your question? What is the most efficient way to deep clone an object in JavaScript?Communion
When you say "I know there are various functions based on frameworks like JSON.parse(JSON.stringify(o)) and $.extend(true, {}, o) but I don't want to use a framework like that," I'm confused. $ is certainly an external library (jQuery), but JSON is part of vanilla JavaScript. What was the aversion (if you remember) to using JSON? In 2010, maybe the lack of IE 6 & 7 support?Leucoplast
How do you clone an Array of Objects in Javascript?Vinnie
@Leucoplast the JSON strategy won't work on objects which contain values which aren't serializable. Like Date or Blob, File etc.Kanter
@Kanter Fair, but I think my point was that ultimately all these methods seem to have edge case holes where their deep clone process doesn't work, with an implication that serializing to json and knowing the rules of how to rehydrate your object model might have the "best qua conventional" results. That is, I'm betting we can always say, "the X strategy won't work on [some subset of objects] that require [m, n, o]," and wondered specifically how JSON.stringify had failed OP.Leucoplast
C
85

It really depends what you would like to clone. Is this a truly JSON object or just any object in JavaScript? If you would like to do any clone, it might get you into some trouble. Which trouble? I will explain it below, but first, a code example which clones object literals, any primitives, arrays and DOM nodes.

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}

var copy = clone({
    one : {
        'one-one' : new String("hello"),
        'one-two' : [
            "one", "two", true, "four"
        ]
    },
    two : document.createElement("div"),
    three : [
        {
            name : "three-one",
            number : new Number("100"),
            obj : new function() {
                this.name = "Object test";
            }   
        }
    ]
})

And now, let's talk about problems you might get when start cloning REAL objects. I'm talking now, about objects which you create by doing something like

var User = function(){}
var newuser = new User();

Of course you can clone them, it's not a problem, every object expose constructor property, and you can use it to clone objects, but it will not always work. You also can do simple for in on this objects, but it goes to the same direction - trouble. I have also included clone functionality inside the code, but it's excluded by if( false ) statement.

So, why cloning can be a pain? Well, first of all, every object/instance might have some state. You never can be sure that your objects doesn't have for example an private variables, and if this is the case, by cloning object, you just break the state.

Imagine there is no state, that's fine. Then we still have another problem. Cloning via "constructor" method will give us another obstacle. It's an arguments dependency. You never can be sure, that someone who created this object, did not did, some kind of

new User({
   bike : someBikeInstance
});

If this is the case, you are out of luck, someBikeInstance was probably created in some context and that context is unkown for clone method.

So what to do? You still can do for in solution, and treat such objects like normal object literals, but maybe it's an idea not to clone such objects at all, and just pass the reference of this object?

Another solution is - you could set a convention that all objects which must be cloned should implement this part by themselves and provide appropriate API method ( like cloneObject ). Something what cloneNode is doing for DOM.

You decide.

Cynicism answered 16/12, 2010 at 12:13 Comment(9)
I came apon the hurdle of dealing with objects that use closures to hide state myself. How can one clone an object and its entire state but still ensure the clone cant alter the originals state by itself. A couinter point to result = new item.constructor(); being bad is that given the constructor function & the item object you should be able to RE any paramaters passed into the constructor.Evulsion
@Evulsion : if objects use closures to hide state, then you can't clone them. Hence the term 'closure'. As nemisj says at the end, the best way is to implement an API method for cloning (or serialization/deserialization) if that's an option.Justiciary
@MichielKalkman I had a feeling that was the case. Although someone might have had a really clever solution to this.Evulsion
What is the effect of false && item.constructor? Isn't that if useless?Penultimate
@GabrielPetrovay That if is 'useless' from the functional perspective, because it'll never ever run, but it has the academic purpose of showing an hypothetical implementation one might try to use, which author does not advice because of the reason explained later. So, yes, it will trigger else clause everythime the condition is evaluated, and yet there's a reason for the code to be there.Draconian
@nemisj: the normalizing for Boolean will fail, because new Boolean(new Boolean(false)) => [Boolean: true]. Isn't it?Partiality
For me, the check for primitive isn't working. For example, "foo" instanceof String is false.Sylviesylvite
OP wrote "dealing with self reference"... This doesn't deal with it.Platysma
for boolean it would be more convenient to return item.valueOf() instead of Boolean(item) since Boolean(new Boolean(false)) is trueTerribly
H
207

Very simple way, maybe too simple:

var cloned = JSON.parse(JSON.stringify(objectToClone));
Hasin answered 29/1, 2015 at 18:52 Comment(12)
Great unless an object value is a function, at which point you'll have to use something like the accepted answer. Or use a helper function like cloneDeep in Lodash.Ritualize
If an object value is a function, the object is no JSON.Germaine
What use case may justify to clone a function rather than just using it?Hasin
If i remember correctly this also converts dates into strings.Moultrie
@G.Ghez if you clone a object that contains a function that function will be lost..Moultrie
The OP stated "I know there are various functions ... like JSON.parse(JSON.stringify(o)) ... but I don't want to use a framework like that.". So this is a) repeating what was in the question and b) not a correct answer.Anastomosis
this type of copy is causes error if the object contains circular references;Chekhov
@HamedMahdizadeh The answer would run infinitely, so an error may be better than completely crashing your appPurdum
@Anastomosis yes technically you are correct. Tho in all practicality, this answer helped me more than any other. I didn't read more of the original question beside the title (since thats all I cared about) so this answer was easier to find that to read the entire question body (which was 99% irrelevant to my search)Inoperative
@Anastomosis Though OP says they don't want to use a "framework" like JSON or $. The problem? One of these (jQuery, natch) really is a framework. JSON is part of vanilla JavaScript. Probably worth asking the OP too, but my reply would be "You're not using a framework like jQuery when you use JSON".Leucoplast
@Leucoplast yes I guess it depends on how much you try to answer the question exactly as asked compared to providing a useful answer. I think the answers here show that you either use this answer which is very simple solution that works for everything except functions or you have to write a very complex Javascript recursive function that will probably fail in some other way.Anastomosis
Worked on my vue 3 project, faced a row data change on datatable row while using row data. Now fixed ! :-)Verlinevermeer
C
85

It really depends what you would like to clone. Is this a truly JSON object or just any object in JavaScript? If you would like to do any clone, it might get you into some trouble. Which trouble? I will explain it below, but first, a code example which clones object literals, any primitives, arrays and DOM nodes.

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}

var copy = clone({
    one : {
        'one-one' : new String("hello"),
        'one-two' : [
            "one", "two", true, "four"
        ]
    },
    two : document.createElement("div"),
    three : [
        {
            name : "three-one",
            number : new Number("100"),
            obj : new function() {
                this.name = "Object test";
            }   
        }
    ]
})

And now, let's talk about problems you might get when start cloning REAL objects. I'm talking now, about objects which you create by doing something like

var User = function(){}
var newuser = new User();

Of course you can clone them, it's not a problem, every object expose constructor property, and you can use it to clone objects, but it will not always work. You also can do simple for in on this objects, but it goes to the same direction - trouble. I have also included clone functionality inside the code, but it's excluded by if( false ) statement.

So, why cloning can be a pain? Well, first of all, every object/instance might have some state. You never can be sure that your objects doesn't have for example an private variables, and if this is the case, by cloning object, you just break the state.

Imagine there is no state, that's fine. Then we still have another problem. Cloning via "constructor" method will give us another obstacle. It's an arguments dependency. You never can be sure, that someone who created this object, did not did, some kind of

new User({
   bike : someBikeInstance
});

If this is the case, you are out of luck, someBikeInstance was probably created in some context and that context is unkown for clone method.

So what to do? You still can do for in solution, and treat such objects like normal object literals, but maybe it's an idea not to clone such objects at all, and just pass the reference of this object?

Another solution is - you could set a convention that all objects which must be cloned should implement this part by themselves and provide appropriate API method ( like cloneObject ). Something what cloneNode is doing for DOM.

You decide.

Cynicism answered 16/12, 2010 at 12:13 Comment(9)
I came apon the hurdle of dealing with objects that use closures to hide state myself. How can one clone an object and its entire state but still ensure the clone cant alter the originals state by itself. A couinter point to result = new item.constructor(); being bad is that given the constructor function & the item object you should be able to RE any paramaters passed into the constructor.Evulsion
@Evulsion : if objects use closures to hide state, then you can't clone them. Hence the term 'closure'. As nemisj says at the end, the best way is to implement an API method for cloning (or serialization/deserialization) if that's an option.Justiciary
@MichielKalkman I had a feeling that was the case. Although someone might have had a really clever solution to this.Evulsion
What is the effect of false && item.constructor? Isn't that if useless?Penultimate
@GabrielPetrovay That if is 'useless' from the functional perspective, because it'll never ever run, but it has the academic purpose of showing an hypothetical implementation one might try to use, which author does not advice because of the reason explained later. So, yes, it will trigger else clause everythime the condition is evaluated, and yet there's a reason for the code to be there.Draconian
@nemisj: the normalizing for Boolean will fail, because new Boolean(new Boolean(false)) => [Boolean: true]. Isn't it?Partiality
For me, the check for primitive isn't working. For example, "foo" instanceof String is false.Sylviesylvite
OP wrote "dealing with self reference"... This doesn't deal with it.Platysma
for boolean it would be more convenient to return item.valueOf() instead of Boolean(item) since Boolean(new Boolean(false)) is trueTerribly
T
54

we can achieve deep clone by using structuredClone()

const original = { name: "stack overflow" };


// Clone it
const clone = structuredClone(original);
Thickleaf answered 16/11, 2021 at 12:15 Comment(6)
Sure.. currently only supported by firefoxGrogshop
and also supported by nodejs 17+Thickleaf
Note that this can be polyfilled in browsers through the History API function structuredClone(val) { const { href } = location; const prev = history.state; history.replaceState(val, "", href); const res = history.state; history.replaceState(prev, "", href); return res; } However this structured cloning algorithm will not handle every types of inputs, e.g functions, or DOM Elements do throw.Leontina
Can_I_use reports now 89.7% availability as of time of this comment.Determined
Also available on Chrome and Edge from v98 onward.Idzik
becareful it's not support JS Proxies like the one used by Vue for reactive objectsCompellation
P
53

The JSON.parse(JSON.stringify()) combination to deep copy Javascript objects is an ineffective hack, as it was meant for JSON data. It does not support values of undefined or function () {}, and will simply ignore them (or null them) when "stringifying" (marshalling) the Javascript object into JSON.

A better solution is to use a deep copy function. The function below deep copies objects, and does not require a 3rd party library (jQuery, LoDash, etc).

function copy(aObject) {
  // Prevent undefined objects
  // if (!aObject) return aObject;

  let bObject = Array.isArray(aObject) ? [] : {};

  let value;
  for (const key in aObject) {

    // Prevent self-references to parent object
    // if (Object.is(aObject[key], aObject)) continue;
    
    value = aObject[key];

    bObject[key] = (typeof value === "object") ? copy(value) : value;
  }

  return bObject;
}

Note: This code can check for simple self-references (uncomment the section // Prevent self-references to parent object), but you should also avoid creating objects with self-references when possible. Please see: https://softwareengineering.stackexchange.com/questions/11856/whats-wrong-with-circular-references

Paddlefish answered 6/1, 2016 at 2:27 Comment(21)
Except when aObject (or another object it contains) contains a self reference to itself... stackoverflow™ !Knitwear
@ringø - Can you provide some "self reference" test cases?Paddlefish
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };Knitwear
I like this solution. Only fix for me was dealing with null values: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;Cheesy
This function is simple, understandable and will catch almost all cases. In the JavaScript world that's about as perfect as you can get.Anastomosis
@DavidKirkland I'd put the v === null check as a guard block at the top of the function. This then catches if null is passed in initially and also stops the recursion expression from getting any more complexAnastomosis
@Anastomosis - please provide the code - I don't see how v === null can moved to the top of the function.Paddlefish
This code has bugs. Using it on {a:[1,2,3]} won't deep clone the array property a. Consider using github.com/pvorb/clone instead.Trainload
@Trainload - let me know what other bugs you found. I'll take a look at the {a: [1, 2, 3]} issue. The entire point of this code, is so people don't need to install a third-party package.Paddlefish
@tfmontague imo this is exactly what git and npm are for, instead of trying to iterate on a code snippet on a Q&A websiteTrainload
@Trainload - From what I can tell with testing, {a:[1,2,3]} is deep copied correctlyPaddlefish
Although not packaged, your code is still open source and still a dependency, @tim. It's not superior in any way to the packages you're advertising against. On the contrary. It fails in more cases than those packages. And it's lacking the true power (and spirit) of open source software, coming from two main ingredients: collaborative authoring and distributed testing. If someone in my team used your code instead of a well tested package, I'd consider they disregarded our team's interests and the interests of our clients, losing us money by taking unnecessary risks.Tarantella
let uh = {}; uh.oh = uh; copy(uh); // => oh...Tarantella
@Tarantella - this is a StackOverflow question on how to deep clone, and what I've provided is a simple code solution for others to debate and improve. This is a far better solution, then telling others to install a 3rd-party package, which does the thinking for them, and includes dozens-to-hundreds of unused functions, which introduce their own bugs.Paddlefish
Déjàvu, ringø, tao - I do not believe objects with self-references are considered a good practice - please see: softwareengineering.stackexchange.com/questions/11856/…. However, I have provided a commented-out check for simple self-references between child and parent objects if needed.Paddlefish
Whether or not self reference is a good practice is not the subject here. It's used in the wild, and it's part of the requirement. You chose to answer this question. Ref your rant against open source libs: lodash-es is modular. So you can import only cloneDeep, if that's all you need. That function is as open source as yours. Neither version "thinks". It's just code. It either does what's expected or not. Your version doesn't, as per OP's requirements. Why not open cloneDeep's source code? I guarantee it won't "think for you". Most likely, it will make you think. And learn.Tarantella
@Tarantella - OP said "I know there are various functions based on frameworks like JSON.parse(JSON.stringify(o)) and $.extend(true, {}, o) but I don't want to use a framework like that. What is the most elegant or efficient way to create a deep clone. We do care about edge cases like cloning array's. Not breaking prototype chains, dealing with self reference. ... There should be no real world need for such a function anymore. This is mere academic interest."Paddlefish
IMO, the problem is that this question, and its answers, are used by junior devs. Who read the questions' title, copy/paste the answers' code, without reading anything else, and expect it to work. That's why I objected to your apparent advice to not use well tested solutions. IMHO, that advice is detrimental. And it also renders you as someone who doesn't understand the benefits of using open source software. Besides, I find it hard to believe you never use utility libraries, exactly because one shouldn't reinvent the wheel in every project.Tarantella
@Paddlefish This will also fail with deepcopying tree structures. As childs will have a reference to their parent and the parent to the child. And from the page you linked, this answer argues that in trees a circular reference like that isn't bad design at all. softwareengineering.stackexchange.com/a/11861/315637Leroy
@KerwinSneijders - You are correct. Circular references in graphs, trees, etc are not considered bad design. However, if there is a simple reference from the child to the parent, this code (with the "prevent self-reference" check) will still deep copy those structures - but break the reference. I will try to think of a rewrite to the code, that deep copies all structures with circular references (and preserves them).Paddlefish
@Tarantella If your junior devs copy/paste without thinking that's your job to set them straight, not the job of stackoverflow users. Furthermore, using open source software is essentially giving someone else keys to your house; if things go missing, who do you blame then? Either you write your own code or you accept the risk of using someone else's. tim-montague is making it easier for other developers to think through this problem, but they still have to choose to think for themselves.Ernaernald
P
36

There is now structuredClone in the Web API which also works with circular references.


Previous answer

Here is an ES6 function that will also work for objects with cyclic references:

function deepClone(obj, hash = new WeakMap()) {
    if (Object(obj) !== obj) return obj; // primitives
    if (hash.has(obj)) return hash.get(obj); // cyclic reference
    const result = obj instanceof Set ? new Set(obj) // See note about this!
                 : obj instanceof Map ? new Map(Array.from(obj, ([key, val]) => 
                                        [key, deepClone(val, hash)])) 
                 : obj instanceof Date ? new Date(obj)
                 : obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
                 // ... add here any specific treatment for other classes ...
                 // and finally a catch-all:
                 : obj.constructor ? new obj.constructor() 
                 : Object.create(null);
    hash.set(obj, result);
    return Object.assign(result, ...Object.keys(obj).map(
        key => ({ [key]: deepClone(obj[key], hash) }) ));
}

// Sample data
var p = {
  data: 1,
  children: [{
    data: 2,
    parent: null
  }]
};
p.children[0].parent = p;

var q = deepClone(p);

console.log(q.children[0].parent.data); // 1

A note about Sets and Maps

How to deal with the keys of Sets and Maps is debatable: those keys are often primitives (in which case there is no debate), but they can also be objects. In that case the question becomes: should those keys be cloned?

One could argue that this should be done, so that if those objects are mutated in the copy, the objects in the original are not affected, and vice versa.

On the other hand one would want that if a Set/Map has a key, this should be true in both the original and the copy -- at least before any change is made to either of them. It would be strange if the copy would be a Set/Map that has keys that never occurred before (as they were created during the cloning process): surely that is not very useful for any code that needs to know whether a given object is a key in that Set/Map or not.

As you notice, I am more of the second opinion: the keys of Sets and Maps are values (maybe references) that should remain the same.

Such choices will often also surface with other (maybe custom) objects. There is no general solution, as much depends on how the cloned object is expected to behave in your specific case.

Platysma answered 27/10, 2016 at 20:56 Comment(6)
doesn't handle date and regexpEstrus
@mkeremguc, thanks for your comment. I updated the code to support date and regexp.Platysma
Nice solution including map. Just missing support for cloning es6 sets.Uranometry
If I'm not mistaken, you can probably add support for Sets with: if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));Uranometry
@RobertBiggs, that is one possibility, but in my opinion if a Set has a certain key, that should also be true in the cloned version of that Set. With your suggested code this would not hold true if the keys are objects. Therefore I would suggest not cloning the keys -- I really think it would then behave more as expected. See update of my answer in that respect.Platysma
True, but if you're working with reactive code that expects immutable data, then you would want Sets that were deep clones as well. I have to admit that I do sometimes want a object with pointers to remote objects so I can mutate them from there, rather than having to reach out to the originals. That said, I seem to be doing that less and less wit the passage of time. Having immutable data makes more sense in more situations than the other way around.Uranometry
D
13

The Underscore.js contrib library library has a function called snapshot that deep clones an object

snippet from the source:

snapshot: function(obj) {
  if(obj == null || typeof(obj) != 'object') {
    return obj;
  }

  var temp = new obj.constructor();

  for(var key in obj) {
    if (obj.hasOwnProperty(key)) {
      temp[key] = _.snapshot(obj[key]);
    }
  }

  return temp;
}

once the library is linked to your project, invoke the function simply using

_.snapshot(object);
Duthie answered 28/5, 2015 at 10:1 Comment(2)
good solution, just a point to remember: the clone and the original share the same prototype. if that's a problem its possible to add "temp.__proto__ = .snapshot(obj.__proto_);" right above the "return temp", and to support build-in classes with properties marked as 'no enumerate' you can iterate on getOwnPropertyNames() instead of "for (var key in obj)"Finery
This will not handle those situations: (1) props is Symbol (2) circular referenceTraditor
M
4

As others have noted on this and similar questions, cloning an "object", in the general sense, is dubious in JavaScript.

However, there is a class of objects, which I call "data" objects, that is, those constructed simply from { ... } literals and/or simple property assignments or deserialized from JSON for which it is reasonable to want to clone. Just today I wanted to artificially inflate data received from a server by 5x to test what happens for a large data set, but the object (an array) and its children had to be distinct objects for things to function correctly. Cloning allowed me to do this to multiply my data set:

return dta.concat(clone(dta),clone(dta),clone(dta),clone(dta));

The other place I often end up cloning data objects is for submitting data back to the host where I want to strip state fields from the object in the data model before sending it. For example, I might want to strip all fields starting with "_" from the object as it is cloned.

This is the code I ended up writing to do this generically, including supporting arrays and a selector to choose which members to clone (which uses a "path" string to determine context):

function clone(obj,sel) {
    return (obj ? _clone("",obj,sel) : obj);
    }

function _clone(pth,src,sel) {
    var ret=(src instanceof Array ? [] : {});

    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }

        var val=src[key], sub;

        if(sel) {
            sub+=pth+"/"+key;
            if(!sel(sub,key,val)) { continue; }
            }

        if(val && typeof(val)=='object') {
            if     (val instanceof Boolean) { val=Boolean(val);        }
            else if(val instanceof Number ) { val=Number (val);        }
            else if(val instanceof String ) { val=String (val);        }
            else                            { val=_clone(sub,val,sel); }
            }
        ret[key]=val;
        }
    return ret;
    }

The simplest reasonable deep-clone solution, assuming a non-null root object and with no member selection is:

function clone(src) {
    var ret=(src instanceof Array ? [] : {});
    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }
        var val=src[key];
        if(val && typeof(val)=='object') { val=clone(val);  }
        ret[key]=val;
        }
    return ret;
    }
Metamorphic answered 20/9, 2014 at 1:13 Comment(0)
S
4

Lo-Dash, now a superset of Underscore.js, has a couple of deep clone functions:

From an answer of the author himself:

lodash underscore build is provided to ensure compatibility with the latest stable version of Underscore.

Scalar answered 26/3, 2018 at 12:46 Comment(4)
question said " i don't want to use libraries"Routinize
@FemiOni the question doesn't have anything about libraries (even in its old edits)... A few other answers in here are also using one library or another as well.Scalar
@FemiOni answer downvoted yesterday. I wonder... Anyway, this is a learning place and just in case someone is actually going to implement the deep clone itself, the lodash source code baseClone may provide some ideas.Scalar
@FemiOni the JSON object is neither a library nor a framework... If you are going to implement the function, I suggest looking into one of the open source libraries and use the parts you need (many have been tested for years). It will avoid bugs and missed considerations in the long term.Scalar
C
3

This is the deep cloning method I use, I think it Great, hope you make suggestions

function deepClone (obj) {
    var _out = new obj.constructor;

    var getType = function (n) {
        return Object.prototype.toString.call(n).slice(8, -1);
    }

    for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
            _out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
        }
    }
    return _out;
}
Corrosive answered 15/9, 2017 at 8:21 Comment(0)
J
3

The below function is most efficient way to deep clone javascript objects.

function deepCopy(obj){
    if (!obj || typeof obj !== "object") return obj;

    var retObj = {};

    for (var attr in obj){
        var type = obj[attr];

        switch(true){
            case (type instanceof Date):
                var _d = new Date();
                _d.setDate(type.getDate())
                retObj[attr]= _d;
                break;

            case (type instanceof Function):
                retObj[attr]= obj[attr];
                break;

            case (type instanceof Array):
                var _a =[];
                for (var e of type){
                    //_a.push(e);
                    _a.push(deepCopy(e));
                }
                retObj[attr]= _a;
                break;

            case (type instanceof Object):
                var _o ={};
                for (var e in type){
                    //_o[e] = type[e];
                    _o[e] = deepCopy(type[e]);
                }
                retObj[attr]= _o;
                break;

            default:
                retObj[attr]= obj[attr];
        }
    }
    return retObj;
}

var obj = {
    string: 'test',
    array: ['1'],
    date: new Date(),
    object:{c: 2, d:{e: 3}},
    function: function(){
        return this.date;
    }
};

var copyObj = deepCopy(obj);

console.log('object comparison', copyObj === obj); //false
console.log('string check', copyObj.string === obj.string); //true
console.log('array check', copyObj.array === obj.array); //false
console.log('date check', copyObj2.date === obj.date); //false
console.log('object check', copyObj.object === obj.object); //false
console.log('function check', copyObj.function() === obj.function()); //true
Jerrome answered 4/8, 2019 at 18:56 Comment(5)
Do you have any arguments to support your claim?Jemima
I have given an example below the function. Do you have any doubt?Jerrome
Those examples show that the function is working, which is cool but why is it "the most efficient way"?Jemima
Because it is recursively copying the attributes of the object in single loop. Also Date, Function, Object, Array, Number, String all thses are handled properly. Do you have any any other way around?Jerrome
Why are the checks false for many tests ?Quiescent
B
3

Avoid use this method

let cloned = JSON.parse(JSON.stringify(objectToClone));

Why? this method will convert 'function,undefined' to null

const myObj = [undefined, null, function () {}, {}, '', true, false, 0, Symbol];

const IsDeepClone = JSON.parse(JSON.stringify(myObj));

console.log(IsDeepClone); //[null, null, null, {…}, "", true, false, 0, null]

try to use deepClone function.There are several above

Basalt answered 28/8, 2020 at 20:24 Comment(0)
O
3

Deep cloning of the object can be done in several ways but each having their own limitations as mentioned below. Hence, I will suggest you to use structuredClone algorithm.

  • JSON.parse(JSON.stringify(object)) - won't copy functions, Dates, undefineds & many more.

const obj = {
  name: 'alpha',
  printName: function() {
    console.log(this.name);
  }
};

console.log(JSON.parse(JSON.stringify(obj))); // function not copied
  • _.cloneDeep(object) - It is a good option but requires lodash.

const obj = {
  name: 'alpha',
  printName: function() {
    console.log(this.name);
  }
};

filteredArray = _.cloneDeep(obj);
console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.2.1/lodash.min.js"></script>
  • structuredClone(object) - Browser Native API (It is good to use as JSON.parse() and JSON.stringify() do not serialize the circular object or things like Map, Set, Date, RegEx etc.)

const a = { x: 20, date: new Date() };
a.c = a;

console.log(structuredClone(a)); // { x: 20, date: <date object>, c: <circular ref> }

console.log(JSON.parse(JSON.stringify(a))); // throwing a TypeError
Oconnor answered 9/5, 2022 at 11:23 Comment(0)
L
3

structuredClone now is supported by most the browsers

its main limitation is about DONT coping functions. It would require some extra work to copy/move it manually.

We can at least copy classes in an easy way by adding the prototypes later

const proto = Object.getPrototypeOf(object)
const newObject = structuredClone(object)
Object.setPrototypeOf(newObject, proto)
Lycaon answered 19/10, 2022 at 16:11 Comment(0)
A
2

There should be no real world need for such a function anymore. This is mere academic interest.

As purely an exercise, this is a more functional way of doing it. It's an extension of @tfmontague's answer as I'd suggested adding a guard block there. But seeing as I feel compelled to ES6 and functionalise all the things, here's my pimped version. It complicates the logic as you have to map over the array and reduce over the object, but it avoids any mutations.

const cloner = (x) => {
    const recurseObj = x => (typeof x === 'object') ? cloner(x) : x
    const cloneObj = (y, k) => {
        y[k] = recurseObj(x[k])
        return y
    }
    // Guard blocks
    // Add extra for Date / RegExp if you want
    if (!x) {
        return x
    }
    if (Array.isArray(x)) {
        return x.map(recurseObj)
    }
    return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
    null,
    [],
    {},
    [1,2,3],
    [1,2,3, null],
    [1,2,3, null, {}],
    [new Date('2001-01-01')], // FAIL doesn't work with Date
    {x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
    {
        obj : new function() {
            this.name = "Object test";
        }
    } // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))
Anastomosis answered 22/4, 2018 at 17:12 Comment(0)
D
2

my addition to all the answers

function deepCopy(arr) {
  if (typeof arr !== 'object') return arr
  if (Array.isArray(arr)) return [...arr].map(deepCopy)
  for (const prop in arr) 
    copy[prop] = deepCopy(arr[prop])
  return copy
}
Dispenser answered 21/12, 2018 at 2:28 Comment(0)
A
2

My solution, deep clones objects, arrays and functions.

let superClone = (object) => {
  let cloning = {};

  Object.keys(object).map(prop => {
     if(Array.isArray(object[prop])) {
        cloning[prop] = [].concat(object[prop])
    } else if(typeof  object[prop] === 'object') {
      cloning[prop] = superClone(object[prop])
    } else cloning[prop] = object[prop]
  })

  return cloning
}

example

let obj = {
  a: 'a',
  b: 'b',
  c: {
    deep: 'try and copy me',
    d: {
      deeper: 'try me again',
      callDeeper() {
       return this.deeper
     }
    },
    arr: [1, 2, 3]
  },
  hi() {
    return this.a
  }
};


const cloned = superClone(obj)
obj.a = 'A' 
obj.c.deep = 'i changed'
obj.c.arr = [45,454]
obj.c.d.deeper = 'i changed'

console.log(cloned) // unchanged object

If your objects contain methods don't use JSON to deep clone, JSON deep cloning doesn't clone methods.

If you take a look at this, object person2 only clones the name, not person1's greet method.


const person1 = {
  name: 'John',
  greet() {
    return `HI, ${this.name}`
  }
}
 
const person2 = JSON.parse(JSON.stringify(person1))
 
console.log(person2)  // { name: 'John' }

Apiculate answered 6/12, 2020 at 2:39 Comment(0)
M
1

I noticed that Map should require special treatment, thus with all suggestions in this thread, code will be:

function deepClone( obj ) {
    if( !obj || true == obj ) //this also handles boolean as true and false
        return obj;
    var objType = typeof( obj );
    if( "number" == objType || "string" == objType ) // add your immutables here
        return obj;
    var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
    if( obj instanceof Map )
        for( var key of obj.keys() )
            result.set( key, deepClone( obj.get( key ) ) );
    for( var key in obj )
        if( obj.hasOwnProperty( key ) )
            result[key] = deepClone( obj[ key ] );
    return result;
}
Magnien answered 22/9, 2016 at 15:31 Comment(0)
M
1

This works for arrays, objects and primitives. Doubly recursive algorithm that switches between two traversal methods:

const deepClone = (objOrArray) => {

  const copyArray = (arr) => {
    let arrayResult = [];
    arr.forEach(el => {
        arrayResult.push(cloneObjOrArray(el));
    });
    return arrayResult;
  }

  const copyObj = (obj) => {
    let objResult = {};
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        objResult[key] = cloneObjOrArray(obj[key]);
      }
    }
    return objResult;
  }

  const cloneObjOrArray = (el) => {
    if (Array.isArray(el)) {
      return copyArray(el);
    } else if (typeof el === 'object') {
      return copyObj(el);
    } else {
      return el;
    }
  }

  return cloneObjOrArray(objOrArray);
}
Monosyllable answered 20/12, 2017 at 8:26 Comment(0)
F
0

We can utilize recursion for making deepCopy. It can create copy of array, object, array of object, object with function. if you want, you can add function for other type of data structure like map etc.

function deepClone(obj) {
         var retObj;
        _assignProps = function(obj, keyIndex, retObj) {
               var subType = Object.prototype.toString.call(obj[keyIndex]);
               if(subType === "[object Object]" || subType === "[object Array]") {
                    retObj[keyIndex] = deepClone(obj[keyIndex]);
               }
               else {
                     retObj[keyIndex] = obj[keyIndex];
               }
        };

        if(Object.prototype.toString.call(obj) === "[object Object]") {
           retObj = {};
           for(key in obj) {
               this._assignProps(obj, key, retObj);
           }
        }
        else if(Object.prototype.toString.call(obj) == "[object Array]") {
           retObj = [];
           for(var i = 0; i< obj.length; i++) {
              this._assignProps(obj, i, retObj);
            }
        };

        return retObj;
    };
Fringe answered 10/3, 2018 at 20:48 Comment(0)
E
0

Use immutableJS

import { fromJS } from 'immutable';

// An object we want to clone
let objA = { 
   a: { deep: 'value1', moreDeep: {key: 'value2'} } 
};

let immB = fromJS(objA); // Create immutable Map
let objB = immB.toJS(); // Convert to plain JS object

console.log(objA); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
console.log(objB); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }

// objA and objB are equalent, but now they and their inner objects are undependent
console.log(objA === objB); // false
console.log(objA.a === objB.a); // false
console.log(objA.moreDeep === objB.moreDeep); // false

Or lodash/merge

import merge from 'lodash/merge'

var objA = {
    a: [{ 'b': 2 }, { 'd': 4 }]
};
// New deeply cloned object:
merge({}, objA ); 

// We can also create new object from several objects by deep merge:
var objB = {
    a: [{ 'c': 3 }, { 'e': 5 }]
};
merge({}, objA , objB ); // Object { a: [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
Ebeneser answered 15/3, 2019 at 12:23 Comment(0)
H
0

This one, using circular reference, works for me

 //a test-object with circular reference :
 var n1 = {   id:0,   text:"aaaaa",   parent:undefined} 
 var n2 = {  id:1,   text:"zzzzz",   parent:undefined } 
 var o = { arr:[n1,n2],   parent:undefined } 
 n1.parent = n2.parent = o;
 var obj = {   a:1,   b:2,   o:o }
 o.parent = obj;

 function deepClone(o,output){ 

     if(!output) output = {};  
     if(o.______clone) return o.______clone;
     o.______clone = output.______clone = output;

   for(var z in o){

     var obj = o[z];
     if(typeof(obj) == "object") output[z] = deepClone(obj)
     else output[z] = obj; 
    }

   return output;
}

console.log(deepClone(obj));
Halverson answered 16/7, 2019 at 16:37 Comment(0)
D
0

var newDate = new Date(this.oldDate); I was passing oldDate to function and generating newDate from this.oldDate, but it was changing this.oldDate also.So i used that solution and it worked.

Dupre answered 13/3, 2020 at 9:2 Comment(0)
V
0

This solution will avoid recursion problems when using [...target] or {...target}

function shallowClone(target) {
  if (typeof a == 'array') return [...target]
  if (typeof a == 'object') return {...target}
  return target
}

/* set skipRecursion to avoid throwing an exception on recursive references */
/* no need to specify refs, or path -- they are used interally */
function deepClone(target, skipRecursion, refs, path) {
  if (!refs) refs = []
  if (!path) path = ''
  if (refs.indexOf(target) > -1) {
    if (skipRecursion) return null
    throw('Recursive reference at ' + path)
  }
  refs.push(target)
  let clone = shallowCopy(target)
  for (i in target) target[i] = deepClone(target, refs, path + '.' + i)
  return clone
}
Volley answered 5/4, 2020 at 1:50 Comment(0)
L
0

Hello i just wanted to post my answer since i think its more readable. Note:this doesnt cover classes since i dont use them but you can easily add a condition for that

/** Copies any type of object/array of objects 
 * @param obj The object to be copied
 * @param customKeys A list of keys that are to be excluded from deepCopy (optional)
*/
export function deepCopyObject(obj: any, customKeys?: Array<string|number|symbol>) {
    if (obj == undefined)
        return;
    if (typeof obj !== 'object')
        return obj;
    if (typeof obj === 'function')
        return obj;
    
    const isArray = obj.length > -1;
    if (isArray)
        return copyArray(obj);
    
    const isObjectDate = obj instanceof Date;
    if(isObjectDate)
        return new Date(obj);
    
    const isDOM = obj.nodeType && typeof obj.cloneNode == "function";
    if (isDOM)
        return obj.cloneNode(true);
    
    const isHtmlComponent = obj.$$typeof != undefined; // you can pass html/react components and maybe setup a custom function to copy them
    if (isHtmlComponent)
        return obj;

    const newObject = <typeof obj>{};
    const keys = Object.keys(obj);
    keys.forEach((key: keyof (typeof obj)) => {
        newObject[key] = copyKeysOfTypeObject(obj, key, customKeys);
    })

    const cantAccessObjectKeys = keys.lenght ==0; // ex: window.navigator
    if (cantAccessObjectKeys)
        return obj;
    
    return newObject
}

function copyArray(arr: any) {
    const newArr = new Array(0);
    arr.forEach((obj: any) => {
        newArr.push(deepCopyObject(obj));
    })
    return newArr;
}

function copyKeysOfTypeObject(obj: any, key: string | number | symbol, customKeys?: Array<string | number | symbol>) {
    if (!key)
        return;
    if (customKeys && customKeys.includes(key))
        return obj[key];
    return deepCopyObject(obj[key]);
}
Lean answered 17/1, 2022 at 20:5 Comment(0)
R
0
let obj1 = {
    a: 100,
    b: {
        c: 200,
        d: [1, { f: 5 }, 3],
        e: () => {}
    }
}

function deepClone(obj) {
    let newObj = {};

    for (let key in obj) {
        let val = obj[key];

        if (val instanceof Array) {
            //newObj[key] = [...val]
          newObj[key] = [];
          val.forEach((value, index) => {
            newObj[key][index] = (typeof value === 'object' ? deepClone(value) : value);
          });
          
        } else if (val instanceof Date) {
            newObj[key] = new Date(val)
        } else if (typeof val === 'object') {
            newObj[key] = deepClone(val)
        } else {
            newObj[key] = val;
        }
    }
    return newObj;
}

obj2 = deepClone(obj1);

obj1.b.d[1].f = 2;

console.log(obj1);
console.log(obj2);
Respond answered 26/6, 2022 at 19:24 Comment(3)
newObj[key] = [...val] This will shallow clone an array of objectsGrogshop
@SurajRao thanks for pointing that out. Made few line of changes to handle nested array elements as well.Respond
True. it will work for nested arrays. It might trip for other objects like Date though...Grogshop
U
0

Simple variant. Does not cover all cases.

let deepClone = function(obj) {
    let newObj = {...obj};
    for(property in newObj) {
        if(typeof newObj[property] === "object") {
            newObj[property] = deepClone(newObj[property]);
        }
    }
    return newObj;
}
Unideaed answered 29/9, 2023 at 9:40 Comment(0)
C
0

For the arrays and objects which contain array/objects with primitives as fields values is possible to use JSON.parse(JSON.stringify(structure)) as posted many times here.

but for modern fields values as React components it will not work, because JSON.parse conversion just ruin sensitive content of React object which contains many special characters.

In this case good to use lodash/cloneDeep (not _.cloneDeep, because it's spoiling tree shaking)

Castlereagh answered 17/12, 2023 at 18:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.