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 Streza June 15, 2008

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:

var functionWithScope = function(scope,handler){
  return function() {
    handler.call(scope);
  };
}

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.

var functionWithScope = function(scope,handler) {
  var args = Array.prototype.splice.call(arguments,2);
  return function() {
    handler.apply(scope,Array.prototype.concat.apply(args,arguments));
  }
}

Invoke it like so:

var foo = functionWithScope(console, console.log);
foo("Hello world!");

This will print "Hello world!" to the console.log in Safari or Firefox. However, you can also do this:

var foo = functionWithScope(console, console.log, "OMG: ");
foo("Hello world!");

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:

LIB.on = function(element, type, handler, thisObj) {
  element.addEventListener(type, functionWithScope(thisObj, handler), false);
};

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.

Peter Michaux June 15, 2008

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.

bob84123 June 15, 2008

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?

Peter Michaux June 15, 2008

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.

Matt Snider June 16, 2008

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.

Peter Michaux June 16, 2008

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?

Eric Larson June 16, 2008

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.

Brad Fults July 13, 2008

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").

Marco Demaio April 4, 2009

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.

anonymous August 17, 2009

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.

Peter Michaux August 18, 2009

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.