258

I have a big object with much data. And i want to clone this in other variable. When i set some param of the instance B has the same result in the original object:

var obj = {a: 25, b: 50, c: 75};
var A = obj;
var B = obj;

A.a = 30;
B.a = 40;

alert(obj.a + " " + A.a + " " + B.a); // 40 40 40

My output should be 25 30 40. Any ideas?

EDIT

Thanks Everyone. I change the code of dystroy and this is my result:

Object.prototype.clone = Array.prototype.clone = function() {
    if (Object.prototype.toString.call(this) === '[object Array]') {
        var clone = [];
        for (var i = 0; i < this.length; i++)
            clone[i] = this[i].clone();

        return clone;
    } else if (typeof(this) === "object") {
        var clone = {};
        for (var prop in this) {
            if (this.hasOwnProperty(prop))
                clone[prop] = this[prop].clone();
            }
        return clone;
    } else {
        return this;
    }
}

var obj = {a: 25, b: 50, c: 75};
var A = obj.clone();
var B = obj.clone();
A.a = 30;
B.a = 40;
alert(obj.a + " " + A.a + " " + B.a);

var arr = [25, 50, 75];
var C = arr.clone();
var D = arr.clone();
C[0] = 30;
D[0] = 40;
alert(arr[0] + " " + C[0] + " " + D[0]);
7
  • 3
    See What is the most efficient way to clone a JavaScript object? Commented Oct 2, 2012 at 12:22
  • 4
    @RobW—there's an awful lot of junk in that thread, including different ideas on what a "clone" is. Commented Oct 2, 2012 at 12:45
  • 2
    +1 And the accepted and upvoted answer is, with respect to his author, just a comment on another answer using jQuery. That thread went full banana... Commented Oct 2, 2012 at 12:47
  • 7
    You can simply use copy = Object.create(originalObj); but you may want to use copy = JSON.parse(JSON.stringify(originalObj)); to avoid any reference in sub objects (Deep Copy). Commented Apr 12, 2019 at 9:11
  • 1
    Best way is << let B = JSON.parse(JSON.stringify(A)) >> As it is like a stream of data. The concept of reference is due to deep copy & shallow copy. In Deep copy reference will not be there, where as it will be there in shallow copy like << let B = A >>. Deep copy has some demerit like in custom object, nested object. So, read properly & apply whichever suits you Commented May 15, 2019 at 5:59

4 Answers 4

287

If you use an = statement to assign a value to a var with an object on the right side, javascript will not copy but reference the object.

Spoiler : using JSON.parse(JSON.stringify(obj)) may work but is costly, and might throw a TypeError as in

const a = {};
const b = { a };
a.b = b;
const clone = JSON.parse(JSON.stringify(a));
/* Throws
Uncaught TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property 'b' -> object with constructor 'Object'
    --- property 'a' closes the circle
    at JSON.stringify (<anonymous>)
    at <anonymous>:4:6
*/

As of es2015, if you want a shallow copy (clone the object, but keeping deep refences in the inner structure) you can use destructuring :

const obj = { foo: { bar: "baz" } };
const shallowClone = { ...obj };

shallowClone is a new object, but shallowClone.foo holds a reference to the same object as obj.foo.

You can use lodash's clone method, which does the same, if you don't have access to the spread operator.

var obj = {a: 25, b: 50, c: 75};
var A = _.clone(obj);

Or lodash's cloneDeep method if your object has multiple object levels

var obj = {a: 25, b: {a: 1, b: 2}, c: 75};
var A = _.cloneDeep(obj);

Or lodash's merge method if you mean to extend the source object

var obj = {a: 25, b: {a: 1, b: 2}, c: 75};
var A = _.merge({}, obj, {newkey: "newvalue"});

Or you can use jQuery's extend method:

var obj = {a: 25, b: 50, c: 75};
var A = $.extend(true,{},obj);

Here is jQuery 1.11 extend method's source code :

jQuery.extend = jQuery.fn.extend = function() {
    var src, copyIsArray, copy, name, options, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;

        // skip the boolean and the target
        target = arguments[ i ] || {};
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    // extend jQuery itself if only one argument is passed
    if ( i === length ) {
        target = this;
        i--;
    }

    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );

                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
};

var item ={ 'a': 1, 'b': 2}
Object.assign({}, item);

UPDATE: 05/31/2023 a new global function was release that allows DEEP COPY called window.structuredClone()

Sign up to request clarification or add additional context in comments.

15 Comments

I'll be damned. Sometimes jQuery IS the answer. I was about to beat my head through writing a function that does pretty much this, and here it is.
Vanilla javascript quick and dirty var A = JSON.parse(JSON.stringify(obj));
mintedsky's solution works, but it feels so, so dirty...
Also you could create let a = {...obj}
WARNING: "When you have nested data in an array or object the spread operator will create a deep copy of the top most data and a shallow copy of the nested data." The Spread Operator: Deep and Shallow copies - so I still use the JSON.parse method.
|
155

While this isn't cloning, one simple way to get your result is to use the original object as the prototype of a new one.

You can do this using Object.create:

var obj = {a: 25, b: 50, c: 75};
var A = Object.create(obj);
var B = Object.create(obj);

A.a = 30;
B.a = 40;

alert(obj.a + " " + A.a + " " + B.a); // 25 30 40

This creates a new object in A and B that inherits from obj. This means that you can add properties without affecting the original.

To support legacy implementations, you can create a (partial) shim that will work for this simple task.

if (!Object.create)
    Object.create = function(proto) {
        function F(){}
        F.prototype = proto;
        return new F;
    }

It doesn't emulate all the functionality of Object.create, but it'll fit your needs here.

10 Comments

I think that A doesn't directly contain b with this solution, so it's a very different object.
+1 for Object.create(...). So if I receive a JSON string rapresenting an object and I want to process it and return a new object of the same type, Object.create is all that I need.
@dystroy: Yes, that is explained in my answer. "While this isn't cloning,..." and "This creates a new object in A and B that inherits from obj."
@ADC: If the JSON has already been parsed, then yes, you can create new objects that inherits from the original so that you can make local modifications without affecting the original. But you should be aware that if the JSON has nested structures, you would need to traverse through and do the same for all nested objects.
I wanted to point what is maybe not clear : you can't iterate on own properties of A like you do on obj. But I agree that, depending on what OP wants to do with his objects, this may be a solution.
|
18

You could define a clone function.

I use this one :

function goclone(source) {
    if (Object.prototype.toString.call(source) === '[object Array]') {
        var clone = [];
        for (var i=0; i<source.length; i++) {
            clone[i] = goclone(source[i]);
        }
        return clone;
    } else if (typeof(source)=="object") {
        var clone = {};
        for (var prop in source) {
            if (source.hasOwnProperty(prop)) {
                clone[prop] = goclone(source[prop]);
            }
        }
        return clone;
    } else {
        return source;
    }
}

var B = goclone(A);

It doesn't copy the prototype, functions, and so on. But you should adapt it (and maybe simplify it) for you own need.

6 Comments

There is no need to use a different method for "cloning" an Array to cloning any other object. If you use a loop from 0 to length, then the copy may have properties that don't exist on the original array so for..in is a better choice in this case. Also, there is no need to test source[prop], why not copy falsey properties?
You might use source.constructor to avoid a test (I didn't use it because this is adapted from a code in which I wanted to be sure to remove prototype) but I fail to see how this would add unwanted properties. I've made a fiddle.
@dystroy: It would if it's a sparse Array. Also, sometimes an Array has extra non numeric properties that are carried along for convenience. A numeric iteration will fail to clone those.
...oh wait, maybe not, but you are skipping all "falsey" values in the Array because of the if(source[i]), so it handles the sparse array, but in a destructive manner. You should be doing if (i in source) or if (source.hasOwnProperty(i))
@user1689607 Look at my fiddle : it was made with a sparse array. I fixed for the falsey values (my original code was designed to remove null, false, 0 and so on for lighter communication with a Go server...).
|
10

A and B reference the same object, so A.a and B.a reference the same property of the same object.

Edit

Here's a "copy" function that may do the job, it can do both shallow and deep clones. Note the caveats. It copies all enumerable properties of an object (not inherited properties), including those with falsey values (I don't understand why other approaches ignore them), it also doesn't copy non–existent properties of sparse arrays.

There is no general copy or clone function because there are many different ideas on what a copy or clone should do in every case. Most rule out host objects, or anything other than Objects or Arrays. This one also copies primitives. What should happen with functions?

So have a look at the following, it's a slightly different approach to others.

/* Only works for native objects, host objects are not
** included. Copies Objects, Arrays, Functions and primitives.
** Any other type of object (Number, String, etc.) will likely give 
** unexpected results, e.g. copy(new Number(5)) ==> 0 since the value
** is stored in a non-enumerable property.
**
** Expects that objects have a properly set *constructor* property.
*/
function copy(source, deep) {
   var o, prop, type;

  if (typeof source != 'object' || source === null) {
    // What do to with functions, throw an error?
    o = source;
    return o;
  }

  o = new source.constructor();

  for (prop in source) {

    if (source.hasOwnProperty(prop)) {
      type = typeof source[prop];

      if (deep && type == 'object' && source[prop] !== null) {
        o[prop] = copy(source[prop]);

      } else {
        o[prop] = source[prop];
      }
    }
  }
  return o;
}

2 Comments

"copy" is a reserved word.
@ChrisScott—not in the current version of ECMA-262 or the next one.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.