I'm sure this has to exist somewhere but I haven't been able to find it...
I'm trying to write a function that takes an object as an argument and updates its reference. Not a property of the reference, and not reassign the object, but update the whole reference.
Note that PubSub is in there just to demonstrate need for asynchronicity and flexibility in types of objects passed in and updated.
Best explained by example:
//ideally how function would work
function watch(event, obj) {
PubSub.on(event, function(model) {
// I want to update the entire object
// I understand that currently, this is just reassigning
// I know I can do obj.prop = model, but that's not what I want to do
obj = model;
}
};
//example usage
var myObj = {"name" : "Tom"};
watch("anEvent", myObj);
console.log(myObj.name); //still "Tom"
// time passes, then somewhere
PubSub.trigger("anEvent", {"name" : "John"})
console.log(myObj.name); // now should read "John"
Is this the sort of scenario where a bind()
method, e.g. $.bind()
or currying would be useful, or is that just for using "this" in the appropriate context?
I'm sure this has to exist somewhere but I haven't been able to find it...
I'm trying to write a function that takes an object as an argument and updates its reference. Not a property of the reference, and not reassign the object, but update the whole reference.
Note that PubSub is in there just to demonstrate need for asynchronicity and flexibility in types of objects passed in and updated.
Best explained by example:
//ideally how function would work
function watch(event, obj) {
PubSub.on(event, function(model) {
// I want to update the entire object
// I understand that currently, this is just reassigning
// I know I can do obj.prop = model, but that's not what I want to do
obj = model;
}
};
//example usage
var myObj = {"name" : "Tom"};
watch("anEvent", myObj);
console.log(myObj.name); //still "Tom"
// time passes, then somewhere
PubSub.trigger("anEvent", {"name" : "John"})
console.log(myObj.name); // now should read "John"
Is this the sort of scenario where a bind()
method, e.g. $.bind()
or currying would be useful, or is that just for using "this" in the appropriate context?
- 2 Should questions include "tags" in their titles? No. – timss Commented May 13, 2013 at 1:21
- See my answer to this related question for a detailed explanation of how arguments are passed in js: stackoverflow./questions/13506398/… – slebetman Commented May 13, 2013 at 1:48
4 Answers
Reset to default 8It's not possible.
What confuses you is that in js runtime you don't have a way to manage the actual reference values, but only can assign some reference value to a variable.
That's it - you can "attach" another reference to a given variable name, but you cannot modify the reference value.
To clear what I said above: the memory for object is allocated on its initialization
var o = {};
after that the reference to an unnamed object (objects don't have names, variables do) is assigned to a o
variable. After that you cannot re-allocate another object to the same place in memory.
I'm going to assume that you've read my explanation at: Why are objects' values captured inside function calls?.
If you understand what's going on. And you think about it hard enough. You'll see an obvious work around.
So, contrary to everyone else's answer I'm going to say: yes, it's possible (within limits).
The Work-around
First, a recap. You realize that what you have is actually two separate references to the same object? Assigning one reference will not cause the other reference to be reassigned:
var foo = {some : 'values'};
(function (bar) {
// 'bar' in here points to the
// same object as foo but is a
// pletely different reference
})(foo)
// is it obvious yet?
So, while the straight-forward bar = new_object
won't work. Realize that both references point to the same object at the point you're interested in.
Which means that, even though you have no access to the original reference ('foo' in my example, 'myObj' in your example) what you do have is the object it points to. Which is what we can exploit.
Provided that you don't care what happens to the current object and provided that you don't mind that all references to the object get modified you can simply do a deep delete of all it's attributes and replace them with a deep copy of the new object.
So, while you can't do:
obj = model;
you can do:
for (var n in obj) {
if (obj.hasOwnProperty(n)) {
delete(obj[n]);
}
}
for (var n in model) {
if (model.hasOwnProperty(n)) {
obj[n] = model[n];
}
}
and your code should work as you expect it to.
If you're doing this a lot (or if you just want to make your code more readable) you can encapsulate this logic:
function reassignObjRef (ref, new_obj) {
for (var n in ref) {
if (ref.hasOwnProperty(n)) {
delete(ref[n]);
}
}
for (var n in new_obj) {
if (new_obj.hasOwnProperty(n)) {
ref[n] = new_obj[n];
}
}
}
So you can simply do:
function watch(event, obj) {
PubSub.on(event, function(model) {
reassignObjRef(obj,model);
}
};
References are just variables, like primitives. Reassigning it is changing its value.
You say
and not reassign the object, but update the whole reference.
Problem is that those mean the same thing. If you mean update another reference which points to this object to point to something else, that can only be done if the other reference is in your scope.
There is no way to say, given a reference to a particular object, change all references to that object to point to some other object. It sounds like that is what you are trying to do. Sorry, not possible. You would need an intermediate object with a property which is the reference you want to change.
//ideally how function would work
function watch(event, obj) {
PubSub.on(event, function(model) {
// I want to update the entire object
// I understand that currently, this is just reassigning
// I know I can do obj.prop = model, but that's not what I want to do
obj.model = model;
}
};
//example usage
var myObj = {model: {"name" : "Tom"}};
watch("anEvent", myObj);
console.log(myObj.model.name); //still "Tom"
// time passes, then somewhere
PubSub.trigger("anEvent", {"name" : "John"})
console.log(myObj.name); // now should read "John"
There is no way to know, or find, all of the references to an object, so there is no general way to re–assign all of the references to some other object. You can do it statically with closures and get
and set
methods (see Douglas Crockford's Private Members in Javascript), but that doesn't seem to be what you want.
Also, bind is specific to a function's this variable, which is one parameter of a function's execution context. It's not related to scope.