Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the New York ColdFusion User Group (Jan. 2009) with:

Binding Javascript Method References To Their Parent Classes

By Ben Nadel on

NOTE: Dan G. Switzer, II caught a huge error in my original post. I was using call() rather than apply(). This has been updated in the following demonstration.

For those of you who have used Classes in Javascript, you probably have noticed that function references used within the class methods are not actually bound to the class instance in which they were defined. By that, I mean that if you do something like create an AJAX callback method, the callback method is not run in the context of the parent object, but rather in the context of the window. To get around this in the past, I have created local variables that point to the THIS scope and then referenced those in the callback methods:

  • var objSelf = this;
  •  
  • someMethod(
  • function(){
  •  
  • objSelf.DoSomething();
  •  
  • }
  • );

In this example, because the someMethod() callback function is defined inline within it's parent context and then passed out, it creates a closure. This allows the objSelf variable referenced within the callback method to actually refer to the original parent context. In case you are totally confused, what we are doing here is substituting objSelf for the "this" keyword since the "this" keyword will actually refer to the window object when the callback method gets executed (not the original parent context).

Yesterday, I came across an interesting "A List Apart" article titled, "Getting Out of Binding Situations in JavaScript". In it, the author Christophe Porteneuve demonstrates how to use the Javascript apply() method to bind a function reference to its parent context such that work-arounds like the one above don't have to be used. Below, I have put together a little demonstration of how this works:

  • <script type="text/javascript">
  •  
  • // Define the base class for all object classes.
  • function BaseObject(){
  • // Class properties.
  • }
  •  
  • // Method returns a class-bound vesion of the passed-in
  • // function; this will execute in the context of the
  • // originating object (this).
  • BaseObject.prototype.Bind = function( fnMethod ){
  • var objSelf = this;
  •  
  • // Return a method that will call the given method
  • // in the context of THIS object.
  • return(
  • function(){
  • return( fnMethod.apply( objSelf, arguments ) );
  • }
  • );
  • }
  •  
  •  
  • // ------------------------------------------------ //
  • // ------------------------------------------------ //
  •  
  •  
  • // Extend the base object.
  • Person.prototype = new BaseObject();
  •  
  • // Define person class.
  • function Person( strName ){
  • this.Name = strName;
  • }
  •  
  • // This method will wait a given amount of time and
  • // then ping the given person.
  • Person.prototype.Ping = function( objPerson ){
  • setTimeout(
  • this.Bind(
  • function(){
  •  
  • // Because this method is being bound,
  • // the THIS scope referenced below
  • // refers to the originating object, not
  • // the WINDOW object.
  • alert( "Hey " + objPerson.Name + ", it's " + this.Name );
  •  
  • }
  • ),
  • 2000
  • );
  • }
  •  
  •  
  • // ------------------------------------------------ //
  • // ------------------------------------------------ //
  •  
  •  
  • // Create two people.
  • var objBen = new Person( "Ben" );
  • var objSarah = new Person( "Sarah" );
  •  
  • // Call one of them shortly.
  • objBen.Ping( objSarah );
  •  
  • </script>

It's a small demo, but it's complex, so let's walk through it. First, I am creating a BaseObject class that will act as a base class for all other Javascript classes to extend (this extension is explicit, not implicit). I am doing this because I am trying to get more in the habit of thinking about objects. The base class has a single method: Bind():

  • BaseObject.prototype.Bind = function( fnMethod ){
  • var objSelf = this;
  •  
  • // Return a method that will call the given method
  • // in the context of THIS object.
  • return(
  • function(){
  • return( fnMethod.apply( objSelf, arguments ) );
  • }
  • );
  • }

This method takes a method reference and the returns an inline method definition that, in turn, executes the passed-in method in the context of the parent object. Now, it's true that we are still using the "objSelf" hack here, but because we are using it to bind the method reference to the parent object, we won't have to worry about it anywhere else (including the body of the original method reference).

Once we have this method in place, we can now use it to bind our callback function references to the parent object as in our Person.Ping() method:

  • Person.prototype.Ping = function( objPerson ){
  • setTimeout(
  •  
  • this.Bind(
  • function(){
  •  
  • // Because this method is being bound,
  • // the THIS scope referenced below
  • // refers to the originating object, not
  • // the WINDOW object.
  • alert( "Hey " + objPerson.Name + ", it's " + this.Name );
  •  
  • }
  • ),
  •  
  • 2000
  • );
  • }

Notice that in our setTimeout() method call, we are not passing our callback method directly in as the first argument; rather, we are passing the method reference to our bind method (this.Bind()), which will create a bound version of it and then pass that method in as the first setTimeout() argument. Because we are essentially decorating our method reference with this binding, we can now refer to the "this" keyword within our callback method and know that it will point to the original Person object instance.

Now, when we run the above code, we get the following alert:

Hey Sarah, it's Ben

Had we not bound our method, we would have gotten this:

Hey Sarah, it's undefined

This is because the callback method has no implicit binding to the parent Person instance; and, when it gets run, it would think that this.Name is referring to window.Name, which is undefined.

This is a really awesome technique and will allow us to use a more object-oriented mindset. However, I just wanted to caution about overusing it! A lot of Javascript libraries such as jQuery purposely set the THIS context of callback methods, such as when handling events or iterating over collections. In that case, I would say you don't want to override the explicit "this" context (or do so with a full understanding).




Reader Comments

Don't forget about the apply() method--which is even handier (and what I prefer:)

function Person(name){
this.name = name;
this.setAge = function (age, dob){
// if we have an onSetAge callback defined
if( !!this.onSetAge ) this.onSetAge.apply(this, arguments);
}
}

Person.prototype.onSetAge = function (){
// we get the same arguments passed to the setAge() method
console.log(arguments);
}

var bo = new Person("Dan");
bo.setAge("Dan", "Feb 28, 1972");

Actually, looking at your example and the use of fnMethod.call( objSelf, arguments ) I think you've confused apply() and call().

The difference is apply() takes an array of arguments as the second argument, where call() uses the first argument as a reference to an object and then each additional argument is passed as it's own argument:

function test(p1, p2, p3){

}

function test2(p1, p2, p3){
test.call(this, p1, p2, p3);
}

As mentioned in the A List Apart article, Prototype has a great function called "bind" which helps with this sort of thing. Although, doesn't it look a bit weird?

<pre>
iterator.each(
function(e) {
this.something();
}.bind(this)
)<pre>

@Dan,

Oops! You are exactly correct. I have reversed the call() and the apply()! I agree - the apply() is way more useful. Great catch, thanks!

@Joe,

Yeah, I am not crazy about that notation; but, really cool if you are already using Prototype.

@Pratik,

Glad you found this helpful. Hopefully, one day, enough of the old browsers die out that we can start using the native Function.prototype.bind() feature in HTML5:

http://kangax.github.io/compat-table/es5/#Function.prototype.bind