tau
tau

Reputation: 6749

Pass function as argument

How can I get this function to pass by reference with this code?

var Class = function() {
    var callback1;
    var callback2;

    function buildStuff(data, callback) {
        element.onclick = function() {
            doStuff(callback);
        };
    }

    function doStuff(callback) {
        callback();
    }

    return {
        "setCallback1":function(fn) {
            callback1 = fn;
        },
        "setCallback2":function(fn) {
            callback2 = fn;
        },
        //may rebuild with different data, but same callback
        "buildFoo":function(data) {
            buildStuff(data, callback1);
        },
        //may rebuild with different data, but same callback
        "buildBar":function(data) {
            buildStuff(data, callback2);
        }
    };
}

function main() {
    var object = Class();
    object.setCallback1(function() {
        //do stuff
    });
    object.setCallback2(function() {
        //do something else
    });
}

When you actually click on the element, callback is undefined. I would expect it to be the anonymous function I set it to with the setCallback function because the user click occurs after I call the setCallback function.

Thanks!

EDIT: Thanks for the input. I should have mentioned I need to be able to dynamically set what callback equals. So, I can't just eliminate the callback parameter from buildStuff.

EDIT2: Very sorry for the confusion; I realize my example was a bit too out of context to show what I am doing. buildStuff is actually a private member function (using the module pattern) that is called repeatedly. Depending on what is being built, it needs a different callback. The callback is actually set outside of the class (well, module pattern class), so it has to be dynamic. I've updated my code, and again, sorry for the bad example.

Upvotes: 0

Views: 92

Answers (2)

Matt
Matt

Reputation: 75307

The click handler you create in buildStuff creates a closure over the local variables. You pass callback to your buildStuff function, but at the time you pass it, it's undefined. As this shadows the other callback variable, you always see this value of undefined, rather than the state of the other callback variable.

Instead, don't pass a parameter to buildStuff, and the closure will be created, and will capture the callback variable you want.

function buildStuff() {
    element.onclick = function() {
        doStuff(callback);
    };
}

Imagine this;

  1. Your global variable callback points to a value (in this case undefined).

  2. When you buildStuff in main(), you pass the value pointed to by callback (undefined) as a parameter to buildStuff

  3. Your click handler creates a closure over local variables + other variables in scope (note that the local callback shadows the global callback). callback in your event handler is now undefined.

  4. You then setCallback. setCallback changes the value the global callback variable points to using the = operator. The global callback and local callback now point to different values, which is why you don't see the callback in the event handler update.

    What you want to do in this situation is to change the value pointed to by callback, so other variables pointing there also update, but JavaScript doesn't let you do this.

Upvotes: 3

Paul Roub
Paul Roub

Reputation: 36438

Yes, but you've already called buildStuff before setCallback.

The contents of callback at the time (undefined) will be used.

If you want to call buildStuff with different callbacks, just do that, and eliminate the redundant setCallback:

function buildStuff(callback) {
    element.onclick = function() {
        doStuff(callback);
    };
}

function doStuff(callback) {
    callback();
}

function main() {
    buildStuff(  
      function() {
        //do something
      }
    );
}

Upvotes: 2

Related Questions