JavaScript: How to pass object by value?
JavascriptJavascript Problem Overview
-
When passing objects as parameters, JavaScript passes them by reference and makes it hard to create local copies of the objects.
var o = {}; (function(x){ var obj = x; obj.foo = 'foo'; obj.bar = 'bar'; })(o)
o
will have .foo
and .bar
.
-
It's possible to get around this by cloning; simple example:
var o = {}; function Clone(x) { for(p in x) this[p] = (typeof(x[p]) == 'object')? new Clone(x[p]) : x[p]; } (function(x){ var obj = new Clone(x); obj.foo = 'foo'; obj.bar = 'bar'; })(o)
o
will not have .foo
or .bar
.
###Question
- Is there a better way to pass objects by value, other than creating a local copy/clone?
Javascript Solutions
Solution 1 - Javascript
Not really.
Depending on what you actually need, one possibility may be to set o
as the prototype of a new object.
var o = {};
(function(x){
var obj = Object.create( x );
obj.foo = 'foo';
obj.bar = 'bar';
})(o);
alert( o.foo ); // undefined
So any properties you add to obj
will be not be added to o
. Any properties added to obj
with the same property name as a property in o
will shadow the o
property.
Of course, any properties added to o
will be available from obj
if they're not shadowed, and all objects that have o
in the prototype chain will see the same updates to o
.
Also, if obj
has a property that references another object, like an Array, you'll need to be sure to shadow that object before adding members to the object, otherwise, those members will be added to obj
, and will be shared among all objects that have obj
in the prototype chain.
var o = {
baz: []
};
(function(x){
var obj = Object.create( x );
obj.baz.push( 'new value' );
})(o);
alert( o.baz[0] ); // 'new_value'
Here you can see that because you didn't shadow the Array at baz
on o
with a baz
property on obj
, the o.baz
Array gets modified.
So instead, you'd need to shadow it first:
var o = {
baz: []
};
(function(x){
var obj = Object.create( x );
obj.baz = [];
obj.baz.push( 'new value' );
})(o);
alert( o.baz[0] ); // undefined
Solution 2 - Javascript
Check out this answer https://stackoverflow.com/a/5344074/746491 .
In short, JSON.parse(JSON.stringify(obj))
is a fast way to copy your objects, if your objects can be serialized to json.
Solution 3 - Javascript
Here is clone function that will perform deep copy of the object:
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;
}
Now you can you use like this:
(function(x){
var obj = clone(x);
obj.foo = 'foo';
obj.bar = 'bar';
})(o)
Solution 4 - Javascript
Use Object.assign()
Example:
var a = {some: object};
var b = new Object;
Object.assign(b, a);
// b now equals a, but not by association.
A cleaner example that does the same thing:
var a = {some: object};
var b = Object.assign({}, a);
// Once again, b now equals a.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Solution 5 - Javascript
Use this
x = Object.create(x1);
x
and x1
will be two different object,change in x
will not change x1
Solution 6 - Javascript
You're a little confused about how objects work in JavaScript. The object's reference is the value of the variable. There is no unserialized value. When you create an object, its structure is stored in memory and the variable it was assigned to holds a reference to that structure.
Even if what you're asking was provided in some sort of easy, native language construct it would still technically be cloning.
JavaScript is really just pass-by-value... it's just that the value passed might be a reference to something.
Solution 7 - Javascript
Javascript always passes by value. In this case it's passing a copy of the reference o
into the anonymous function. The code is using a copy of the reference but it's mutating the single object. There is no way to make javascript pass by anything other than value.
In this case what you want is to pass a copy of the underlying object. Cloning the object is the only recourse. Your clone method needs a bit of an update though
function ShallowCopy(o) {
var copy = Object.create(o);
for (prop in o) {
if (o.hasOwnProperty(prop)) {
copy[prop] = o[prop];
}
}
return copy;
}
Solution 8 - Javascript
As a consideration to jQuery users, there is also a way to do this in a simple way using the framework. Just another way jQuery makes our lives a little easier.
var oShallowCopy = jQuery.extend({}, o);
var oDeepCopy = jQuery.extend(true, {}, o);
references :
Solution 9 - Javascript
Actually, Javascript is always pass by value. But because object references are values, objects will behave like they are passed by reference.
So in order to walk around this, stringify the object and parse it back, both using JSON. See example of code below:
var person = { Name: 'John', Age: '21', Gender: 'Male' };
var holder = JSON.stringify(person);
// value of holder is "{"Name":"John","Age":"21","Gender":"Male"}"
// note that holder is a new string object
var person_copy = JSON.parse(holder);
// value of person_copy is { Name: 'John', Age: '21', Gender: 'Male' };
// person and person_copy now have the same properties and data
// but are referencing two different objects
Solution 10 - Javascript
ES6
Using the spread operator like obj2 = { ...obj1 }
Will have same values but different references
ES5
Use Object.assign obj2 = Object.assign({}, obj1)
Solution 11 - Javascript
I needed to copy an object by value (not reference) and I found this page helpful:
https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-an-object-in-javascript. In particular, cloning an object with the following code by John Resig:
//Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);
Solution 12 - Javascript
With the ES6 syntax:
let obj = Object.assign({}, o);
Solution 13 - Javascript
When you boil down to it, it's just a fancy overly-complicated proxy, but maybe Catch-All Proxies could do it?
var o = {
a: 'a',
b: 'b',
func: function() { return 'func'; }
};
var proxy = Proxy.create(handlerMaker(o), o);
(function(x){
var obj = x;
console.log(x.a);
console.log(x.b);
obj.foo = 'foo';
obj.bar = 'bar';
})(proxy);
console.log(o.foo);
function handlerMaker(obj) {
return {
getOwnPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
// a trapping proxy's properties must always be configurable
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
getPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name); // not in ES5
// a trapping proxy's properties must always be configurable
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
getOwnPropertyNames: function() {
return Object.getOwnPropertyNames(obj);
},
getPropertyNames: function() {
return Object.getPropertyNames(obj); // not in ES5
},
defineProperty: function(name, desc) {
},
delete: function(name) { return delete obj[name]; },
fix: function() {}
};
}
Solution 14 - Javascript
If you are using lodash or npm
, use lodash's merge function to deep copy all of the object's properties to a new empty object like so:
var objectCopy = lodash.merge({}, originalObject);