Is this overrated?

In many of the JavaScript tutorials, there's plenty of concern over this. The this keyword is used to reference the current function/object context. The problem is, a function can easily be 'detached' from the object it was originally defined as a method of. As a result, the this keyword no longer references what you thought it should and errors occur.

var obj = {
  myProperty: 5,
  myMethod:function(){ alert(this.myProperty) }
  }

obj.myMethod(); // alerts 5

var mytest = obj.myMethod;
mytest(); // undefined!

As this overly simplistic example demonstrates, the assigning of the method to the test variable means that the current context now belongs to the window object (since mytest is a property of the window object).

Call and Apply

To get around this, a function can be called in the context of another object using call or apply. For example. mytest.call(obj) would give us the desired result of 5. The bindAsEventListener and bind methods in Prototype use call and apply, respectively.

This is most commonly used to retain the proper context during event handling. Take a look through the Prototype source and you'll repeatedly find .bind(this) calls. To explain why, let's take a quick look at a common pattern:

declare object → initialize object → define object method to be run during an event ~//~ event fires → run function → access properties and methods of initial object.

Basically, the act of assigning the function as an event handler separates it from the object. The call/apply is designed to re-establish that link.

Relying on Closures

More often than not these days, though, I find myself relying on closures instead. Closures are extremely handy in that functions retain access to variables from its original scope chain, despite the separation.

function myFunc(){
   var toRemember = 5;
   var aMethod = function(){ 
     alert(toRemember);
   }

   Event.observe(element, 'click', aMethod);
}

This example uses Prototype for assigning the event handler but the key thing is that I haven't used bind or bindAsEventListener. I've just passed aMethod as-is. The closure still gives me access to my variable toRemember.

As a result, I find myself moving more to defining functions within functions to retain the closure information than worrying about using this and binding to get back to my original object.

Using a Single Object

One of the other things I find myself doing is taking advantage of a single object. For most tasks, the need to create multiple objects of a particular type is simply unnecessary. Instead, I'll build out a central management object that maintains the functionality I need.

var uniqueObj = {
   init:function()
   {
      Event.observe(element, 'click', uniqueObj.aMethod);
   },
   aMethod:function()
   {
      uniqueObj.anotherMethod();
   },
   anotherMethod:function()
   {
      alert(5);
   }
}

In this example, again, I've avoided having to worry about binding issues since my unique object will always be my unique object and I can refer to it directly.

Admittedly, this can by marginally cumbersome as one is constantly having to write out the unique object name (as a result, my object names tend to be short to avoid excessive typing).

No more this?

We all go with what works best and while the this keyword is handy, I just find myself using it less and less.

Published December 18, 2006
Categorized as JavaScript
Short URL: https://snook.ca/s/732

Conversation

12 Comments · RSS feed
Peder Rice said on December 18, 2006

What about the 'self' variable?

function someClass()
{

someClass.self = this;

}

Jonathan Snook said on December 18, 2006

All you've done is assigned the current context to a property of the object. However, from your example, it's hard to say how you intend to use your object. As a result, you may still be relying on closures to access the object.

James Bennett said on December 18, 2006

I waffle on this.

Several of the good toolkits go out of their way to ensure that this gets corrected to what it intuitively "should" be, and this is generally handy.

But.

Sometimes the workarounds to get fine-grained correction of this just get too cumbersome, and I either resort to closures or find some other way to get an object instance passed to event handlers (I know that YUI makes that fairly easy, with quite a few useful optional arguments to its addListener).

So for now I'm doing bits of both, but I feel myself migrating away from use of this and toward other tricks (with the result that my JS starts looking more like my Python, having instances passed explicitly to methods).

Randy said on December 18, 2006

Hmmm... I am still somewhat partial with this. Sometimes it just seems simpler. Good points though.

Oh, and where you talk about Call and Apply I think you have a typo:
"mytext.call(obj)" should be "mytest.call(obj)" to jive with your example.

TJ said on December 18, 2006

I typically go with the "single object" method. It resembles namespaces, and not only helps you keep your data together, it prevents function naming conflicts when using multiple scripts.

Cory Smith said on December 18, 2006

A variation on avoiding the transient nature of this is to avail yourself on the arguments.callee property.


  function aListener( e ){
     var self = arguments.callee;
     self.aMethod();
  }
  aListener.aMethod = function(){
     alert(5);
  }

It is more functional and compact, hence less readable, but it does have some unique uses that suggest it for specialized situations, like RegExp callbacks, and reusable listeners.

And, depending on your browser, it may provide smaller memory footprint, du to the fewer Function objects being created. This may or may not matter.

Andy Kant said on December 18, 2006

I tend to use a combination. The 'this' pointer is important because it is required to access instance variables from prototype methods. I use closures to reference 'this' for everything that might be called outside of the normal context (anything called by another member function). I could use the '.call' or '.apply' but I found that sometimes that breaks (probably my error) and it just makes my code uglier to look at and maintain.

On a sort of related note, while figuring out how I should handle 'this' pointers I created a method of having quasi-protected variables. It gives me a way to access private variables from other objects/classes while also hiding them from the public interface. The downside is that private variables are now potentially exposed for anyone to alter.

function Obj() {
var _self = this;
this.value = 99;
this.$expose = function(prop) {
return eval('(' + prop + ')');
};
var _helper = function(multiplier) {
_self.value *= multiplier;
};
};

Obj.prototype.square = function() {
this.$expose('_helper')(this.value);
};

Andrew Tetlaw said on December 18, 2006

I agree, in a lot of cases it's easier to use the 'single object' technique. I find it easier & safer to use and easier to read.

Jeremy Keith said on December 19, 2006

I absolutely agree about using closures instead of this. Nine times out of ten, it's easier to read and understand as well as being more portable.

Tim McCormack said on December 20, 2006

I've never really seen the need for the this keyword. After closures, object-based namespacing, and simulated private/protected variables -- who needs this?

Frank Manno said on February 17, 2007

Hey Jon,

Not sure if you're still checking comments from this post, but after having read your suggestion on using closures, the one thing that came to mind was memory leaks.

Would there be a need for memory concerns in this context?

Jonathan Snook said on February 17, 2007

@Frank: using closures doesn't automatically mean that there's a memory leak (as many seem to think). It does mean you have to be a little more aware of how circular references are created for IE4-6. Most of the addEvent scripts out there take memory leaks into account and will ensure that memory gets recaptured when the page unloads.

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.