JavaScript: How to pass object by value? - javascript

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?

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

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.

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)

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

Use this
x = Object.create(x1);
x and x1 will be two different object,change in x will not change x1

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.

ES6
Using the spread operator like obj2 = { ...obj1 }
Will have same values but different references
ES5
Use Object.assign obj2 = Object.assign({}, obj1)

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

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 :
http://api.jquery.com/jquery.extend/
https://stackoverflow.com/a/122704/1257652
and to dig into the source.. http://james.padolsey.com/jquery/#v=1.8.3&fn=jQuery.extend

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

I needed to copy an object by value (not reference) and I found this page helpful:
What is the most efficient way to deep 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);

With the ES6 syntax:
let obj = Object.assign({}, o);

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() {}
};
}

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);
https://lodash.com/docs#merge
https://www.npmjs.com/package/lodash.merge

Related

Purpose of moving data through JSON

JSON.stringify(value) will convert a JavaScript value to JSON string. JSON.parse(value) will convert a valid JSON string into a JavaScript value (object, array or other JSON-available primitive).
Why would you take a JavaScript value and move it through JSON ? Means take a value then stringify it and then parse it back. It serves no purpose to my knowledge and just wastes machine resources.
I'm asking this is because I've come across this function:
function ser(value) {
return value == null ? null : JSON.parse(JSON.stringify(value))
}
it's found in crow-tech.js in sandbox of Chapter 11 of book Eloquent JavaScript by Marijn Heverbeke and I'm wondering why would he wanna do that!
This is a cheap way to deep clone an object in JavaScript. Consider the following:
function a() {
const obj = {name: "fred"};
b(obj);
console.log(obj);
}
function b(obj) {
obj.age = 42;
}
a();
The function a passes an object to b and when it gets modified. Sometimes that is what you want, other times, you want to preserve the original object from modifications, so you have to clone it. JavaScript doesn't have any facility to do that but JSON.stringify -> JSON.parse will create a new object for you so it be used for cloning:
function a() {
const obj = {name: "fred"};
b(JSON.parse(JSON.stringify(obj)));
console.log(obj);
}
function b(obj) {
obj.age = 42;
}
a();
This is a good illustration of what can get wrong, but in the real world, it's not always so simple. The above can also be done through a shallow clone of the object
function a(){
const obj = {name: "fred"};
b(Object.assign({}, obj)); //shallow clone
console.log(obj);
}
function b(obj) {
obj.age = 42;
}
a();
However, that will fail in the following scenario:
const obj = {
name: "fred",
child: { name: "pebbles" }
};
const objClone = Object.assign({}, obj)
objClone.age = 42;
objClone.child.age = 2;
console.log('the "cloned" object was modified', objClone);
console.log("so was the original nested object", obj);
This is because the nested object child was not cloned, so we modified the top-level clone of the parent but then modified the original child. Here is when deep cloning is useful:
const obj = {
name: "fred",
child: { name: "pebbles" }
};
const objClone = JSON.parse(JSON.stringify(obj));
objClone.age = 42;
objClone.child.age = 2;
console.log('the cloned object was modified', objClone);
console.log("none of the original was", obj);
This doesn't solve all problems. JSON.stringify and then JSON.parse only works with very simple objects anywhere, it will not copy prototypes or functions. Some examples:
const obj = { name: "Alice" };
const proto = { age: 42 };
Object.setPrototypeOf(obj, proto);
console.log("assembled object", obj);
const clone = JSON.parse(JSON.stringify(obj));
console.log("cloned object", clone);
const objWithFunction = { name: "Bob", getAge: function() { return 42; } };
console.log("object with function assigned to property", objWithFunction);
console.log("getAge from original", objWithFunction.getAge());
const cloneWithFunction = JSON.parse(JSON.stringify(objWithFunction));
console.log("cloned object with function assigned to property", cloneWithFunction);
console.log("getAge from clone", cloneWithFunction.getAge());
function Person(name, age) {
this.name = name;
this.age = age;
}
const p = new Person("Carol", 42);
console.log("what is p:", p);
console.log("is p a Person?", p instanceof Person);
const clone = JSON.parse(JSON.stringify(p));
console.log("what is clone:", clone);
console.log("is clone a Person?", clone instanceof Person);
console.log("the clone is actually a plain object:", Object.getPrototypeOf(clone) == Object.prototype);
JSON.parse(JSON.stringify(value))
This is mostly used to clone an object. You cannot directly copy a object to another variable because objects are copied by reference.
let user = {name : "John"}
let newUser = user; // newUser and user refer to same object (ie same memory location.) .
If you modify user then newUser will also get modified. This is because objects are copied via reference not by value.
So to do a object copy we use the method you specified.
Example
let value = {"name": "John"}; // this is a object.
JSON.stringify(value) // were are changing the type object to string
Strings can be copied by value.
Now are we need to again convert string to Object, for that we use
let newValue = JSON.parse(JSON.stringify(value));
This returns a object - a clone of value
Now value and newValue are two different objects.

Best practices for stringifying JSON inner objects

Basically I have something like this:
MyClass
var MyClass = function() {
this.num = 123;
this.obj = new MyInnerClass();
};
MyClass.prototype.stringify = function() {
return JSON.stringify(this);
};
MyInnerClass
var MyInnerClass = function() {
this.foo = 456;
this.bar = 'bonjour!';
};
MyInnerClass.prototype.stringify = function() {
return JSON.stringify(this, function(k, v) {
// ignores 'foo' attribute
return k !== 'foo' ? v : undefined;
});
};
Each class has its own stringify implementation, so when I do:
var mc = new MyClass();
mc.stringify();
I would like something like calling MyClass.stringify should stringify my mc object, but respecting inner objects stringify implementations. Once we don't have control over the JSON.stringify method logic, is there a good way to do that?
Thank you!
If you look on MDN at JSON.stringify, you'll see a section that talks about a toJSON property
If an object being stringified has a property named toJSON whose value is a function, then the toJSON() method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON() method when called will be serialized.
Basically, define a toJSON method for your Object which creates another Object, but one that can be serialised as you desire. Then JSON.stringify will serialise the return of your toJSON function instead, i.e.
var MyClass = function() {
this.num = 123;
this.obj = new MyInnerClass();
};
var MyInnerClass = function() {
this.foo = 456;
this.bar = 'bonjour!';
};
MyInnerClass.prototype.toJSON = function () {
// shallow clone `this`, except .foo
var o = Object.create(null), k, blacklist = ['foo'];
for (k in this) // loop over enumerable properties
if (Object.prototype.hasOwnProperty.call(this, k)) // ignore inherited properties
if (blacklist.indexOf(k) === -1) // ignore blacklisted properties
o[k] = this[k]; // add to our clone
return o;
};
JSON.stringify(new MyClass()); // '{"num":123,"obj":{"bar":"bonjour!"}}'
This will also replace your need for the current stringify method.
Sadly you can't call JSON.stringify(this) inside .toJSON because it becomes circular and you get RangeError: Maximum call stack size exceeded, but you'd not get the desired result this way anyway as it would be serialised a second time, giving you a String in your JSON.

JS - Create object shorthand

Take the below object:
var value = 'bar';
var obj = { foo: value }
// -> Object { foo="bar" }
Supposing the key was also a variable, one could go:
var key = 'foo', value = 'bar';
var obj = {}
obj[key] = value;
// -> Object { foo="bar" }
Now, I want to do this in one line (shorthand). So I tried:
var obj = {}[key] = value; // or,
var obj = ({})[key] = value; // or,
var obj = new Object()[key] = value;
// -> "bar"
This oddly produces a String instead of an Object.
Is there any way to do this shorthand?
ECMAScript 6 will allow you to do
var obj = {
[key]: value
};
Browser support is not great yet, but transpilers such as 6to5 allow you to use this notation today!
You can, or almost can. If you put the creation of the object, and its assignment to obj in parentheses, you can set a property of the result of that expression:
var obj, key='foo', value = 'bar';
(obj = {})[key] = value;
alert(obj); // [object Object]
alert(obj.foo); // bar
The var keyword cannot be included in that expression within parentheses, so either you will have to declare obj before (like I did), or not declare it at all and accept that it will be in global scope.
Depends on what you call a one-liner, with some code golf you can do
var obj = (function(o) {o[key]=value; return o})({});
it think that's the shortest you'll get it ?
Something like that is coming in ECMAScript 6:
With ECMAScript 6, there is a shorter notation available to achieve
the same:
var a = "foo",
b = 42,
c = {};
// Shorthand property names (ES6)
var o = { a, b, c };
So you could create your object with the following code
var foo = 'bar';
var obj = {foo};
No shorthand in ECMAScript 5 (the current version), but you can make a method
function createObject(key, value /*, key, value, ... */ ) {
var object = {};
for (var i = 0; i < arguments.length; i = i + 2) {
object[arguments[i]] = arguments[i+1];
}
return object;
}
Then you can call it like this
var obj = createObject(key, value, 'anotherKey', 'anotherVal');

A object property value is showing while the other refrence object is null

var obj={name: "faizan"}
var obj2= obj;//obj2 pointing to the same memoray location of obj
console.log("before making null obj",obj2.name);
obj={}; //obj became null
console.log("after making null obj",obj2.name);//now this will need to be null but is working why??
i made object (obj) then assigned it to the second object (obj2) and finally make obj null
but after that obj2.name showing me "faizan". why? it need to show nothing cause obj null now
The way you believe it to work is incorrect. The second time you set obj = {}; you aren't nullifying the original object. You are instead creating an entirely new empty object, while obj2 still references the original.
You could achieve what you think by using a parent container:
var obj = { container: { name: 'faizan' } };
var obj2 = obj;
obj.container = {};
as you say :
obj={};
this way just refer the obj to a new reference in the heap , and the content
{name: "faizan"}
exsits in the heap as before , and obj2 is refer the {name: "faizan"} in the heap now .
if you want to deep copy obj to obj2 , just like this :
function copy(o){
var copy = Object.create( Object.getPrototypeOf(o) );
var propNames = Object.getOwnPropertyNames(o);
propNames.forEach(function(name){
var desc = Object.getOwnPropertyDescriptor(o, name);
Object.defineProperty(copy, name, desc);
});
return copy;
}
var obj={name: "faizan"}
var obj2 = copy(obj);
and then take obj refer null
obj = null
the GC will recycle obj automatically

How to set class name for JavaScript object

I have some test class
TestKlass = (function() {
function TestKlass(value) {
this.value = value != null ? value : 'hello world';
console.log(this.value);
}
return TestKlass;
})();
x = new TestKlass;
x instanceof TestKlass; (gives true)
I have new empty object
y = {}
y instanceof Object
Can I find any ways to set any properties for y, something like this
y.__proto__ = x.__proto__
y.constructor.prototype = x.constructor.prototype
for to have this result
y instanceof TestKlass => true
====================================================
UPD:
So. My main aim - it's to create CLONE function. Now my solution works for me. Please look at this code:
JavaScript JS object clone
Object._clone = function(obj) {
var clone, property, value;
if (!obj || typeof obj !== 'object') {
return obj;
}
clone = typeof obj.pop === 'function' ? [] : {};
clone.__proto__ = obj.__proto__;
for (property in obj) {
if (obj.hasOwnProperty(property)) {
value = obj.property;
if (value && typeof value === 'object') {
clone[property] = Object._clone(value);
} else {
clone[property] = obj[property];
}
}
}
return clone;
};
CoffeeScript JS object clone
# Object clone
Object._clone = (obj) ->
return obj if not obj or typeof(obj) isnt 'object'
clone = if typeof(obj.pop) is 'function' then [] else {}
# deprecated, but need for instanceof method
clone.__proto__ = obj.__proto__
for property of obj
if obj.hasOwnProperty property
# clone properties
value = obj.property
if value and typeof(value) is 'object'
clone[property] = Object._clone(value)
else
clone[property] = obj[property]
clone
Now you can try to do that
A = new TestKlass
B = Object._clone(A)
B instanceof TestKlass => true
It's work fine with Moz FF 13. But I think it's not cross-browser. and proto is deprecated.
I think there is no universal solution. But maybe It's will be helpful for somebody.
Perhaps you should read the following answer first. What you are trying to achieve is really simple. However before I explain let's look at your solutions:
Solution 1
y.__proto__ = x.__proto__;
I wouldn't recommend doing this as the __proto__ property is now deprecated. The above code won't work in Rhino.
Solution 2
y.constructor.prototype = x.constructor.prototype;
This is a very bad solution. The reason is that y.constructor is actually y.__proto__.constructor and since y is an object y.__proto__ is the same as Object.prototype. This means that y.constructor is the same as Object and you're essentially changing the prototype property of Object. This could potentially break other code on your page. I strongly discourage this practice.
Solution 3 (my solution)
What you want to do is simply change the internal [[proto]] property of an object. Since there's no cross platform way to do so we need to employ a hack. A simple solution would be to create a new object with the desired __proto__ property and set getters and setters on it to change the original object. It would be as follows:
function setPrototypeOf(object, prototype) {
var constructor = function () {
for (var key in object) {
if (object.hasOwnProperty(key)) {
(function (key) {
Object.defineProperty(this, key, {
get: function () {
return object[key];
},
set: function (value) {
object[key] = value;
},
enumerable: true
});
}).call(this, key);
}
}
};
constructor.prototype = prototype;
return new constructor;
}
After this all you need to do is:
y = setPrototypeOf(y, TestKlass.prototype);
Here is a working fiddle.
Edit:
The problem with your clone function is that it only clones objects and arrays. You forgot to account for functions which are also passed by reference. Thus when you clone an object which has methods which close over the internal state of the object, the object and its clone will share the same internal state. See the following fiddle:
function Value(value) {
this.get = function () {
alert(value);
};
this.set = function (newValue) {
value = newValue;
};
}
var a = new Value(5);
var b = Object._clone(a);
b.set(10); // setting b to 10 also sets a to 10
a.get(); // alerts 10, should alert 5
There's absolutely no way in JavaScript to clone the internal state of an object so cloning objects and arrays in JavaScript will only work for objects which do not expose closures as methods.
To know more about the problems with cloning objects in JavaScript read this answer. Click on the following link to read John Resig's answer.
obj instanceof SomeConstructor checks whether SomeConstructor is found anywhere in the prototype chain of obj.
If you want inheritance, you need to set the .prototype of FirstConstructor to a newly created SomeConstructor object.
SomeConstructor = function() {}
FirstConstructor = function() {}
FirstConstroctor.prototype = new SomeConstructor();
var y = new FirstConstroctor();
y instanceof FirstConstructor; // true
y instanceof SomeConstructor ; // true, it bubbles from FirstConstructor.__proto__ to SomeConstructor

Categories

Resources