3

I have a case where I have an object, "foo", that needs to call a callback on another object, "bar", and I need the context within the callback to be that of "bar". The kicker though, is that beyond getting its callback from "bar", "foo" should not know anyting about it.

Example:

var foo = 
{
    message: "Hello foo",
    doStuff: undefined
};

var bar =
{
    message: "Hello bar",
    callback = function ()
    {
        alert(this.message);
    }

};

foo.doStuff = bar.callback;

foo.doStuff();

I realize that normally you would use "call" or "apply" to switch the context to bar but in my particular case, at the time of calling foo.doStuff() I no longer have information about where the callback came from. So is there another way of figuring the context (say from within the callback function itself)?

2
  • 4
    foo.doStuff = bar.callback.bind(bar);. or foo.doStuff = function () {bar.callback.call(bar)}; which is basically what bind is doing behind the scenes. Commented Nov 21, 2012 at 19:12
  • bar.callback.call(bar) is functionally identical to bar.callback(). Commented Nov 21, 2012 at 19:24

3 Answers 3

1

So is there another way of figuring the context (say from within the callback function itself)?

Nope. Not without some extra specific before you pass the callback. If all you have is a function reference, you typically have no context. But with some prep, there are some solutions.

You could wrap the callback. When this is invoked, bar.callback() will be called in it's full form, preserving the context of bar.

foo.doStuff = function() { bar.callback(); };

Or in modern JS engines you can bind the callback to a specific context.

foo.doStuff = bar.callback.bind(bar);

Or use underscore.js to do that in all JS engines.

foo.doStuff = _(bar.callback).bind(bar);

Or make your own bind function if you want compatibility with old engines and dont want to use a whole library for it.

var bind = function(fn, context) {
  return function() {
    fn.call(context);
  };
};
foo.doStuff = bind(bar.callback, bar);

All that said, a simple wrapper is usually the simplest and most common. It's got few drawbacks, it's fast, easy to understand, and gives you lots of control over what gets invoked with what arguments. In this case, this form makes the most sense I think.

foo.doStuff = function() { bar.callback(); };
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, bind is exactly what I was looking for. Wrapping in a function would probably work as well but as I am retrieving the callbacks dynamically I do not know what parameters need to be passed (until that function is called) and so I can't provide them at the point of assigning the callback to the other object. The only scary part is MDN says that Safari mobile does not support bind (although some quick tests using jsfiddle seemed to prove otherwise...). At worst though, I will just have to add a polyfill to get it to work.
1

Use closure for bar.

var foo = {
    message: "Hello foo",
    doStuff: undefined
};

var bar = (function() {
    var message = "Hello bar";
    return {
        callback: function() {                
            alert(message);
        }
    };
})();


foo.doStuff = bar.callback;
foo.doStuff();
​

jsfiddle

Comments

0

I'd do it with a closure.

foo.doStuff = function(){
  bar.callback();
};

If you fear that the value of bar is changed between the moment you create the callback and the moment it is executed, you could "lock" the value of bar, but that might be overkill

foo.doStuff = (function(bar){
  return function(){
    bar.callback();
  }
})(bar);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.