Method & Function Binding in Prototype Javascript

Over the past few days, I’ve been asked by several colleagues about Prototype’s bind() method; what exactly it does and when it should be used. In short, method binding in prototype allows you control the object that the keyword this references within a given context. Binding is a fairly complicated topic that, as I’m writing this post, can be as difficult to explain as it is to understand. However, once you grasp binding, it will seem perfectly natural to you.

To understand binding, you first have to understand a fundamental concept of Javascript: everything is an object. Every function, every element, every string, every array; at their basic level, they are all objects.

The next thing you must understand is what the keyword this means in object-oriented programming; this always refers to the current object. Consider this snippet:

Class TestClass{
  private int testInt = 0;
  public int getTestInt(){
    return this.testInt;
  }
  public void setTestInt(int myInput){
    this.testInt = myInput;
  }
}

In this brief Java example, we can see the use of the this keyword. In plain english, this corresponds to the object saying “This is my value.”

This is where it becomes important to remember that everything in Javascript is an object. That means any time we create a function, we are creating an object. The keyword this will refer to the function; not its class. So when does this affect us using prototype? Most often, if affects us any time we use a closure such as in this example:

var Person = Class.create({
  initialize: function(name){
    this.name = name;
    var myArray = $('submitform').getInputs('text');
    myArray.each(function(textbox, i){
      /* this.processFormFields doesn't exist, because "this" refers to the
         anonymous function we've created as a closure */
      this.processFormFields(textbox);
    });
  },
  processFormFields: function(textbox){
    /* do some stuff here */
    /* This will not work, because in this case, this will refer
       to the closure, not the class object */
    textbox.value = this.name;
  }
});

In this case, we can’t access methods of our person-classed object from within the closure object. So how can we run the class method from within the closure? Method binding!

Prototype’s bind() method allows us to specify the object with which to associate the this keyword. Most often, you’ll want to bind the method to the classed object you’re working with as in this modified example:

var Person = Class.create({
  initialize: function(name){
    this.name = name;
    this.age = 0;
    var myArray = $('submitform').getInputs('text');

    var boundProcess = this.processFormFields.bind(this);
    myArray.each(function(textbox, i){
      /* boundProcess() is a copy of processFormFields that is
         specifies that THIS refers to the class object--not the closure */
      boundProcess(textbox);
    });
  },
  processFormFields: function(textbox){
    /* do some stuff here */
    textbox.value = this.name;
  }
}

Notice two important things here. First, we’ve used the bind() method. It has created a copy of the processFormFields method that will be able to be used from within the closure (since it does not rely on the this keyword to be called). Second, it has associated itself with the class object; which means that within the method, this.name will refer to this.name within the class object and not look for it in the closure (which would simply be undefined).

The general rule: any time you need to use one of your class methods within a closure and that class method will refer to any class properties using the this keyword, you need to bind that method to the class object.

One of the times that this becomes the most obvious is in event handling. Suppose we add this method to our class above:

eventHandler: function(e){
  this.age = e.element.identify();
}

And we observe it on a click of the “myButton” element:

$('myButton').observe("click", this.eventHandler);

In this case, the context of the event handler will be the element on which the event handler was called. Since myButton does not have a property called this.age, an error will be generated. We need to bind the method to the class object, not the myButton object. However, because this is an event handler, we will use prototypes bindAsEventListener() method which works exactly like bind–except the returned function automatically accepts the event object as its first parameter.

$('myButton').observe("click", this.eventHandler.bindAsEventListener(this));

Now, using this.age in the eventHandler() method will reference the class property age rather than a property of the element on which the eventHandler() was fired.

It’s all about context!

Remember, the purpose of method binding is to ensure that you can make use of the “this” keyword. Without access to this, you lose the ability to work effectively with your class objects.  

  • http://ooyes.net/blog seo

    Thanks for the great post ;D

  • http://javascriptmvc.com Justin Meyer

    Nice thoughtful post! Are you using prototype for AutoTrader?

  • http://ooyes.net website design

    Good explanation.

  • http://autotrader.com Dennis

    Finally, I got time to read this. Your site may well end up being my new FF3 home page…

  • http://ryanangilly.com Ryan

    This is a phenomenal write up. Thank you.

  • http://www.chaneloutletstores.com chanel

    Thanks a landlord it! I acquired yet some insight. Life is so colorful, we should be able to live in, such as Korea and honor the planet. Human life is like rivers, slowly flowing, flowing rivers, flowing through the snow, flows through the prairie and ultimately into the sea, return to the embrace of nature, start a new reincarnation. Allow us to feel the meaning of life will come only to those you have those memories http://www.cheap-nikeshox.com