Friday, February 17, 2012

Javascript closure for callbacks



Closures are useful mostly when invoking callbacks.

Like in case of Youtube iframe API's, it expects a global function to be defined.

eg.,

var yt_player = new YT.Player('elementId', {
                              width : Xpx,
                              height : Xpx,
                              events : {
                              'onReady' : onPlayerReady,
                              'onError' : onPlayerError
                              }
                             }); 

Where the documentation for the method can be found here.
The methods onPlayerReady, onPlayerError are expected to be in global scope.

But if you have Javascript based prototyping, i.e., Object-ness in your code and one of your objects needs to have a Youtube player as one of its properties, the code would become something like

MyObject.prototype.readyEvent = function(e) {
  this.yt_player = e.target
};

MyObject.prototype.loadPlayer = function() {
  var that = this;
  // that to make JS point to MyObject
  var yt_player = new YT.Player('elementId', {
                                width: ...,
                                height: ...,
                                events : {
                                'onReady' : that.readyEvent
                                }
                               });
While you would expect this to run, your instance of MyObject would not have yt_player anything.

The reason is the callback expects a global function, where this refers to DOMWindow object.

If you put a client side debugger through the browser in the readyEvent function, it will stop there, but if you do an inspection on this, it will say DOMWindow.

Javascript closure to the rescue. Your code would have to be like :


MyObject.prototype.loadPlayer = function() {
  var that = this;
  // that to make JS point to MyObject
  var yt_player = new YT.Player('elementId', {
                                width: ...,
                                height: ...,
                                events : {
                                'onReady' : (function(){
                                  var obj = that;
                                  return function(e) {
                                    obj.readyEvent(e);
                                  }
                                })()
                               });

The return function inside the onReady event returns a function which when gets executed, makes a call to the desired function (readyEvent in this case) of the instance of MyObj (obj in this case).

No comments:

Post a Comment