user2888100
user2888100

Reputation: 33

How to pass the 'callbacks' array argument to an anonymous function?

I have a member function in the object which gets the array of callback functions and the name of the event for which this function is set:

...
setHandlesByList: function (list) {
    for (var i in list) {
        var self = this;
        $(document).on(list[i].name, function (e) {
            list[i].callBack.call(self,e)
        });
    };
},
...

Somewhere in the child objects I have a call to this function of the parent object:

...
initClass: function () {
    this.setHandlesByList([
     { name: 'configChecked', callBack: onConfigChecked },
     { name: 'configExpired', callBack: onConfigExpired },
    ]);
},
onConfigChecked: function() {
    // some code
},
onConfigExpired: function() {
    // some code
},
....

but something goes wrong - for all events the handler is the last set callback function...

Upvotes: 0

Views: 127

Answers (2)

ScalaWilliam
ScalaWilliam

Reputation: 741

Try the following:

setHandlesByList: function (list) {
    for ( var i = 0; i < list.length; i++ ) {
        addCallback(list[i].name, list[i].callback);
    }
    function addCallback(on, name, callback) {
        $(document).on(name, function(e) { callback.call(on, e); });
    }
},

There is a problem with your scoping, because the value of i eventually ends up being the last value of i when your callbacks are evaluated.

Also note that you could use list.forEach.

Upvotes: 3

T.J. Crowder
T.J. Crowder

Reputation: 1075875

Each event handler function you create in this code:

setHandlesByList: function (list) {
    for (var i in list) {
    var self = this;
    $(document).on(list[i].name, function (e) {
        list[i].callBack.call(self,e)
        });
    };
},

...has an enduring reference to list and i, not copies of them as of when the function is created. Since i ends up being the last property enumerated, all handlers end up referring to the same list entry.

Instead, create a builder function to create the callback (sorry, I can't recreate the indentation style you use, I've just used a fairly standard one):

setHandlesByList: function (list) {
    var self = this;

    for (var i in list) {
        $(document).on(list[i].name, buildHandler(list[i]));
    };

    function buildHandler(entry) {
        return function (e) {
            entry.callBack.call(self,e)
        };
    }
},

Now, the function created closes over entry, the argument to the buildHandler call, rather than over list and i. Since entry (the argument) doesn't change, the handler works.

Note also that I've moved the var self = this; out of the loop, as it didn't vary from iteration to iteration and so had no business being in the loop.


Side note: You've said that the function receives an array. If so, for-in (with no safeguards) is not the correct way to loop through the entries in that array. More: Myths and realities of for..in

Upvotes: 2

Related Questions