3701

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?

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
soundly_typed
  • 39,257
  • 5
  • 28
  • 36
  • 34
    See this question: http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object – Niyaz Jun 21 '11 at 10:13
  • Definitely support @Niyaz! Shortlink: http://tinyurl.com/JSCopyObject – chiccodoro Sep 26 '11 at 14:56
  • 288
    For JSON, I use `mObj=JSON.parse(JSON.stringify(jsonObject));` – Lord Loh. Feb 02 '13 at 10:09
  • 78
    I really don't get why no one suggests `Object.create(o)`, it does everything the author asks? – froginvasion Aug 08 '14 at 15:23
  • 5
    @froginvasion Probably because it's not supported in IE8 and under. – WynandB Sep 08 '14 at 04:07
  • 4
    @Wynand That's not a good argument at all. There are lots of polyfills for `Object.create` that can be found, as for instance: https://www.refheap.com/90006 – froginvasion Sep 09 '14 at 07:08
  • 1
    @froginvasion Fair enough, although I could argue that OP wanted an "elegant" [_sic_] way and not everyone would necessarily agree that using a polyfill fits that description. I was just trying to provide you with _a_ possible reason why `Object.create` hasn't been suggested (if that's the case), which is why I wrote "Probably". Regardless, I'm in favor of polyfills and recently looked at [this](http://qr.net/HZCC) one incidentally, which is similar to your snippet. If you're after better feedback you could also consider adding your suggestion as an answer. – WynandB Sep 09 '14 at 14:07
  • 2
    Object.create works great if you want to copy your object's prototype. – BTC Jun 26 '15 at 15:55
  • 59
    ```var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;``` After doing this, `y.deep.key` will also be 2, hence Object.create CAN NOT BE USED for cloning... – Ruben Stolk Jul 04 '15 at 15:04
  • 1
    @froginvasion - because it doesn't work even for flat objects? (Chrome 45). You just get `__proto__`, not a clone... – Matt Sep 20 '15 at 17:31
  • 2
    `Object.create` does not get you `__proto__`, it gets you a new object, with it's prototype pointing to the argument, thus creating a chain. I agree that it is maybe not a real 'clone' in the traditional sense, but depending on the situation `Object.create` is much more consistent than using various cloning techniques used below. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create – froginvasion Sep 22 '15 at 08:13
  • 1
    how about `var newObj = (function(){ return oldObj; }());` – r3wt Jan 28 '16 at 20:42
  • 23
    @r3wt that will not work... Please post only after doing basic test of the solution.. –  Feb 16 '16 at 18:54
  • 2
    here a **benchmark** of different solutions: http://jsben.ch/#/bWfk9 – EscapeNetscape Nov 14 '16 at 16:03
  • @RubenStolk why OP cannot store object into new variable ? like `var copyobject=object `?? – Mahi Nov 24 '16 at 12:42
  • 4
    @Mahi That does not copy the object. It creates a new variable and makes it point to the same object. – soundly_typed Nov 28 '16 at 19:56
  • @LordLoh. what if you're in a javascripting environment where you can't import libraries like JSON. Apple's Quartz Composer Editor for example, not sure about Adobe CC scripting. – wide_eyed_pupil Dec 30 '16 at 16:18
  • 1
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign - just tried this on the chrome developer terminal - `oj1={}; oj1.p1=222; oj2={}; Object.assign(oj2,oj1); oj2.p1=555; oj1.p1;` - this will show `222` Also answered here : http://stackoverflow.com/a/36177142/482176 – Lord Loh. Dec 31 '16 at 00:03
  • @LordLoh. Seems like I can call JSON.parse and JSON.stringify after all inside QC. Core functions after all. Also i did find this polyfill on the reference page and links to more advanced polyfills JSON2 and JSON3: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON – wide_eyed_pupil Dec 31 '16 at 01:45
  • 3
    `{...original}` – OverCoder Feb 08 '17 at 18:57
  • 6
    If you are suggesting Object.create() then you know very little about JS. Object.create will not clone the object X. Y = Object.create(X) does this and nothing more: Creates a new object Y with X as its prototype. The object X will become the prototype of Object Y. That is not what you want. At all. Because X's methods and data are not copied to Y, they are referenced, Y holds a reference to X's data and methods. How wrong can you be!? – Alexander Mills Mar 15 '17 at 06:17
  • Recently answered it over [here](https://stackoverflow.com/a/53442750/4442322) – Muhammad Ali Jan 27 '19 at 20:01
  • See [developer.mozilla.org's JavaScript/Reference of assign#Deep_Clone](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone). – Peter Krauss Feb 17 '19 at 09:53
  • This should create a copy: `let y = {...x};` – SwiftNinjaPro Jan 01 '20 at 04:55
  • you can use clone method of lodash, or use spread operator let x = {} let y = {some value} x = {...x, ...y} – Geetanshu Gulati Jan 24 '20 at 13:48
  • https://stackoverflow.com/questions/38416020/deep-copy-in-es6-using-the-spread-syntax/72191043#72191043 – Abolfazl Roshanzamir Jul 03 '22 at 07:13

81 Answers81

1911

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.

mrienstra
  • 508
  • 5
  • 7
A. Levy
  • 29,056
  • 6
  • 39
  • 56
  • This is missing symbol keys and symbol values. Nowadays, using `Object.getOwnPropertyDescriptors` is better. – Sebastian Simon Jul 10 '21 at 15:33
  • **structuredClone** is [only 75% compatible globally](https://caniuse.com/?search=structuredClone) – JoshuaDavid May 17 '22 at 21:27
  • In Nodejs, `structuredClone(object)` works for node v17.0.0 and later. – Nikhil Sinha Jun 11 '22 at 07:08
  • 2
    Update on @JoshuaDavid, currently supported in 82.57% of all browsers. – Kay Angevare Jun 15 '22 at 14:23
  • If you want to use `structuredClone(object)` you need to install the node version 17.0.29 at least, you can use this command `npm i --save-dev @types/node@17.0.29` – DariusV Aug 07 '22 at 17:53
  • 2
    what if I want to `clone` the `object` and also want to `append` something to it? – Azhar Uddin Sheikh Sep 06 '22 at 14:38
  • 1
    Ran into issues using this function on mobile devices.. - iOS 11,12,13,14, not working on Safari and Google Chrome. (works on iOS 15,16) - Android 12, 11, 10, 9 on SamsungInternet browser (chrome works). – jfxninja Sep 21 '22 at 14:49
  • Sadly it does not clone symbols, even unique ones not (where it could just make a new reference to them). :-( – Mitar Oct 04 '22 at 09:31
  • Please note that `structuredClone` cannot clone `Proxy` objects, you must clone the raw object: `const b = structuredClone(toRaw(a.value));` – Eli Zatlawy May 15 '23 at 13:17
1151

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.

TOPKAT
  • 6,667
  • 2
  • 44
  • 72
heinob
  • 19,127
  • 5
  • 41
  • 61
  • 1
    sometimes the best answers are the simplest. genius. – dewd Nov 23 '21 at 16:30
  • Helpful, but when comparing objects which contains other objects, I ran through unexpected behaviour when two exactly equal objects where not taken as equals. used JSON.stringify(x) == JSON.stringify(JSON.parse(JSON.stringify(a))) to fix it. For some reason, comparing as strings works perfectly as expected when comparing, could not match otherwise. – Agustin L. Lacuara Apr 28 '22 at 13:36
  • @AgustinL.Lacuara You cannot compare complex datatypes in JS. `a={};b={}; a==b` is `false`. But after `a=b` it becomes `true`, because it is not only identical but it is the same object. – heinob Apr 28 '22 at 18:47
  • 1
    does the job, but, this is goes against any good programming practice. In brazil, we call that a 'Gambiarra' – Diego Favero Jul 05 '22 at 19:03
831

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.

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
Vitalii Fedorenko
  • 110,878
  • 29
  • 149
  • 111
813

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)
buræquete
  • 14,226
  • 4
  • 44
  • 89
Pascal
  • 8,869
  • 2
  • 17
  • 11
328

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.

Efren
  • 4,003
  • 4
  • 33
  • 75
Tareq
  • 5,283
  • 2
  • 15
  • 18
  • The problem happens when you have functions in your object JSON.parse(JSON.stringify(a)) – Tosh Jul 29 '22 at 12:20
149

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;
    }
  });
}
Community
  • 1
  • 1
Eugene Tiurin
  • 4,019
  • 4
  • 33
  • 32
143

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:

  1. Where such inheritance is useful (duh!)
  2. 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.

itpastorn
  • 2,935
  • 1
  • 22
  • 24
  • 116
    This is prototypal inheritance, not cloning. These are completely different things. The new object doesn't have any of it's own properties, it just points to the prototype's properties. The point of cloning is to create a fresh new object that doesn't reference any properties in another object. – d13 Jan 16 '14 at 16:18
100

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

tgogos
  • 23,218
  • 20
  • 96
  • 128
Fabio Poloni
  • 8,219
  • 5
  • 44
  • 74
  • 12
    so the conlcusion is to avoid that problem :) – mikus Oct 23 '14 at 11:39
  • @mikus until there's a *real* specification which covers more than just the basic use cases, yes. – Fabio Poloni Oct 23 '14 at 14:41
  • 2
    An okay analysis of the solutions provided above but the conclusion drawn by the author indicates that there is no solution to this question. – Amir Mog Aug 16 '16 at 16:53
  • 2
    It is shame that JS not includes native clone function. – l00k Nov 14 '16 at 14:53
  • 1
    Among all the top answers, I feel this is close to the correct one. – KTU May 15 '17 at 04:24
  • I'm using `the better (but not perfect) solution` and when working with [Sets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set), an additional if must be added after: `if (o instanceof Array) { /*...*/ }`: `else if (o instanceof Set) { result = new Set(); for (var i of o) { result.add(i); } }` – watery May 30 '18 at 08:49
80

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);
Nadeem Yasin
  • 4,493
  • 3
  • 32
  • 41
dule
  • 17,798
  • 4
  • 39
  • 38
  • 2
    Thanks. Using this technique on a Meteor server. – Turbo Apr 01 '15 at 04:30
  • To get started quickly with lodash, I'd recommend learning npm, Browserify, as well as lodash. I got clone to work with 'npm i --save lodash.clone' and then 'var clone = require('lodash.clone');' To get require to work, you need something like browserify. Once you install it and learn how it works, you’ll use 'browserify yourfile.js > bundle.js;start chrome index.html' every time you run your code (instead of going into Chrome directly). This gathers your file and all the files you required from the npm module into bundle.js. You can probably save time and automate this step with Gulp though. –  Apr 09 '19 at 19:43
77

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
Alireza
  • 100,211
  • 27
  • 269
  • 172
  • Can you please give example of what `big chunk of data` would equate to? 100kb? 100MB? Thanks! – user1063287 Sep 14 '18 at 11:04
  • Yes, @user1063287, that basically the bigger data, the performnce worse... so it really depends, not a kb, mb or gb, it's more about how many times you wanna do that also... Also it won't work for functions and other stuffs... – Alireza Mar 14 '19 at 09:58
  • 3
    `Object.assign` makes a shallow copy (just as the spread, @Alizera) – Bogdan D May 29 '19 at 13:42
  • You can't use let in es5 :^) @Alireza – Womble May 01 '20 at 14:56
60

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.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Mohsen Alyafei
  • 4,765
  • 3
  • 30
  • 42
  • And this adds what new/unique information to this question? – Andreas Jul 06 '20 at 08:24
  • 10
    The `JSON` approach would remove any methods of the object – Andreas Jul 06 '20 at 08:25
  • 3
    To create a string from an object and then parsing that string into another object just to copy the object is a kind of Monty Python's style of programming :-D – Jaromír Adamec Jan 10 '21 at 14:24
  • 1
    This only works for object literals and objects that can be represented as such, but *not* for generic "objects" like you encounter in OO languages. This seems to be what the OP asked for thus that's OK, but it' not a universal solution for every kind of object. – bart Jan 31 '21 at 12:34
  • 3
    The spread operator and Object.assign fail for objects with a hierarchy, ie. nested objects. JSON.parse/stringify works, but as mentioned does not copy methods. – iCode Mar 31 '21 at 17:40
  • the spread syntax is syntactical sugar (for Object.assign in this case). this answer is misleading. – – Tchakabam Jun 04 '21 at 10:31
  • The spread operator still copies the values by reference. – user3348949 Aug 11 '22 at 17:49
47

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
Tim
  • 1,606
  • 2
  • 21
  • 42
Kris Walker
  • 897
  • 1
  • 8
  • 14
  • Why don't functions belong to JSON? I've seen them transfered as JSON more then once... – the_drow Oct 29 '09 at 20:37
  • 6
    Functions are not part of the JSON spec becuase they are not a secure (or smart) way to transfer data, which is what JSON was made for. I know the native JSON encoder in Firefox simply ignores functions passed to it, but I'm not sure about the behavior of others. – Kris Walker Oct 30 '09 at 10:27
  • 1
    @mark: `{ 'foo': function() { return 1; } }` is a literal-constructed object. – abarnert Aug 14 '12 at 01:58
  • @abarnert functions are not data. "Function literals" is a misnomer - since functions can contain arbitrary code, including assignments and all sort of "non-serializable" things. – deprecated Apr 30 '13 at 10:38
42

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

musemind
  • 1,027
  • 9
  • 8
39
const objClone = { ...obj };

Be aware that nested objects are still copied as a reference.

Pavan Garre
  • 2,661
  • 1
  • 18
  • 19
  • 2
    Thanks for the hint that nested objects are still copied as a reference! I almost got crazy when debugging my code because I modified nested properties on the "clone" but the original got modified. – Benny Code Jan 02 '19 at 23:32
  • This is ES2016, not 2018, and this answer was given [two years earlier](https://stackoverflow.com/a/41183504/1269037). – Dan Dascalescu Jun 13 '19 at 23:37
  • so what if i want copy of nested property as well – Sunil Garg Sep 04 '19 at 08:12
  • 2
    @SunilGarg To copy nested property as well you can use `const objDeepClone = JSON.parse(JSON.stringify(obj));` – Pavan Garre Sep 06 '19 at 13:45
30

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;
};
Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
Calvin
  • 4,559
  • 1
  • 25
  • 23
  • 4
    This is close, but doesn't work for any object. Try cloning a Date object with this. Not all properties are enumerable, so they will not all show up in the for/in loop. – A. Levy Apr 08 '09 at 04:17
  • Adding to the object prototype like this broke jQuery for me. Even when I renamed to clone2. – iPadDeveloper2011 Aug 27 '12 at 23:19
  • 3
    @iPadDeveloper2011 The code above had a bug in it where it created a global variable called 'i' '(for i in this)', rather than '(for var i in this)'. I have enough karma to edit and it and fix it so I did. – mikemaccana Sep 22 '12 at 22:09
  • 2
    @Calvin: this should be created an a non-enumerable property, otherwise 'clone' will appear in 'for' loops. – mikemaccana Oct 01 '12 at 10:54
  • 3
    why isn't `var copiedObj = Object.create(obj);` a great way as well? – Dan P. Apr 12 '14 at 20:16
  • @Dany What if the cloned object had a property called `info` and the `info` object has properties, including a `weight` property. Then assigning `myclone.info.weight` would modify the prototype, because it *reads* `info` from the prototype then assigns to the `weight` property of that. – doug65536 Feb 29 '16 at 20:51
29

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...

Lukas Jelinek
  • 2,337
  • 1
  • 19
  • 12
27
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;
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
picardo
  • 24,530
  • 33
  • 104
  • 151
  • 11
    This answer is pretty close, but not quite correct. If you try cloning a Date object, you will not get the same date because the call to the Date constructor function initializes the new Date with the current date/time. That value isn't enumerable and won't be copied by the for/in loop. – A. Levy Apr 08 '09 at 04:21
  • Not perfect, but nice for those basic cases. E.g. allowing simple cloning of an argument that can be a basic Object, Array or String. – james_womack Nov 01 '13 at 20:36
  • Upvoted for correctly calling the constructor using `new`. The accepted answer does not. – GetFree Jun 14 '15 at 06:29
  • works on node everything else ! still left reference links – user956584 Jul 15 '17 at 22:38
  • The recursive thought is great.But If the value is array,it will work? – Q10Viking Dec 30 '19 at 11:32
26

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.

Jan Turoň
  • 31,451
  • 23
  • 125
  • 169
25

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

enter image description here

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

enter image description here

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

enter image description here

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
24

Using Lodash:

var y = _.clone(x, true);
VaZaA
  • 398
  • 3
  • 8
23

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/

João Oliveira
  • 487
  • 7
  • 13
  • 12
    It does not deep clone the object. – August Jun 01 '17 at 05:31
  • That's an assignment, not a copy. clone.Title = "just a clone" means that obj.Title = "just a clone". – HoldOffHunger Aug 16 '17 at 16:05
  • @HoldOffHunger You are mistaken. Check it in your browser's JS console ( `let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";`) – collapsar Sep 01 '17 at 11:00
  • @collapsar: That is precisely what I checked, then console.log(person) will be "Whazzup", not "Thor Odinson". See August's comment. – HoldOffHunger Sep 01 '17 at 11:40
  • 1
    @HoldOffHunger Does not happen in Chrome 60.0.3112.113 nor in Edge 14.14393; August's comment does not apply as the values of primitive types of `obj`'s properties are indeed cloned. Property values that are Objects themselves will not be cloned. – collapsar Sep 01 '17 at 12:50
  • Why did you feel the need to post [yet](https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object/30042948#30042948) another [Object.assign](https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object/40470792#40470792) answer to a question that already had a ton of answers? You answer doesn't help anyone; it only clutters the page. Please consider deleting it - you'll get a [special badge](https://meta.stackexchange.com/questions/7609/what-is-the-purpose-of-the-disciplined-badge)! – Dan Dascalescu Jun 13 '19 at 23:35
21

Interested in cloning simple objects:

JSON.parse(JSON.stringify(json_original));

Source : How to copy JavaScript object to new variable NOT by reference?

bukart
  • 4,906
  • 2
  • 21
  • 40
Mohammed Akdim
  • 2,223
  • 1
  • 13
  • 21
19

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.

Christopher Peisert
  • 21,862
  • 3
  • 86
  • 117
Jeremy
  • 1
  • 85
  • 340
  • 366
18
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.

flori
  • 14,339
  • 4
  • 56
  • 63
  • How this is different from `let cloned = Object.assign({}, obj)` ? – ceztko May 21 '19 at 11:25
  • @ceztko When `obj` is a class instance, `Object.assign()` does not clone e.g. class methods (because they are not enumerable). – flori Jan 02 '21 at 19:58
17

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();
    };
}
Community
  • 1
  • 1
Rob Evans
  • 6,750
  • 4
  • 39
  • 56
  • 1
    +1 `Object.create(...)` seems definitely the way to go. – René Nyffenegger Jun 30 '14 at 14:49
  • Perfect answer. Maybe you could add an explanation for `Object.hasOwnProperty`? That way people know how to prevent searching the prototype link. – froginvasion Aug 09 '14 at 12:30
  • Works well but what browsers does the polyfill work in? – Ian Lunn Oct 09 '14 at 13:25
  • If there are objects or arrays in the original obj1, and they change the objects or arrays in the new obj2, it will be modified obj1's copies because they're stored by reference. Example: ` var obj1 = { thing: {} }; var obj2 = Object.create(obj1); obj2.thing.innerThing = 'test'; console.log(obj1) ` The output will be that obj1 and obj2 are now `{ thing: { innerThing: "test" } }` – Quinthexadec Oct 17 '14 at 04:32
  • 12
    This is creating obj2 with a obj1 as it's prototype. It only works because you are shadowing the `text` member in obj2. You are not making a copy, just deferring to the prototype chain when a member is not found on obj2. – Nick Desaulniers Oct 31 '14 at 21:25
  • @Quinthexadec setting `obj2`'s properties creates "own" properties on `obj2`' and does not modify `obj1`'s. – sam Aug 22 '15 at 18:02
  • 2
    This does NOT create it "without references", it just moves the reference to the prototype. It's still a reference. If a property changes in the original so will the prototype property in the "clone". It's not a clone at all. – Jimbo Jonny May 09 '16 at 00:01
15

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.

Charles Merriam
  • 19,908
  • 6
  • 73
  • 83
  • 2
    it doesn't work when you have functions defined on objects – Petr Marek Feb 05 '17 at 22:06
  • as far as I see spread operator only works with iterables - [developer.mozilla.org](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator) says: `var obj = {'key1': 'value1'};` `var array = [...obj]; // TypeError: obj is not iterable` – Oleh Apr 04 '17 at 08:12
  • @Oleh so use ` {... obj} instead of [...obj];` – manikant gautam Dec 05 '17 at 06:17
  • @manikantgautam I was using Object.assign() before, but now indeed object spread syntax is supported in latest Chrome, Firefox (still not in Edge and Safari). Its ECMAScript proposal... but Babel does support it as far as I can see, so probably its safe to use. – Oleh Dec 06 '17 at 15:11
14

I think there is a simple and working answer. In deep copying there are two concerns:

  1. Keep properties independent to each other.
  2. 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.

ConductedClever
  • 4,175
  • 2
  • 35
  • 69
14

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}}
Jesse Reza Khorasanee
  • 3,140
  • 4
  • 36
  • 53
Nishant Dwivedi
  • 380
  • 3
  • 8
11

(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

  1. Object.create() | MDN
  2. Object.defineProperties() | MDN
  3. Enumerability and ownership of properties | MDN
  4. TypeError: cyclic object value | MDN

Language tricks used

  1. Conditionally add prop to object
ooo
  • 261
  • 4
  • 10
  • Since `Symbol("a") === Symbol("a")` is `false`, shouldn’t `clone(Symbol("a"))` use `Symbol(object.description)` to create a new symbol? Or would this have too weird of an impact on well-known symbols? – Sebastian Simon Jul 10 '21 at 15:39
  • @SebastianSimon Your consideration is very comprehensive! And your last sentence is ***more* correct**, e.g. `(new Map)[Symbol.iterator]` vs `(new Map)[Symbol(Symbol.iterator.description)]`. – ooo Jun 01 '22 at 05:31
10

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}}}

Shallow Copy: lodash _.clone()

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}}}

Deep Copy: lodash _.cloneDeep()

Ashok R
  • 19,892
  • 8
  • 68
  • 68
9

The most correct to copy object is use Object.create:

Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

Such notation will make identically the same object with correct prototype and hidden properties.

rela589n
  • 817
  • 1
  • 9
  • 19
  • yes but depends what you want (you might want to plain "copy" the prop _values_, *not* the actual prop descriptor refs...), and depending on the source obj you might need an `Object.assign` on top of this to get enumarable properties in as well (i.e plain old key-value pairs set on the object, unrelated to prototype and "dynamic" described props. – Tchakabam Jun 04 '21 at 10:27
8

This is an adaptation of A. Levy's code to also handle the cloning of functions and multiple/cyclic references - what this means is that if two properties in the tree which is cloned are references of the same object, the cloned object tree will have these properties point to one and the same clone of the referenced object. This also solves the case of cyclic dependencies which, if left unhandled, leads to an infinite loop. The complexity of the algorithm is O(n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

Some quick tests

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
Radu Simionescu
  • 4,518
  • 1
  • 35
  • 34
6

I just wanted to add to all the Object.create solutions in this post, that this does not work in the desired way with nodejs.

In Firefox the result of

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

is

{test:"test"}.

In nodejs it is

{}
heinob
  • 19,127
  • 5
  • 41
  • 61
  • This is prototypal inheritance, not cloning. – d13 Jan 16 '14 at 16:15
  • 1
    @d13 while your argument is valid, note that there is no standardized way in JavaScript to clone an object. This is prototypical inheritance, but it can be used as clones nevertheless if you understand the concepts. – froginvasion Aug 08 '14 at 15:28
  • @froginvasion. The only problem with using Object.create is that nested objects and arrays are just pointer references to the prototype's nested objects and arrays. http://jsbin.com/EKivInO/2/edit?js,console. Technically a "cloned" object should have its own unique properties that are not shared references to properties on other objects. – d13 Aug 08 '14 at 18:28
  • @d13 okay, I see your point now. But what I meant is that too many people are alienated with the concept of prototypical inheritance, and to me fail to learn how it works. If I'm not mistaken, your example can be fixed by just calling `Object.hasOwnProperty` to check whether you own the array or not. Yes this does add additional complexity to deal with prototypical inheritance. – froginvasion Aug 09 '14 at 10:13
6
function clone(src, deep) {

    var toString = Object.prototype.toString;
    if(!src && typeof src != "object"){
        //any non-object ( Boolean, String, Number ), null, undefined, NaN
        return src;
    }

    //Honor native/custom clone methods
    if(src.clone && toString.call(src.clone) == "[object Function]"){
        return src.clone(deep);
    }

    //DOM Elements
    if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
        return src.cloneNode(deep);
    }

    //Date
    if(toString.call(src) == "[object Date]"){
        return new Date(src.getTime());
    }

    //RegExp
    if(toString.call(src) == "[object RegExp]"){
        return new RegExp(src);
    }

    //Function
    if(toString.call(src) == "[object Function]"){
        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });

    }

    var ret, index;
    //Array
    if(toString.call(src) == "[object Array]"){
        //[].slice(0) would soft clone
        ret = src.slice();
        if(deep){
            index = ret.length;
            while(index--){
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }

    return ret;
};
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
user1547016
  • 79
  • 2
  • 2
6

Since mindeavor stated that the object to be cloned is a 'literal-constructed' object, a solution might be to simply generate the object multiple times rather than cloning an instance of the object:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
Community
  • 1
  • 1
Bert Regelink
  • 2,696
  • 23
  • 17
6

I've written my own implementation. Not sure if it counts as a better solution:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

Following is the implementation:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}
e-cloud
  • 4,331
  • 1
  • 23
  • 37
yazjisuhail
  • 357
  • 3
  • 6
6

Native JS:

const shallowClone = {...originalObj};
const deepClone = JSON.parse(JSON.stringify(originalObj));

Using Libraries:

// Lodash
const shallowClone = _.clone(originalObj);
const deepClone = _. cloneDeep(originalObj);

// JQuery
const shallowClone = jQuery.extend({}, originalObj);
const deepClone = jQuery.extend(true, {}, originalObj);

// Angular
const deepClone = angular.copy(originalObj);
chickens
  • 19,976
  • 6
  • 58
  • 55
6

I've had an issue when copying objects. This is because when you do following, you're only making a 'reference' to the object and when the source object value is updated later, the copy object that was cloned also changes value because it was merely a 'reference' and hence you see multiple values of the last changes to the source object.

let x = { a: 1 };
let y = x; // y is a reference to x, so if x changes y also changes and v/v

So, to tackle this issue you do the following:

let y = JSON.parse(JSON.stringify(x)); //see Note below

The other way to prevent references is by doing the following:

let x = { a: 1 };
let y = Object.assign({}, x); // Object.assign(target, ...sources)

y.a = 2;
console.log(x); // { a: 1 }
console.log(y); // { a: 2 }

Note: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#warning_for_deep_clone

Pulkit Chaudhri
  • 531
  • 5
  • 6
  • `Object.assign` is not a candidate for deep copy. It works only if the properties are value types - like in the example - but for references it basically performs a shallow copy, as clearly indicated by the other answer. So the last part of this answer is wrong. – BaCaRoZzo Dec 07 '22 at 20:00
5

Use deepcopy from npm. Works in both the browser and in node as an npm module...

https://www.npmjs.com/package/deepcopy

let a = deepcopy(b)

Hiranya Sarma
  • 1,454
  • 4
  • 15
  • 25
user3071643
  • 1,405
  • 1
  • 15
  • 18
5

Object copy using ( ... )

//bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2,c: 3 }

//good
const originalObj = { id: 5, name: 'San Francisco'};
const copyObject = {...originalObj, pincode: 4444};
console.log(copyObject)  //{ id: 5, name: 'San Francisco', pincode: 4444 }

Same can be use for copying array from one to other

const itemsCopy = [...items];
Henrik Andersson
  • 45,354
  • 16
  • 98
  • 92
GANESH CHOKHARE
  • 316
  • 3
  • 8
5

Simple recursive method to clone an object. Also could use lodash.clone.

let clone = (obj) => {
 let obj2 = Array.isArray(obj) ? [] : {};
 for(let k in obj) {
          obj2[k] = (typeof obj[k] === 'object' ) ? clone(obj[k]) :  obj[k];
        }
        return obj2;
    }

let w = { name: "Apple", types: ["Fuji", "Gala"]};
let x = clone(w);
w.name = "Orange";
w.types = ["Navel"];
console.log(x);
console.log(w);
Raskolnikov
  • 121
  • 2
  • 7
5

Jan Turoň's answer above is very close, and may be the best to use in a browser due to compatibility issues, but it will potentially cause some strange enumeration issues. For instance, executing:

for ( var i in someArray ) { ... }

Will assign the clone() method to i after iterating through the elements of the array. Here's an adaptation that avoids the enumeration and works with node.js:

Object.defineProperty( Object.prototype, "clone", {
    value: function() {
        if ( this.cloneNode )
        {
            return this.cloneNode( true );
        }

        var copy = this instanceof Array ? [] : {};
        for( var attr in this )
        {
            if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
            {
                copy[ attr ] = this[ attr ];
            }
            else if ( this[ attr ] == this )
            {
                copy[ attr ] = copy;
            }
            else
            {
                copy[ attr ] = this[ attr ].clone();
            }
        }
        return copy;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

This avoids making the clone() method enumerable because defineProperty() defaults enumerable to false.

Andy Burke
  • 67
  • 1
  • 2
  • 2
4

Consult http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data for the W3C's "Safe passing of structured data" algorithm, intended to be implemented by browsers for passing data to eg web workers. However, it has some limitations, in that it does not handle functions. See https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm for more information, including an alternative algorithm in JS which gets you part of the way there.

  • While this has some great links, it's not really an answer. If it was extended to include an implementation of the algorithms referenced it might be an answer. – RobG Jan 21 '19 at 23:53
4

Clone an object based on a template. What do you do if you don't want an exact copy, but you do want the robustness of some kind of reliable clone operation but you only want bits cloned or you want to make sure you can control the existence or format of each attribute value cloned?

I am contributing this because it's useful for us and we created it because we could not find something similar. You can use it to clone an object based on a template object which specifies what attributes of the object I want to clone, and the template allows for functions to transform those attributes into something different if they don't exist on the source object or however you want to handle the clone. If it's not useful I am sure someone can delete this answer.

   function isFunction(functionToCheck) {
       var getType = {};
       return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
   }

   function cloneObjectByTemplate(obj, tpl, cloneConstructor) {
       if (typeof cloneConstructor === "undefined") {
           cloneConstructor = false;
       }
       if (obj == null || typeof (obj) != 'object') return obj;

       //if we have an array, work through it's contents and apply the template to each item...
       if (Array.isArray(obj)) {
           var ret = [];
           for (var i = 0; i < obj.length; i++) {
               ret.push(cloneObjectByTemplate(obj[i], tpl, cloneConstructor));
           }
           return ret;
       }

       //otherwise we have an object...
       //var temp:any = {}; // obj.constructor(); // we can't call obj.constructor because typescript defines this, so if we are dealing with a typescript object it might reset values.
       var temp = cloneConstructor ? new obj.constructor() : {};

       for (var key in tpl) {
           //if we are provided with a function to determine the value of this property, call it...
           if (isFunction(tpl[key])) {
               temp[key] = tpl[key](obj); //assign the result of the function call, passing in the value
           } else {
               //if our object has this property...
               if (obj[key] != undefined) {
                   if (Array.isArray(obj[key])) {
                       temp[key] = [];
                       for (var i = 0; i < obj[key].length; i++) {
                           temp[key].push(cloneObjectByTemplate(obj[key][i], tpl[key], cloneConstructor));
                       }
                   } else {
                       temp[key] = cloneObjectByTemplate(obj[key], tpl[key], cloneConstructor);
                   }
               }
           }
       }

       return temp;
   }

A simple way to call it would be like this:

var source = {
       a: "whatever",
       b: {
           x: "yeah",
           y: "haha"
       }
   };
   var template = {
       a: true, //we want to clone "a"
       b: {
           x: true //we want to clone "b.x" too
       }
   }; 
   var destination = cloneObjectByTemplate(source, template);

If you wanted to use a function to make sure an attribute is returned or to make sure it's a particular type, use a template like this. Instead of using { ID: true } we are providing a function which still just copies the ID attribute of the source object but it makes sure that it's a number even if it does not exist on the source object.

 var template = {
    ID: function (srcObj) {
        if(srcObj.ID == undefined){ return -1; }
        return parseInt(srcObj.ID.toString());
    }
}

Arrays will clone fine but if you want to you can have your own function handle those individual attributes too, and do something special like this:

 var template = {
    tags: function (srcObj) {
        var tags = [];
        if (process.tags != undefined) {
            for (var i = 0; i < process.tags.length; i++) {

                tags.push(cloneObjectByTemplate(
                  srcObj.tags[i],
                  { a : true, b : true } //another template for each item in the array
                );
            }
        }
        return tags;
    }
 }

So in the above, our template just copies the tags attribute of the source object if it exists, (it's assumed to be an array), and for each element in that array the clone function is called to individually clone it based on a second template which just copies the a and b attributes of each of those tag elements.

If you are taking objects in and out of node and you want to control which attributes of those objects are cloned then this is a great way of controlling that in node.js and the code works in the browser too.

Here is an example of it's use: http://jsfiddle.net/hjchyLt1/

Hiranya Sarma
  • 1,454
  • 4
  • 15
  • 25
Action Dan
  • 443
  • 4
  • 10
4

According to the Airbnb JavaScript Style Guide with 404 contributors:

Prefer the object spread operator over Object.assign to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted.

// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this

// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

Also I'd like to warn you that even though Airbnb hardly recommends the object spread operator approach. Keep in mind that Microsoft Edge still does not support this 2018 feature yet.

ES2016+ Compat table >>

webpreneur
  • 795
  • 9
  • 15
4

Just as this link says use this code:

let clone = Object.create(Object.getPrototypeOf(obj),
 Object.getOwnPropertyDescriptors(obj));
Pouria Moosavi
  • 662
  • 7
  • 22
  • Answering questions here is good but this is an old question with many good answers. Any new answer should add significant new information and new insights into the topic. Your answer here is very brief and has no explanation, the link to an offsite page is useful but an answer should be self-contained and have enough detail if the link fails. – AdrianHHH Jun 16 '19 at 16:28
  • this is exactly the same as `Object.assign(newObj, obj)`, its a shallow copy – Exlord Jun 19 '19 at 04:23
4

Simple

var restore = { name:'charlesi',
age:9}
var prev_data ={
name: 'charles'
age : 10
}

var temp = JSON.stringify(prev_data)
restore = JSON.parse(temp)

restore = {
name:'charlie',
age : 12}

output prev_data:

{
name: 'charles'
age : 10
} 
3

In my code I frequently define a function (_) to handle copies so that I can pass by value to functions. This code creates a deep copy but maintains inheritance. It also keeps track of sub-copies so that self-referential objects can be copied without an infinite loop. Feel free to use it.

It might not be the most elegant, but it hasn't failed me yet.

_ = function(oReferance) {
  var aReferances = new Array();
  var getPrototypeOf = function(oObject) {
    if(typeof(Object.getPrototypeOf)!=="undefined") return Object.getPrototypeOf(oObject);
    var oTest = new Object();
    if(typeof(oObject.__proto__)!=="undefined"&&typeof(oTest.__proto__)!=="undefined"&&oTest.__proto__===Object.prototype) return oObject.__proto__;
    if(typeof(oObject.constructor)!=="undefined"&&typeof(oTest.constructor)!=="undefined"&&oTest.constructor===Object&&typeof(oObject.constructor.prototype)!=="undefined") return oObject.constructor.prototype;
    return Object.prototype;
  };
  var recursiveCopy = function(oSource) {
    if(typeof(oSource)!=="object") return oSource;
    if(oSource===null) return null;
    for(var i=0;i<aReferances.length;i++) if(aReferances[i][0]===oSource) return aReferances[i][1];
    var Copy = new Function();
    Copy.prototype = getPrototypeOf(oSource);
    var oCopy = new Copy();
    aReferances.push([oSource,oCopy]);
    for(sPropertyName in oSource) if(oSource.hasOwnProperty(sPropertyName)) oCopy[sPropertyName] = recursiveCopy(oSource[sPropertyName]);
    return oCopy;
  };
  return recursiveCopy(oReferance);
};

// Examples:
Wigit = function(){};
Wigit.prototype.bInThePrototype = true;
A = new Wigit();
A.nCoolNumber = 7;
B = _(A);
B.nCoolNumber = 8; // A.nCoolNumber is still 7
B.bInThePrototype // true
B instanceof Wigit // true
Hiranya Sarma
  • 1,454
  • 4
  • 15
  • 25
Alec
  • 41
  • 3
3

You can use functional closure to gain all the benefits of a deep copy, without a deep copy. It's a very different paradigm, but works well. Instead of trying to copy an existing object, just use a function to instantiate a new object when you need one.

First, create an function that returns an object

function template() {
  return {
    values: [1, 2, 3],
    nest: {x: {a: "a", b: "b"}, y: 100}
  };
}

Then create a simple shallow copy function

function copy(a, b) {
  Object.keys(b).forEach(function(key) {
    a[key] = b[key];
  });
}

Create a new object, and copy the template's properties onto it

var newObject = {}; 
copy(newObject, template());

But the above copy step is not necessary. All you need to do is this:

var newObject = template();

Now that you have a new object, test to see what its properties are:

console.log(Object.keys(newObject));

This displays:

["values", "nest"]

Yes, those are the newObject's own properties, not references to properties on another object. Let's just check:

console.log(newObject.nest.x.b);

This displays:

"b"

The newObject has acquired all of the template object's properties, but is free of any dependency chain.

http://jsbin.com/ISUTIpoC/1/edit?js,console

I added this example to encourage some debate, so please add some comments :)

d13
  • 9,817
  • 12
  • 36
  • 44
  • 1
    Object.keys wasnt implemented until JavaScript 1.8.5, meaning it is not available in IE 8 and other legacy browsers. So while this answer will work great in Modern browsers, it will fail in IE 8. So if you use this method, you must use a properly emulating Object.keys polyfill. – Blaine Kasten May 14 '14 at 17:20
3

I think, that recurrence with caching is the best what we can do it here without libraries.

And underestimated WeakMap comes to the problem of cycles, wherein storing pairs of references to old and new object can help us to recreate pretty easily whole tree.

I prevented deep cloning of the DOM elements, probably you don't want to clone entire page :)

function deepCopy(object) {
    const cache = new WeakMap(); // Map of old - new references

    function copy(obj) {
        if (typeof obj !== 'object' ||
            obj === null ||
            obj instanceof HTMLElement
        )
            return obj; // primitive value or HTMLElement

        if (obj instanceof Date) 
            return new Date().setTime(obj.getTime());

        if (obj instanceof RegExp) 
            return new RegExp(obj.source, obj.flags);

        if (cache.has(obj)) 
            return cache.get(obj);

        const result = obj instanceof Array ? [] : {};

        cache.set(obj, result); // store reference to object before the recursive starts

        if (obj instanceof Array) {
            for(const o of obj) {
                 result.push(copy(o));
            }
            return result;
        }

        const keys = Object.keys(obj); 

        for (const key of keys)
            result[key] = copy(obj[key]);

        return result;
    }

    return copy(object);
}

Some tests:

// #1
const obj1 = { };
const obj2 = { };
obj1.obj2 = obj2;
obj2.obj1 = obj1; // Trivial circular reference

var copy = deepCopy(obj1);
copy == obj1 // false
copy.obj2 === obj1.obj2 // false
copy.obj2.obj1.obj2 // and so on - no error (correctly cloned).

// #2
const obj = { x: 0 }
const clone = deepCopy({ a: obj, b: obj });
clone.a == clone.b // true

// #3
const arr = [];
arr[0] = arr; // A little bit weird but who cares
clone = deepCopy(arr)
clone == arr // false;
clone[0][0][0][0] == clone // true;

NOTE: I'm using constants, for of loop, => operator and WeakMaps to create more essential code. This syntax (ES6) is supported by today's browsers

Maciej Bukowski
  • 3,240
  • 23
  • 28
3

The solution JSON.parse(JSON.stringify(orig_obj) as stated by many peers here for deep_cloning has several issues which I found, and they are listed below:

  1. It discards the entries while copying whose values are undefined in the original object,
  2. If there are some values like Infinity, NaN etc, they will be converted into null while copying,
  3. If there is a Date type in the original object, it will be stringified in the cloned object (typeof date_entry --> string).

Found an effective way for cloning an object, and it worked well for me in all sort of scenarios. Please have a look at below code, as it has resolved all above mentioned pitfalls of JSON.parse(...), yet resulting in proper deep-cloning:

var orig_obj = {
  string: 'my_str',
  number: 123,
  bool: false,
  nul: null,
  nested : {
    value : true
  },
  nan : NaN,
  date: new Date(), 
  undef: undefined,
  inf: Infinity,
}
console.log("original_obj before modification: ", orig_obj, "\n");
console.log(typeof orig_obj.date, "\n");

var clone_obj = Object.assign({}, orig_obj);

//this below loop will help in deep cloning and solving above issues
for(let prop in orig_obj) {
    if(typeof orig_obj[prop] === "object") {
        if(orig_obj[prop] instanceof Date)
            clone_obj[prop] = orig_obj[prop];
        else {
            clone_obj[prop] = JSON.parse(JSON.stringify(orig_obj[prop]));
        }
    }
}

console.log("cloned_obj before modification: ", orig_obj, "\n");

clone_obj.bool = true;
clone_obj.nested.value = "false";

console.log("original_obj post modification: ", orig_obj, "\n");
console.log("cloned_obj post modification: ", clone_obj, "\n");
console.log(typeof clone_obj.date);
asoni94
  • 137
  • 2
  • 11
  • You check all the top level properties to see if they are Dates, but then you JSON stringify/parse them if they are not. What if the top level property is an object that contains a Date as one of its properties? Your solution needs to work recursively in order to catch all the possible cases of nested objects that can't be JSON.parsed. – A. Levy Jan 24 '20 at 14:06
3

I have gone through all above solutions and they are quite well. However, there is another approach that you can use to clone object (with values not reference). Object.assign

let x = {
    a: '1',
    b: '2'
}

let y = Object.assign({}, x)
y.a = "3"

console.log(x)

The output will be

{ a: '1', b: '2' }

Moreover, you can also clone array with the same approach.

clonedArray = Object.assign([], array)
FAHAD SIDDIQUI
  • 631
  • 4
  • 22
2

I've tried this in the case of a scalar object and it works for me:

function binder(i) {
  return function () {
    return i;
  };
}

a=1;
b=binder(a)(); // copy value of a into b

alert(++a); // 2
alert(b); // still 1

Regards.

John Sonderson
  • 3,238
  • 6
  • 31
  • 45
2

Here's a modern solution that doesn't have the pitfalls of Object.assign() (does not copy by reference):

const cloneObj = (obj) => {
    return Object.keys(obj).reduce((dolly, key) => {
        dolly[key] = (obj[key].constructor === Object) ?
            cloneObj(obj[key]) :
            obj[key];
        return dolly;
    }, {});
};
ryanpcmcquen
  • 6,285
  • 3
  • 24
  • 37
2

Using defaults (historically specific to nodejs but now usable from the browser thanks to modern JS):

import defaults from 'object.defaults';

const myCopy = defaults({}, myObject);
aberaud
  • 909
  • 1
  • 11
  • 24
2

To support a better understanding of copying of objects, this illustrative jsbin may be of value

class base {
  get under(){return true}
}

class a extends base {}

const b = {
  get b1(){return true},
  b: true
}

console.log('Object assign')
let t1 = Object.create(b)
t1.x = true
const c = Object.assign(t1, new a())
console.log(c.b1 ? 'prop value copied': 'prop value gone')
console.log(c.x ? 'assigned value copied': 'assigned value gone')
console.log(c.under ? 'inheritance ok': 'inheritance gone')
console.log(c.b1 ? 'get value unchanged' : 'get value lost')
c.b1 = false
console.log(c.b1? 'get unchanged' : 'get lost')
console.log('-----------------------------------')
console.log('Object assign  - order swopped')
t1 = Object.create(b)
t1.x = true
const d = Object.assign(new a(), t1)
console.log(d.b1 ? 'prop value copied': 'prop value gone')
console.log(d.x ? 'assigned value copied': 'assigned value gone')
console.log(d.under ? 'inheritance n/a': 'inheritance gone')
console.log(d.b1 ? 'get value copied' : 'get value lost')
d.b1 = false
console.log(d.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e = { ...t1, ...t2 }
console.log(e.b1 ? 'prop value copied': 'prop value gone')
console.log(e.x ? 'assigned value copied': 'assigned value gone')
console.log(e.under ? 'inheritance ok': 'inheritance gone')
console.log(e.b1 ? 'get value copied' : 'get value lost')
e.b1 = false
console.log(e.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator on getPrototypeOf')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e1 = { ...Object.getPrototypeOf(t1), ...Object.getPrototypeOf(t2) }
console.log(e1.b1 ? 'prop value copied': 'prop value gone')
console.log(e1.x ? 'assigned value copied': 'assigned value gone')
console.log(e1.under ? 'inheritance ok': 'inheritance gone')
console.log(e1.b1 ? 'get value copied' : 'get value lost')
e1.b1 = false
console.log(e1.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('keys, defineProperty, getOwnPropertyDescriptor')
f = Object.create(b)
t2 = new a()
f.x = 'a'
Object.keys(t2).forEach(key=> {
  Object.defineProperty(f,key,Object.getOwnPropertyDescriptor(t2, key))
})
console.log(f.b1 ? 'prop value copied': 'prop value gone')
console.log(f.x ? 'assigned value copied': 'assigned value gone')
console.log(f.under ? 'inheritance ok': 'inheritance gone')
console.log(f.b1 ? 'get value copied' : 'get value lost')
f.b1 = false
console.log(f.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('defineProperties, getOwnPropertyDescriptors')
let g = Object.create(b)
t2 = new a()
g.x = 'a'
Object.defineProperties(g,Object.getOwnPropertyDescriptors(t2))
console.log(g.b1 ? 'prop value copied': 'prop value gone')
console.log(g.x ? 'assigned value copied': 'assigned value gone')
console.log(g.under ? 'inheritance ok': 'inheritance gone')
console.log(g.b1 ? 'get value copied' : 'get value lost')
g.b1 = false
console.log(g.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
TrevTheDev
  • 2,616
  • 2
  • 18
  • 36
2

Ways to Copy Objects in JavaScript

  1. Use the spread (...) syntax
  2. Use the Object.assign() method
  3. Use the JSON.stringify() and JSON.parse() methods
const person = {
    firstName: 'John',
    lastName: 'Doe'
};

// using spread ...
let p1 = {
    ...person
};

// using  Object.assign() method
let p2 = Object.assign({}, person);

// using JSON
let p3 = JSON.parse(JSON.stringify(person));
Dere Sagar
  • 1,719
  • 1
  • 10
  • 7
2

Using the spread syntax performs a shallow copy of the object. This means that none of the nested object instances are cloned as you can see in the following example with the nested object child

const user1 = { 
    name: 'Alex',
    address: '15th Park Avenue',
    age: 43,
    child:{
        name: 'John'
    }
}

const user2 = {...user1};

user1.child.name = 'chris';

console.log(user1);
console.log(user2);

To solve this nested object problem and perform a deep copy we can use JSON.parse(JSON.stringify(someObject))

const user1 = { 
    name: 'Alex',
    address: '15th Park Avenue',
    age: 43,
    child:{
        name: 'John'
    }
}

const user2 = JSON.parse(JSON.stringify(user1));

user1.child.name = 'chris';

console.log(user1);
console.log(user2);
Ran Turner
  • 14,906
  • 5
  • 47
  • 53
2

The different

Only copy top level: {...object} and Object.assign({}, object)

let objA = {
  a: "keyA",
  b: {
    c: "keyC",
  }
}
let objB = Object.assign({}, objA); // or  {...objB}
// change objB
objB.a = "Change objA.a (top)"
console.log("objA.a (top) No Change:\n" + JSON.stringify(objA, false, 2));

objB.b.c = "change should be only for objB.b.c but it in objA.b.c"
console.log("objA.a.c second level has Change:\n" + JSON.stringify(objA, false, 2));

for deep copy use structuredClone() 2022 or JSON.parse(JSON.stringify(object)) for old browser, easy without hack.

let objA = {
  a: "keyA",
  b: {
    c: "keyC",
  }
}
let objB = typeof structuredClone == 'function' ?
  structuredClone(objA) : JSON.parse(JSON.stringify(objA));
// change objB
objB.a = "Change objA.a (top)"
objB.b.c = "change should be only for objB.c but it in objA.c"

console.log("objA has no Change:\n" + JSON.stringify(objA, false, 2));
uingtea
  • 6,002
  • 2
  • 26
  • 40
1

Ok so this might be the very best option for shallow copying. If follows the many examples using assign, but it also keeps the inheritance and prototype. It's so simple too and works for most array-like and Objects except those with constructor requirements or read-only properties. But that means it fails miserably for TypedArrays, RegExp, Date, Maps, Sets and Object versions of primitives (Boolean, String, etc..).

function copy ( a ) { return Object.assign( new a.constructor, a ) }

Where a can be any Object or class constructed instance, but again not be reliable for thingies that use specialized getters and setters or have constructor requirements, but for more simple situations it rocks. It does work on arguments as well.

You can also apply it to primitives to get strange results, but then... unless it just ends up being a useful hack, who cares.

results from basic built-in Object and Array...

> a = { a: 'A', b: 'B', c: 'C', d: 'D' }
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> b = copy( a )
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> a = [1,2,3,4]
[ 1, 2, 3, 4 ]
> b = copy( a )
[ 1, 2, 3, 4 ]

And fails because of mean get/setters, constructor required arguments or read-only properties, and sins against the father.

> a = /\w+/g
/\w+/g
> b = copy( a )  // fails because source and flags are read-only
/(?:)/
> a = new Date ( '1/1/2001' )
2000-12-31T16:00:00.000Z
> b = copy( a )  // fails because Date using methods to get and set things
2017-02-04T14:44:13.990Z
> a = new Boolean( true )
[Boolean: true]
> b = copy( a )  // fails because of of sins against the father
[Boolean: false]
> a = new Number( 37 )
[Number: 37]
> b = copy( a )  // fails because of of sins against the father
[Number: 0]
> a = new String( 'four score and seven years ago our four fathers' )
[String: 'four score and seven years ago our four fathers']
> b = copy( a )  // fails because of of sins against the father
{ [String: ''] '0': 'f', '1': 'o', '2': 'u', '3': 'r', '4': ' ', '5': 's', '6': 'c', '7': 'o', '8': 'r', '9': 'e', '10': ' ', '11': 'a', '12': 'n', '13': 'd', '14': ' ', '15': 's', '16': 'e', '17': 'v', '18': 'e', '19': 'n', '20': ' ', '21': 'y', '22': 'e', '23': 'a', '24': 'r', '25': 's', '26': ' ', '27': 'a', '28': 'g', '29': 'o', '30': ' ', '31': 'o', '32': 'u', '33': 'r', '34': ' ', '35': 'f', '36': 'o', '37': 'u', '38': 'r', '39': ' ', '40': 'f', '41': 'a', '42': 't', '43': 'h', '44': 'e', '45': 'r', '46': 's' } 
Erich Horn
  • 85
  • 6
1

I don't know which cases this doesn't work for, but it got me a copy of an array. I think its cute :) Hope it helps

copiedArr = origArr.filter(function(x){return true})
knowingpark
  • 639
  • 7
  • 16
1

If your object is a class (e.g. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes):

var copiedObject = jQuery.extend(true, {}, originalObject);
copiedObject.__proto__ = originalObject.__proto__;

Then in copiedObject you have a deep-copied instance of originalObject class with all its methods.

j rdl
  • 89
  • 1
  • 1
1

If you are using TypeScript, need to support older web browsers (and so can't use Object.assign), and aren't using a library with a clone method build in, you can make yourself a combine helper in a few lines of code. It combines objects, and if you have only one, just clones it.

/** Creates a new object that combines the properties of the specified objects. */
function combine(...objs: {}[]) {
    const combined = {};
    objs.forEach(o => Object.keys(o).forEach(p => combined[p] = o[p]));
    return combined;
}
Edward Brey
  • 40,302
  • 20
  • 199
  • 253
1

You may clone your Object without modification parent Object -

    /** [Object Extend]*/
    ( typeof Object.extend === 'function' ? undefined : ( Object.extend = function ( destination, source ) {
        for ( var property in source )
            destination[property] = source[property];
        return destination;
    } ) );
    /** [/Object Extend]*/
    /** [Object clone]*/
    ( typeof Object.clone === 'function' ? undefined : ( Object.clone = function ( object ) {
        return this.extend( {}, object );
    } ) );
    /** [/Object clone]*/

    let myObj = {
        a:1, b:2, c:3, d:{
            a:1, b:2, c:3
        }
    };

    let clone = Object.clone( myObj );

    clone.a = 10;

    console.log('clone.a==>', clone.a); //==> 10

    console.log('myObj.a==>', myObj.a); //==> 1 // object not modified here

    let clone2 = Object.clone( clone );

    clone2.a = 20;

    console.log('clone2.a==>', clone2.a); //==> 20

    console.log('clone.a==>', clone.a); //==> 10 // object not modified here
Rajib Chy
  • 800
  • 10
  • 22
1

I'm providing an answer to this question, because I do not see any native, recursive implementations here that resolve the problem of DOM elements.

The problem there is that <element> has parent and child attributes, that link to other elements with parent and child values, which point back to the original <element>, causing either an infinite recursive or cyclic redundancy.

If your object is something safe and simple like

{
    '123':456
}

...then any other answer here will probably work.

But if you have...

{
    '123':<reactJSComponent>,
    '456':document.createElement('div'),
}

...then you need something like this:

    // cloneVariable() : Clone variable, return null for elements or components.
var cloneVariable = function (args) {
    const variable = args.variable;

    if(variable === null) {
            return null;
    }

    if(typeof(variable) === 'object') {
            if(variable instanceof HTMLElement || variable.nodeType > 0) {
                    return null;
            }

            if(Array.isArray(variable)) {
                    var arrayclone = [];

                    variable.forEach((element) => {
                            arrayclone.push(cloneVariable({'variable':element}));
                    });

                    return arrayclone;
            }

            var objectclone = {};

            Object.keys(variable).forEach((field) => {
                    objectclone[field] = cloneVariable({'variable':variable[field]});
            });

            return objectclone;
    }

    return variable;
}
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
1
var x = {'e': 2, 'd': 8, 'b': 5};

const y = {};
for(let key in x) {
    y[key] = x[key];
}
console.log(y); // =>>> {e: 2, d: 8, b: 5}

const z = {};
Object.keys(x).forEach(key => {
    z[key] = x[key];
});
console.log(z); // =>>> {e: 2, d: 8, b: 5}

const w = {};
for(let i = 0; i < Object.keys(x).length; i++) {
    w[Object.keys(x)[i]] = x[Object.keys(x)[i]];
}
console.log(w); // =>>> {e: 2, d: 8, b: 5}

const v = {};
for(let key of Object.keys(x)) {
    v[key] = x[key];
}
console.log(v); // =>>> {e: 2, d: 8, b: 5}

x['q'] = 100;   // Altering x will not affect the other objects

console.log(x); // =>>> {e: 2, d: 8, b: 5, q: 100}
console.log(y); // =>>> {e: 2, d: 8, b: 5}
console.log(z); // =>>> {e: 2, d: 8, b: 5}
console.log(w); // =>>> {e: 2, d: 8, b: 5}
console.log(v); // =>>> {e: 2, d: 8, b: 5}
Fouad Boukredine
  • 1,495
  • 14
  • 18
1

You can use rest operator to clone arrays or objects

let myObj = {1: 100, 'a': 200};

let clone = {...myObj}; 

clone.a = 300;

console.log(clone.a) // Output :- 300
console.log(myObj.a) // Output :- 200
  • Please note that this will only perform "shallow-cloning", https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_object_literals – typekev Jun 22 '21 at 19:16
1

This makes new copy of your obj (not just reference).

let myCopy = JSON.parse(JSON.stringify(obj)); 

..Works much efficiently then the _.cloneDeep(obj).

Radim Šafrán
  • 463
  • 7
  • 16
1

Short and sweet:

let clone = Object.fromEntries(Object.entries(obj));

Demo:

let obj = {a: 'b'};
let clone = Object.fromEntries(Object.entries(obj));

clone.a = 'c';

console.log(obj, clone);
benhatsor
  • 1,863
  • 6
  • 20
1

If there are no circular dependencies in your object, I suggest using one of the other answers or jQuery's copy methods, as they all seem quite effective.

If there are circular dependencies (i.e., two sub-objects link to each other), you are kind of screwed as there is (from a theoretical perspective) no way to solve this issue elegantly.

Community
  • 1
  • 1
Dan Lew
  • 85,990
  • 32
  • 182
  • 176
  • 2
    Actually, Python's object serialization handles circular references by keeping track of nodes in the object graph that it has already processed. You could use that approach to implement a robust copy routine. It would be a little more work though! – A. Levy May 20 '10 at 18:21
0
//
// creates 'clone' method on context object
//
//  var 
//     clon = Object.clone( anyValue );
//
!((function (propertyName, definition) {
    this[propertyName] = definition();
}).call(
    Object,
    "clone",
    function () {
        function isfn(fn) {
            return typeof fn === "function";
        }

        function isobj(o) {
            return o === Object(o);
        }

        function isarray(o) {
            return Object.prototype.toString.call(o) === "[object Array]";
        }

        function fnclon(fn) {
            return function () {
                fn.apply(this, arguments);
            };
        }

        function owns(obj, p) {
            return obj.hasOwnProperty(p);
        }

        function isemptyobj(obj) {
            for (var p in obj) {
                return false;
            }
            return true;
        }

        function isObject(o) {
            return Object.prototype.toString.call(o) === "[object Object]";
        }
        return function (input) {
            if (isfn(input)) {
                return fnclon(input);
            } else if (isobj(input)) {
                var cloned = {};
                for (var p in input) {
                    owns(Object.prototype, p)
                    || (
                        isfn(input[p])
                        && ( cloned[p] = function () { return input[p].apply(input, arguments); } )
                        || ( cloned[p] = input[p] )
                    );
                }
                if (isarray(input)) {
                    cloned.length = input.length;
                    "concat every filter forEach indexOf join lastIndexOf map pop push reduce reduceRight reverse shift slice some sort splice toLocaleString toString unshift"
                    .split(" ")
                    .forEach(
                      function (methodName) {
                        isfn( Array.prototype[methodName] )
                        && (
                            cloned[methodName] =
                            function () {
                                return Array.prototype[methodName].apply(cloned, arguments);
                            }
                        );
                      }
                    );
                }
                return isemptyobj(cloned)
                       ? (
                          isObject(input)
                          ? cloned
                          : input
                        )
                       : cloned;
            } else {
                return input;
            }
        };
    }
));
//
public override
  • 974
  • 8
  • 17
0

I came to this page due to the same question but I'm neither using JQuery and none of the clone-Methods worked for my own objects.

I'm aware my answer isn't related too strong to this question because it's a different approach. Instead of using clone-functions I use a create function. It worked for me for the following (unfortunately restricting) purposes:

  1. I use mostly JSP-generated Javascript
  2. I know in the beginning which Object must be generated (In my case it's Information from a Database which gets fetched once and needs to be deployed more often in the JS.

First I defined my Objects like this:

var obj= new Object();
obj.Type='Row';
obj.ID=1;
obj.Value='Blah blah';

Now I moved everything like:

function getObjSelektor(id_nummer,selected){
var obj = document.createElement("select");
obj.setAttribute("id","Selektor_"+id_nummer);
obj.setAttribute("name","Selektor");
obj.setAttribute("size","1");

var obj_opt_1 = document.createElement("option");
obj_opt_1.setAttribute("value","1");
if(1==selected)
    posopval_opt_1.setAttribute("selected","selected");
obj_opt_1.innerHTML="Blah blah";
obj.appendChild(obj_opt_1);

var obj_opt_2 = document.createElement("option");
obj_opt_2.setAttribute("value","2");
if(2==selected)
    obj_opt_2.setAttribute("selected","selected");
obj_opt_2.innerHTML="2nd Row";
obj.appendChild(obj_opt_2);

...

return obj;
}

And call the function in the regular code:

myDiv.getObjSelektor(getObjSelektor(anotherObject.ID));

As said this is a different approach which solved my issue for my purposes.

Qohelet
  • 1,459
  • 4
  • 24
  • 41
0

If you got an Object with Functions you can do it with JSONfn, see http://www.eslinstructor.net/jsonfn/.

var obj= {
    name:'Marvin',
    getName :  function(){
      return this.name;
    }
}
var cobj = JSONfn.parse(JSONfn.stringify(obj));
basis
  • 158
  • 2
  • 8
0

The problem with copying an object that, eventually, may point at itself, can be solved with a simple check. Add this check, every time there is a copy action. It may be slow, but it should work.

I use a toType() function to return the object type, explicitly. I also have my own copyObj() function, which is rather similar in logic, which answers all three Object(), Array(), and Date() cases.

I run it in NodeJS.

NOT TESTED, YET.

// Returns true, if one of the parent's children is the target.
// This is useful, for avoiding copyObj() through an infinite loop!
function isChild(target, parent) {
  if (toType(parent) == '[object Object]') {
    for (var name in parent) {
      var curProperty = parent[name];

      // Direct child.
      if (curProperty = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {
        if (isChild(target, curProperty)) return true;
      }
    }
  } else if (toType(parent) == '[object Array]') {
    for (var i=0; i < parent.length; i++) {
      var curItem = parent[i];

      // Direct child.
      if (curItem = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {
        if (isChild(target, curItem)) return true;
      }
    }
  }

  return false;     // Not the target.
}
James Koss
  • 523
  • 5
  • 10
0

To handle circular objects that that JSON.stringify can't handle, you can bring in a library called JSOG, that serializes and deserializes arbitrary graphs into JSON format.

var clone = JSOG.parse(JSOG.stringify(original));

It might also be interesting to try patching JSOG for cloning with this trick (don't have time at the moment, but if someone wants to give it a shot...):

Serialize a simple function :

foo.f = function(a) { return a }
var stringForm = foo.f.toString() // "function (a) { return a }"

Deserialize a function :

eval("foo.f = " + stringForm)

Some conventions (probably in the name of the property) to identify functions vs regular strings would be needed (@func_f perhaps).

Of course if the function calls a second function the second function will need to exist just as it would for the original.

The above however is quite dangerous if you are to accept the serialized form from an untrusted source, but then accepting any function in any form from an untrusted source would be dangerous, so if you're interested in cloning functions trust must have already been established (or you're already intent on writing a security flaw!).

Disclaimer: I have not tested the speed of JSOG stringify/parse vs JSON stringify/parse, but It does work on the simple (circular) objects I tested it with.

Gus
  • 6,719
  • 6
  • 37
  • 58
0

Ok, I know it has many answers, but no one pointed out, EcmaScript5 has assign method, work on FF and Chrome, it copies enumerable and own properties and Symbols.

Object Assign

Marco
  • 2,757
  • 1
  • 19
  • 24
  • 1
    No support in IE :( – Naomi Aug 14 '17 at 19:50
  • 1
    Why you need to support a browser that does not exist anymore? Seriously, you don't need to worry, at least is what we do here, just don't support and alert the user if he is using any IE version. – Marco Aug 15 '17 at 20:29
  • 1
    I simply switched to JSON.parse approach mentioned here instead. But yes, IE seems troublesome. I had to also change find and findIndex to lodash functions. – Naomi Aug 16 '17 at 04:27
  • @Naomi Because IE does exists and is unfortunately still a popular enough browser even in 2017. Maybe not for tech-savy users like us, but people still use it. And considering it's not even supported in IE 11, that's a BIG drawback! – Yahya Uddin Sep 04 '17 at 21:58
0

From the Apple JavaScript Coding Guidelines:

// Create an inner object with a variable x whose default
// value is 3.
function innerObj()
{
        this.x = 3;
}
innerObj.prototype.clone = function() {
    var temp = new innerObj();
    for (myvar in this) {
        // this object does not contain any objects, so
        // use the lightweight copy code.
        temp[myvar] = this[myvar];
    }
    return temp;
}

// Create an outer object with a variable y whose default
// value is 77.
function outerObj()
{
        // The outer object contains an inner object.  Allocate it here.
        this.inner = new innerObj();
        this.y = 77;
}
outerObj.prototype.clone = function() {
    var temp = new outerObj();
    for (myvar in this) {
        if (this[myvar].clone) {
            // This variable contains an object with a
            // clone operator.  Call it to create a copy.
            temp[myvar] = this[myvar].clone();
        } else {
            // This variable contains a scalar value,
            // a string value, or an object with no
            // clone function.  Assign it directly.
            temp[myvar] = this[myvar];
        }
    }
    return temp;
}

// Allocate an outer object and assign non-default values to variables in
// both the outer and inner objects.
outer = new outerObj;
outer.inner.x = 4;
outer.y = 16;

// Clone the outer object (which, in turn, clones the inner object).
newouter = outer.clone();

// Verify that both values were copied.
alert('inner x is '+newouter.inner.x); // prints 4
alert('y is '+newouter.y); // prints 16

Steve

Steve Harrison
  • 121,227
  • 16
  • 87
  • 72
  • 4
    Object attributes which did not have a clone method would be shallow-copied by this code. Thus, changes to the original can affect the copy. So this will not solve the problem. – A. Levy Apr 08 '09 at 04:26
0

I found a way to clone object with functions (break multiple lines to easier understand):

const clone = Object.assign(
    Object.create(
        Object.getPrototypeOf(originalObject)
    ),
    dataObject
);
Daevne
  • 41
  • 4
0

To add

  • For comparing the result of
    structuredClone() -- deep copy & Object.assign() -- shallow copy (& Object.keys() -- shallow copy)
    -- to see which level of the object are copied
    here is a demo test case -- with a depth of 4 lv::

  • @code-ex::

        // lv4
        class Material {
          /** @type {String} */ name;
          constructor(name) { this.name = name; } // prettier-ignore
        }
        // lv3
        class Ball {
          /** @type {String} */ name;
          constructor(name) { this.name = name; } // prettier-ignore
          /** @type {Material} */ material;
        }
    
        // lv2
        class NodeX {
          /** @type {String} */ name;
          constructor(name) { this.name = name; } // prettier-ignore
          /** @type {Ball} */ ball;
          /** @type {NodeX} */ node_next;
        }
    
        // lv1
        class ListX {
          /** @type {NodeX} */ node_head;
          /** @type {Number} */ length = 0;
        }
    
        const material_a = new Material('wood');
        const material_b = new Material('stone');
        const ball_a = new Ball('Ball_I');
        const ball_b = new Ball('Ball_II');
        ball_a.material = material_a;
        ball_b.material = material_b;
    
        const node_a = new NodeX('Alpha');
        const node_b = new NodeX('Beta');
        node_a.ball = ball_a;
        node_b.ball = ball_b;
    
        const list = new ListX();
        list.node_head = node_a;
        list.length++;
        node_a.node_next = node_b;
        list.length++;
    
        // #>> structuredClone() -- deep copy
        const list_clone = structuredClone(list); // https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object
        test_DeepCopy_compare(list_clone, list);
    
        // #>>#
        function test_DeepCopy_compare(list_clone, list) {
          expect(list_clone === list).toEqual(false); // the list is cloned - lv1
          expect(list_clone.length === list.length).toEqual(true);
    
          expect(list_clone.node_head === list.node_head).toEqual(false); // the node (list es property) is also cloned -- deep copy - lv2
    
          const node_a_clone = list_clone.node_head;
          expect(node_a_clone === node_a).toEqual(false); // ~rp
          expect(node_a_clone.name === node_a.name).toEqual(true);
          expect(node_a_clone.node_next === node_a.node_next).toEqual(false); // the node_next (node es property) is also cloned -- deep copy - lv3
    
          const node_b_clone = node_a_clone.node_next;
          expect(node_b_clone === node_b).toEqual(false); // ~rp
          expect(node_b_clone.name === node_b.name).toEqual(true);
          expect(node_b_clone.node_next === undefined && node_b.node_next === undefined).toEqual(true);
    
          expect(node_a_clone.ball === node_a.ball).toEqual(false); // aga, the ball (node es property) is also cloned -- deep copy - lv3
          expect(node_b_clone.ball === node_b.ball).toEqual(false);
    
          const ball_a_clone = node_a_clone.ball;
          const ball_b_clone = node_b_clone.ball;
          expect(ball_a_clone.material === ball_a.material).toEqual(false); // aga, the material (ball es property) is also cloned -- deep copy - lv4
          expect(ball_b_clone.material === ball_b.material).toEqual(false);
        }
    
        // #>> Object.assign() -- shallow copy
        const list_clone_ScOa = Object.create(Object.getPrototypeOf(list)); // const list_clone_ScOa = {}; // https://stackoverflow.com/a/43935938/9549068
        Object.assign(list_clone_ScOa, list); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#return_value
        test_ShallowCopy_compare(list_clone_ScOa, list); // Object.assign() & Object.keys() produce same test result
    
        // #>> Object.keys() -- shallow copy
        function shallowCopy_ObjectKeys(obj) {
          const obj_clone = Object.create(Object.getPrototypeOf(obj)); // const obj_clone = {};
          for (const propName of Object.keys(obj)) {
            obj_clone[propName] = obj[propName];
          }
          return obj_clone;
        }
    
        const list_clone_ScOk = shallowCopy_ObjectKeys(list);
        test_ShallowCopy_compare(list_clone_ScOk, list);
    
        // #>>#
        function test_ShallowCopy_compare(list_clone_ScOkOa, list) {
          expect(list_clone_ScOkOa === list).toEqual(false); // the list is cloned - lv1
          expect(list_clone_ScOkOa.length === list.length).toEqual(true);
    
          expect(list_clone_ScOkOa.node_head === list.node_head).toEqual(true); // the node (list es property) is NOT cloned -- shallow copy - lv2
    
          const node_a_clone_ScOkOa = list_clone_ScOkOa.node_head;
          expect(node_a_clone_ScOkOa === node_a).toEqual(true); // ~rp
          expect(node_a_clone_ScOkOa.name === node_a.name).toEqual(true);
          expect(node_a_clone_ScOkOa.node_next === node_a.node_next).toEqual(true); // the node_next (node es property) is NOT cloned -- shallow copy - lv3
    
          const node_b_clone_ScOk = node_a_clone_ScOkOa.node_next;
          expect(node_b_clone_ScOk === node_b).toEqual(true); // ~rp
    
          expect(node_a_clone_ScOkOa.ball === node_a.ball).toEqual(true); // aga, the ball (node es property) is NOT cloned -- shallow copy - lv3
          expect(node_b_clone_ScOk.ball === node_b.ball).toEqual(true);
    
          expect(list_clone_ScOkOa instanceof ListX).toEqual(true);
        }
    
  • @comment::

    for Es6 class, seems private / static members are not copied.

    maybe build yourself a own clone() method for your Es6 class is the best way

    For more, see: // https://stackoverflow.com/a/57546221/9549068 // https://stackoverflow.com/a/65024713/9549068

Nor.Z
  • 555
  • 1
  • 5
  • 13
-2

My favorite & elegant JS objects clone solution is

function CloneObject() {}
function cloneObject(o) {
   CloneObject.prototype = o;
   return new CloneObject();
}

Use cloneObject(object) to get a clone of JS object.

Unlike many copy solutions this clone keeps prototype relationship in cloned object.

Alex Che
  • 39
  • 3
  • 1
    I think this not answer the question posted by the op. `code` var o1 = {a: 1} var o2 = cloneObject(o1) o2.b = 2 console.log(o1) // will be {a:1, b:2} `code` – Jaime Jun 07 '13 at 16:41
  • 1
    This is the same as [`Object.create`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create "Object.create - JavaScript | MDN"). – Aadit M Shah Jun 09 '13 at 04:55
  • 1
    This is prototypal inheritance, not cloning. – d13 Jan 16 '14 at 16:13
  • @d13—for many, this is cloning. Copying properties is copying. – RobG May 20 '14 at 12:09
  • Can you be sure that the implementation will *always* copy the prototype reference into the instance right there at instantiation time? I am not claiming it won't, but I have doubt. Anyway, this is just making an empty object with the original as the prototype, which is not the same. If you were not going to modify anything, then you don't need to clone, and if you want to preserve a snapshot, it won't work either because the properties of the prototype are going to be changing because the prototype *is* the original object. – doug65536 Feb 29 '16 at 21:12
  • @RobG - as you say: "Copying properties is copying". Exactly...and this **doesn't** copy them. It just references the old ones...no copy is made. There's still only 1 property, and if you change it then you'll see that change in all locations that reference it too. That's why this is not cloning. – Jimbo Jonny Apr 15 '18 at 17:33
  • @JimboJonny—please read the OP, the term used is "clone", not "copy", hence the distinction. There is no definition of the term so it's just semantics anyway. – RobG Apr 15 '18 at 20:56
  • @RobG - Sorry for the lateness, never checked the response until now. To "clone" actually does have a definition. It is to create an exact replication of something. By no definition is simply referencing something considered a "clone" _or_ a "copy." If a replication does not exist (i.e. another distinct entity) then a cloning hasn't occurred. That is the definition of a "clone." The fact that you or others may have been looking for something other than replication when searching for "cloning" just means you were using the word wrong when searching, and doesn't change those facts. – Jimbo Jonny Dec 02 '19 at 18:27
  • @JimboJonny—yes a long time! My distinction is that a [*clone*](https://en.m.wikipedia.org/wiki/Cloning) means a genetic copy, not an exact copy. Simply coping the enumerable properties of one object to another makes a copy in the same way `a = 'a'; b = a;` makes a copy of `'a'`. Setting two objects to have the same `[[Prototype]]` gives them the same genetic makeup, so a clone, in the same way identical twins have identical DNA, but are different entities and are not exact copies of each other (e.g. they usually have different personalities and are not exactly identical in appearance). – RobG Dec 02 '19 at 22:05
  • @RobG - You talk about how twins are "different entities." That's the issue with what you're saying. Setting the prototype does not make for a different entity. It just references the old one. *And* notice in the article you cite, that that type of "cloning" is just one type. In the first line of the article there is a link to disambiguation (the term "clone" existed before genetic cloning). In there is a link to the programming version of "clone," where it clearly states it is a copy, and even has a section about making pointers/references specifically to point out how that is not a clone. – Jimbo Jonny Dec 03 '19 at 18:07
  • @RobG - TL;DR version: "clone" is an actual computer science term with a specific meaning. It is not just a term we're borrowing for the sake of this question. One of the major points *of* that term is *specifically* to differentiate from pointers/references. Not to include them. From the article on cloning: _"The process of actually making another exact replica of the object instead of just its reference is called cloning."_ – Jimbo Jonny Dec 03 '19 at 18:10
-3
function clone(obj)
{
    var cloneObj = Object.create(obj);

    return cloneObj;
}

In Javascript objects individually inherit another object (Prototypal inheritance). Object.create(obj) returns an object that is a sub-object or child object of obj. In the above function it will effectively return a copy of the object.

However, This is a very odd way to clone because I am not using inheritance for its real purpose.

Shuaib
  • 779
  • 2
  • 13
  • 47
  • 1
    This is prototypal inheritance, not cloning. The new object has none of it's own properties, it simply uses those on the prototype. – d13 Jan 16 '14 at 16:12
  • @d13 I dont need new properties on the new object, i just want properties of the parent object in the new one, so why not inherit? – Shuaib Jan 22 '14 at 08:55
  • 3
    that can be very useful, but it's not cloning, which is what this topic is about. Cloning is where you explcitly copy the properties of an object into another object so that the new object no longer references properties on a prototype. The reason for doing this is to prevent confusion about whether a property belongs to an object or its prototype. That confusion can lead to hard-to-diagnose bugs if you're not certain of this behaviour. [Check this JSBin example and explanation](http://jsbin.com/EKivInO/2/edit?js,console). – d13 Jan 22 '14 at 15:50
  • 1
    @Shuaib - because if you change the property values on the original object they'll change on the new object...they aren't separated...the new object doesn't "have" those properties, it's just referencing the old object. ... Which means you might as well just go `var cloned = otherObj;` and make a reference that way. In both cases the only reason the new variable has references to the old object is because it's literally a pointer to it. One's just the point itself while the other holds the pointer on the prototype. – Jimbo Jonny Oct 10 '16 at 14:53
  • @JimboJonny how about recursive Object.create() ? – l00k Nov 14 '16 at 15:07
  • @l00k - I'm not sure of the exact method you mean when talking about doing it recursively, but no matter what the result should be the same. `Object.create` makes a new object that references the object supplied to it as that new object's prototype. It doesn't clone stuff. Change the values of the old object and they will change in the prototype of the new one (because the prototype of the new one literally IS the old one, that's quite literally what `Object.create` is for). – Jimbo Jonny Nov 14 '16 at 19:55