Binding Javascript Method References To Their Parent Classes

Posted March 5, 2009 at 9:51 AM by Ben Nadel

Tags: Javascript / DHTML

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

Mar 5, 2009 at 11:45 AM // reply »
170 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);
}


Mar 5, 2009 at 11:45 AM // reply »
12 Comments

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>


Mar 5, 2009 at 11:51 AM // reply »
11,238 Comments

@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.


Mar 5, 2009 at 11:55 AM // reply »
11,238 Comments

@Dan,

Great catch - the post has been updated.


Mar 5, 2009 at 11:58 AM // reply »
11,238 Comments

@Dan,

Looks like I had it backwards on this post as well:

http://www.bennadel.com/index.cfm?dax=blog:1514.view

Dang, lots of saves today :)


Sep 29, 2009 at 7:31 PM // reply »
1 Comments

Very great post thanks for your insight!! :D


Aug 31, 2010 at 11:06 AM // reply »
11,238 Comments

jQuery now makes this super easy and powerful with their .proxy() method:

http://www.bennadel.com/blog/2001-Using-jQuery-s-Proxy-Method-In-Event-Binding-And-Unbinding.htm

It even allows you to unbind proxied event handlers using the original function references.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools