How to Copy JavaScript Object to New Variable Not by Reference

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

I've found that the following works if you're not using jQuery and only interested in cloning simple objects (see comments).

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

Documentation

  • JSON.parse()
  • JSON.stringify()

How do I correctly clone a JavaScript object?

2022 update

There's a new JS standard called structured cloning. It works in many browsers (see Can I Use).

const clone = structuredClone(object);

Old answer

To do this for any object in JavaScript will not be simple or straightforward. You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance. If, for instance, you are adding a clone method to Object.prototype, as some answers depict, you will need to explicitly skip that attribute. But what if there are other additional methods added to Object.prototype, or other intermediate prototypes, that you don't know about? In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty method.

In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties. For example, prototype is a hidden property of a function. Also, an object's prototype is referenced with the attribute __proto__, which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes. I think __proto__ might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically.

Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly. If your source object's prototype is Object, then simply creating a new general object with {} will work, but if the source's prototype is some descendant of Object, then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty filter, or which were in the prototype, but weren't enumerable in the first place. One solution might be to call the source object's constructor property to get the initial copy object and then copy over the attributes, but then you still will not get non-enumerable attributes. For example, a Date object stores its data as a hidden member:

function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
var d2 = clone(d1);
alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

The date string for d1 will be 5 seconds behind that of d2. A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class. I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!

When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object, Array, Date, String, Number, or Boolean. The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing. I further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list. This can be accomplished with code like the following:

function clone(obj) {
var copy;

// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;

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

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

// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}

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

The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure. That is, there isn't more than one reference to the same data in the object. For example:

// This would be cloneable:
var tree = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"right" : null,
"data" : 8
};

// This would kind-of work, but you would get 2 copies of the
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cyclicGraph["right"] = cyclicGraph;

It will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it.

Modifying a copy of a JavaScript object is causing the original object to change

It is clear that you have some misconceptions of what the statement var tempMyObj = myObj; does.

In JavaScript objects are passed and assigned by reference (more accurately the value of a reference), so tempMyObj and myObj are both references to the same object.

Here is a simplified illustration that may help you visualize what is happening

// [Object1]<--------- myObj

var tempMyObj = myObj;

// [Object1]<--------- myObj
// ^
// |
// ----------- tempMyObj

As you can see after the assignment, both references are pointing to the same object.

You need to create a copy if you need to modify one and not the other.

// [Object1]<--------- myObj

const tempMyObj = Object.assign({}, myObj);

// [Object1]<--------- myObj
// [Object2]<--------- tempMyObj

Old Answer:

Here are a couple of other ways of creating a copy of an object

Since you are already using jQuery:

var newObject = jQuery.extend(true, {}, myObj);

With vanilla JavaScript

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 newObject = clone(myObj);

See here and here

JS object copy by value vs copy by reference

I'd rather think of variables with objects as pointers to objects (like C pointers) rather than references.

In your third line, you just replaced objA, making it "point to" another object. It does not change whatever objB is "pointing".

By line 3, objA now points to {a:2} while objB still points to whatever objA was pointing at the time you assigned it to objB, at line 2, which is {a:1}.

line 1: objA -> {a:1}
line 2: objA -> {a:1} <- objB
line 3: objA -> {a:2}, objB -> {a:1}

Copy a variable's value into another

It's important to understand what the = operator in JavaScript does and does not do.

The = operator does not make a copy of the data.

The = operator creates a new reference to the same data.

After you run your original code:

var a = $('#some_hidden_var').val(),
b = a;

a and b are now two different names for the same object.

Any change you make to the contents of this object will be seen identically whether you reference it through the a variable or the b variable. They are the same object.

So, when you later try to "revert" b to the original a object with this code:

b = a;

The code actually does nothing at all, because a and b are the exact same thing. The code is the same as if you'd written:

b = b;

which obviously won't do anything.

Why does your new code work?

b = { key1: a.key1, key2: a.key2 };

Here you are creating a brand new object with the {...} object literal. This new object is not the same as your old object. So you are now setting b as a reference to this new object, which does what you want.

To handle any arbitrary object, you can use an object cloning function such as the one listed in Armand's answer, or since you're using jQuery just use the $.extend() function. This function will make either a shallow copy or a deep copy of an object. (Don't confuse this with the $().clone() method which is for copying DOM elements, not objects.)

For a shallow copy:

b = $.extend( {}, a );

Or a deep copy:

b = $.extend( true, {}, a );

What's the difference between a shallow copy and a deep copy? A shallow copy is similar to your code that creates a new object with an object literal. It creates a new top-level object containing references to the same properties as the original object.

If your object contains only primitive types like numbers and strings, a deep copy and shallow copy will do exactly the same thing. But if your object contains other objects or arrays nested inside it, then a shallow copy doesn't copy those nested objects, it merely creates references to them. So you could have the same problem with nested objects that you had with your top-level object. For example, given this object:

var obj = {
w: 123,
x: {
y: 456,
z: 789
}
};

If you do a shallow copy of that object, then the x property of your new object is the same x object from the original:

var copy = $.extend( {}, obj );
copy.w = 321;
copy.x.y = 654;

Now your objects will look like this:

// copy looks as expected
var copy = {
w: 321,
x: {
y: 654,
z: 789
}
};

// But changing copy.x.y also changed obj.x.y!
var obj = {
w: 123, // changing copy.w didn't affect obj.w
x: {
y: 654, // changing copy.x.y also changed obj.x.y
z: 789
}
};

You can avoid this with a deep copy. The deep copy recurses into every nested object and array (and Date in Armand's code) to make copies of those objects in the same way it made a copy of the top-level object. So changing copy.x.y wouldn't affect obj.x.y.

Short answer: If in doubt, you probably want a deep copy.

Assign value not reference in javascript

You can use Object.assign:

var fruit = {   name: "Apple"};
var vegetable = Object.assign({}, fruit);vegetable.name = "potatoe";console.log(fruit);

How can I clone a JavaScript object except for one key?

There is a Destructuring assignment syntax in JavaScript that can be used

let obj = {a: 1, b: 2, c: 3, z:26};
let {b, ...rest} = obj;

// skips the "Unused variable" warning
let {b: _, ...rest} = obj;

// removes property based on the dynamic key
const dynamicKey = "b";
let {[dynamicKey]: _, ...rest} = obj;

Modern browsers already support it out of the box.
See: JavaScript operator: Destructuring assignment: Rest in objects

For old browser versions there is an option to use Babel to support destructuring assignment. It will be transpiled into:

"use strict";

function _objectWithoutProperties(obj, keys) {
var target = {};
for (var i in obj) {
if (keys.indexOf(i) >= 0) continue;
if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
target[i] = obj[i];
}
return target;
}

var x = { a: 1, b: 2, c: 3, z: 26 };
var b = x.b;

var y = _objectWithoutProperties(x, ["b"]);

Why reassigning of an object have different value?

Objects are stored by reference and not by value.

let a = { name:'Pete' };

The line above creates an object in memory and variable a stores a reference to this object.

let b = a

When you make b equal to a, the variable b also stores the reference to the same object.

Now, whenever you make any changes to the object, it will reflect in both the variables a and b, because they both store the reference to the same object.

Sample Image

Now, when you do a = {}, this creates a new object (empty) in memory and variable a now stores a reference to this new object. But this would not change variable b because it stores a reference to the first object which hasn't changed.

Sample Image



Related Topics



Leave a reply



Submit