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.
Conversation
What about the 'self' variable?
function someClass()
{
someClass.self = this;
}
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.
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 itsaddListener
).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).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.
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.
A variation on avoiding the transient nature of
this
is to avail yourself on thearguments.callee
property.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.
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);
};
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.
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.
I've never really seen the need for the this keyword. After closures, object-based namespacing, and simulated private/protected variables -- who needs this?
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?
@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.