JavaScript Widgets Without "this"
I put this
on my list of JavaScript Warning Words and raised some eyebrows. I have been working to eliminate this
from my code both as an exercise to understand closures better and also because this
does causes problems. This article shows programming a toggle widget both with and without this
With this
This version could be considered a "standard" approach because the JavaScript community has been lead down this road mostly due to marking. Sun pushed Java with millions of dollars of marketing. Seeing this
or self
in programming books is the norm. We are just accustomed to this style of programming and it is no wonder JavaScript programmers have pursued this style.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>A Toggle Widget Demo</title>
<script type="text/javascript">
// A stub library that could be written cross-browser
var LIB = {};
LIB.on = function(element, type, handler, thisObj) {
element.addEventListener(type, function(event) {
handler.call(thisObj, event);
}, false);
};
// the widget
var ToggleWidget = function(toggler, togglee) {
this.toggler = toggler;
this.togglee = togglee;
LIB.on(toggler, 'click', this.handleClick, this);
};
ToggleWidget.prototype.handleClick = function() {
var s = this.togglee.style;
if (s.display == 'none') {
s.display = '';
this.toggler.innerHTML = 'hide';
}
else {
s.display = 'none';
this.toggler.innerHTML = 'show';
}
};
// bootstrap code
LIB.on(window, 'load', function() {
new ToggleWidget(
document.getElementById('toggler'),
document.getElementById('togglee'));
});
</script>
</head>
<body>
<p>
<span id="toggler" style="text-decoration:underline;">show</span>
<span id="togglee" style="display:none;">SURPRISE!</span>
</p>
</body>
</html>
Without this
This second version doesn't use this
. Instead closures are used more directly.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>A Toggle Widget Demo</title>
<script type="text/javascript">
// A stub library that could be written cross-browser
var LIB = {};
LIB.on = function(element, type, handler) {
element.addEventListener(type, handler, false);
};
// the widget
var makeToggleWidget = function(toggler, togglee) {
LIB.on(toggler, 'click', function() {
doToggle(toggler, togglee);
});
};
var doToggle = function(toggler, togglee) {
var s = togglee.style;
if (s.display == 'none') {
s.display = '';
toggler.innerHTML = 'hide';
}
else {
s.display = 'none';
toggler.innerHTML = 'show';
}
};
// bootstrap code
LIB.on(window, 'load', function() {
makeToggleWidget(
document.getElementById('toggler'),
document.getElementById('togglee'));
});
</script>
</head>
<body>
<p>
<span id="toggler" style="text-decoration:underline;">show</span>
<span id="togglee" style="display:none;">SURPRISE!</span>
</p>
</body>
</html>
Comparison
Programmers often site that using this
and prototype
are more efficient in memory space. Compare the two examples above. The second version's code is a marginally shorter but in longer programs the second style seems to be noticeably shorter. The difference is where the extra closures are made. In the first example there is an extra closure created in every call to LIB.on
to set the this
object when handleClick
is called. In the second example, the extra closure is created in the makeToggleWidget
function. At runtime the first example has more work to do as it uses Function.prototype.call
to handle the user's click.
In the first version the application programmer concentrates on object-oriented programming and leaves the necessary closures to the library. In the second example the application programmer concentrates on the closures directly. As JavaScript programming has evolved, the direct use of closures has become more common practice. Programming with this
often involves thinking both in the object-oriented style and closure style. Programming like the second version means just thinking in the closure style.
The second example has some more advantages...
The doToggle
is available for use at any time. Just send it two DOM elements and it will work. If you didn't want the doToggle
function out in the open you could hide it in a module pattern or namespace it somehow. Calling the handleClick
generically with two DOM elements would be quite ugly.
There is stronger encapsulation in the second example. When the doToggle
function runs it only has the two DOM elements on which it should be working as its parameters. In the first example, the DOM elements are passed in implicitly as properties of the this
object to the handleClick
function. The handleClick
function easily has access to any other properties of the widget even though it should not be touching them.
Comments
Have something to write? Comment on this article.
Steve,
I didn't particularly bring up the performance issue to argue in favor of the closure system. I brought it up to show it isn't a valid argument in favor of the OO solution. The performance issue is commonly given as an argument for the OO solution over the closure solution.
The closure version has reusable code. doToggle
is very reusable. It is just differently reusable than OO code is.
I was not focusing on namespacing in these examples as I wanted to focus on the OO vs. closure issue. As I wrote in the article, there are many ways to "clean up" the closure version. Equally I didn't focus on feature testing here because that would cloud the issues also.
I have used your functionWithScope
before under the more common name bind
.
Using the Array.prototype.splice.call(arguments, 2);
trick does not work in all browsers. Some will error. A manual loop to copy the arguments to a real Array
instance is safe.
Your last example with functionWithScope
still does create the extra closure my first example has. But rather than the extra closure being in Lib.on
, you've moved the creation of the extra closure into functionWithScope
.
I found your second code example interesting but I don't understand why it's better than the first. You say that 'this' "causes problems" but I can't see what they are.. could you elaborate on that?
bob84123,
A main problem which people encounter with this
is that it needs to be set properly any time a function is called that uses it. It isn't possible to just pass a function-valued property of an object which uses this
to another function because this
will not be correct when called.
Another problem with this
is it breaks encapsulation because JavaScript doesn't have private object properties.
Testing code that uses this
is more difficult because and entire environment needs to be mocked for that object to be constructed and work. Testing the doToggle
function in the second example requires much less mocking.
Reading code that depends on this
is more difficult because finding where properties are set on this
can be a convoluted search. this
can be used as a big grab bag of properties that starts to have some of the problems of global variables. As a contrast, reading the doToggle
function above is very easy because it only works with its formal parameters.
I agree with you that, in this case, not using 'this' makes more sense. In fact, I can't think of an example where I would use 'this' in a callback function, because IMO closures are easier to read and debug.
The only time I would consider using 'this' is in conjunction with 'prototype' and 'new'. And there is very little need for that, because for 95% of the time using the module pattern (or another functional pattern) and closures will work just fine.
However, what I was arguing on your last post is that sometimes, for performance reasons, it does make more sense to not use closures.
For example, I have been a fan of the Module pattern for a long time and use it extensively on Mint.com. Recently, I noticed that the transaction page was becoming quite slow. We transfer lots of text through an Ajax call to populate the table when a user searches their transactions. This text then is converted to an object, which is used in the business logic on the page. Iterating through and creating 50 such objects, ended up being noticeably slower using closures, than it was when I extended the prototype of an object and created 'new' instances.
So, the point I was trying to make is that it only makes sense not to use closures, when you have to worry about performance, and then only if you have to use many instances of an object. Otherwise, I agree that they should be avoided.
Matt,
I agree that when problems become an issue with any style of programming then another style will need to be used. I think that any one style will likely have problems somewhere. this
can lead to development difficulties. Closures may lead to performance difficulties under certain circumstances.
I would be very interested to see the code which you are describing where closures are so slow for only 50 objects. How many closures per object? Were the closures only one line each with the main logic factored out to functions outside the closure?
I think one aspect this example reveals is the functional nature of Javascript. Most of the time when I make an effort to think in terms of OO with Javascript, it only ends up in a confusing mess with 'this' being a pain. It is very similar to XSLT in that the context always provides the meaning to the expression, with the major difference being Javascript's context is not as consistent (events). jQuery is a great example functional programming with Javascript that I think makes for a good model.
I appreciate the "OOP is not always the best method" message, but, as with most things, the answer is not the other extreme either. There is something to be said for using JS prototype-based OOP (as it was intended) to accomplish tasks in a quick and coherent manner.
Your critique of the testing complexity of JS OOP seems to be a red herring. From what I can tell, your "much less mocking" statement about the closure style is referring to a single invocation of .call()
. That doesn't seem as dramatic as you make it out to be.
It's entirely possible to write JS OOP in the prototype style and be successful with not only the central function and readability of the code, but with testing as well. There are certainly times when it makes more sense to take a functional closure-based approach and jQuery illustrates the power of this method quite well. Your critiques apply very narrowly and only manage to conclude that the closure-based style is better in some cases. There will always be some tasks that are better handled with a prototype-based style (for some value of "better").
I have got a question regarding the insane IE6 and IE7 (I bet u also IE8) closure memory leak bug http://support.microsoft.com/kb/830555/
According to Microsfot article, are both of your samples creating memory leaks in IE, aren't they? I'm asking because I sill quite ignrant about coding in Javascript and you webiste to me is so helpful in understanding how things work and also how unreadble code libraryies like jQuery might work behind the scenes.
Moreover I did not understand properly, are u saying is slightly more preformant in showing up the 1st example in or the 2nd one with no objects?
Thanks.
the current version of jslint - with the browser option on - does not like "aliases of the global object, window and self". i bring this up since if things like window.addEventListener() are to be avoided, i'm not sure how to do so except with the "this" keyword as this.addEventListener().
so although i agree with you peter, i'm not sure how to *not* use "this" and still reference the global object.
JSLint is a valuable tool but it boils down to Douglas Crockford's opinion. Everyone has different opinions about what makes good JavaScript. If I think something is good JavaScript and JSLint disagrees then I would keep my code as is.
Have something to write? Comment on this article.
Your example is too small to expose the root of the problem you're creating. You're trading a negligible performance impact for a lot of downsides. First, you're throwing away the benefits of using (and reusing) object-oriented code. Second, you're polluting the global namespace with functions that don't belong there. And third, there are more elegant solutions to this problem.
One very important thing to remember is that JavaScript is a massively different beast. As you probably know, functions in JavaScript are first-class objects. However, one massively powerful aspect of this that few people take advantage of is that you can have a function which returns function objects. Here is an example:
Calling this function will return a function object which will always execute with a certain scope as "this". This is creating a closure, but it's really not bringing any other variables into the closure as there aren't any in scope. However, it doesn't let you pass arguments into the function. So let's extend this further.
Invoke it like so:
This will print "Hello world!" to the console.log in Safari or Firefox. However, you can also do this:
Which will print "OMG: Hello world!" to the console. You can pass in as many arguments as you want to either the functionWithScope call or the foo call. Then, your LIB.on function looks like this:
The event object will get passed in automatically, and no unnecessary closures get created.
Contact me at the email address I attached if you'd like more info.