I ran into an annoying issue with js callback scope recently and wanted to document it for myself somewhere. Callbacks are super simple at the core — passing a function as a parameter to another function.
callbackFn = function(){ alert("bam"); } firstFn = function(callback){ callback(); } firstFn(callbackFn);
That’s all well and good so long as everything is in the global scope. But that’s just bad practice.
So in OO js, you’d have something like this:
Obj = { callbackFn: function(){ alert("bam"); }, firstFn: function(callback){ callback(); }, secondFn: function(){ this.firstFn(this.callbackFn); } } Obj.secondFn();
This will actually be executing callbackFn in the global scope. The ‘this’ just gets lost. Note that you may only notice this if you’re expecting the this of callbackFn to find what’s in your obj (the above sample will probably work just fine).
Obj = { myalert: function(){ alert("bam!"); }, callbackFn: function(){ this.myalert(); }, firstFn: function(callback){ callback(); }, secondFn: function(){ this.firstFn(this.callbackFn); } } Obj.secondFn();
There, that one will fail.
The solution is to use the apply() function.
https://developer.mozilla.org/en-US/docs…
Obj = { myalert: function(){ alert("bam!"); }, callbackFn: function(){ this.myalert(); }, firstFn: function(callback, callbackObj){ callback.apply(callbackObj); }, secondFn: function(){ this.firstFn(this.callbackFn, this); } } Obj.secondFn();
And that will work as intended.
Function context is something that you always have to pay attention to in javascript and there’s a few ways to make sure you get the right context:
1) Pass both the callback function and context as parameters and then use
fn.apply(context, arguments)
.2) Wrap the callback function in a closure that contains a reference to the this context. Since this is a special variable in every function, you must use assign the value to a different variable name in order to use it within the wrapping function. A common idiom you’ll see is:
var that = this
ORvar self = this
, depending on your preference.3) Use a general-purpose bind function that takes a function and context and returns another function. It’s really easy to write one yourself, but why write one when most major libraries like jQuery, Underscore/Lodash, etc already provide one for you to use:
– http://lodash.com/docs#bind
– http://api.jquery.com/jQuery.proxy/
Here’s a snippet showing methods #1-3 with the original example: http://pastebin.com/icDeqQWD