Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function to modify object in arguments, not just a property

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?

like image 732
Max Bates Avatar asked May 13 '13 01:05

Max Bates


3 Answers

It'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.

like image 147
zerkms Avatar answered Oct 19 '22 17:10

zerkms


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
    // completely 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);
    }
};
like image 23
slebetman Avatar answered Oct 19 '22 16:10

slebetman


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"
like image 1
Brandon Avatar answered Oct 19 '22 15:10

Brandon