Efficient JavaScript or Inefficient Browser?

I caught this article on Reddit this weekend and had definite mixed feelings about it. Many of their suggestions were obviously good ideas—calling SetTimeout() with a string is obviously stupid, as are 90% of the possible uses of eval() and Function(). The advice on what causes the DOM to repaint and reflow was interesting and informative.

It would have been a really good article except that some of their advice seemed weak. Some of what was masquerading as optimization “advice” seemed more like work-arounds for a substandard javascript implementation.

Take this for example:

var min = Math.min(a,b);
A.push(v);

These alternatives provide the same functionality, while performing better:

var min = a < b ? a : b;
A[A.length] = v;

Yes, I can see in someone’s world that might perform better. But come on, the compiler/scanner should be able to produce the equivalent code. Anything less is a failing of your browser and not an “unoptimized” program. Do you really expect people to crappify their programs because your browser can’t properly optimize javascript for you?

“Well,” I hear you say, “it’s interpreted, not compiled.” To which I say, yeah, well there’s your first problem. Fix that before telling me that I need to use stupid constructs to make my program go fast.

Here’s another example:

This example requires the script engine to create 21 new string objects, once for each time the length property is accessed, and once each time the charAt method is called:

var s = '0123456789';
for( var i = 0; i < s.length; i++ ) {
  s.charAt(i);
}

This equivalent example creates just a single object, and will perform better as a result:

var s = new String('0123456789');
for( var i = 0; i < s.length; i++ ) {
  s.charAt(i);
}

Again, this strikes me as being a failing of the compiler. I see no reason why the browser should instantiate a string object just to call its length(). A little bit of hard coding in your implementation can optimize that to a strlen() call or better—No object instantiation at all. What if someone overrode String.prototype.length()? That’s one if statement in C and some fallback code. Again, I don’t think my program should suffer because your browser doesn’t properly optimize things.

But then again, maybe I’m just crazy.

3 big problems with Javascript

For years I had an irrational loathing of javascript. It was to the point where I’d always have it switched off in my browser. To me it was synonymous with bad web design and crappy menus that didn’t ever work in netscape. Then google maps came out and I was completely impressed. They did all that with javascript? I didn’t know javascript could *do* that. So about a year ago I decided to actually look at the language and see what you could do with it. I updated an old cgi script game of mine into a modern javascript game complete with XMLHttpRequest high score stuff. And I was generally very happy with everything. My friend Jim wrote a card game and we developed it into a very nice game site

I really love javascript now, but that doesn’t mean it doesn’t have its faults. I can make a number of little nitpicks but I want to focus on three major deficiencies of the language.

Problem 1: No include directive

This is particularly annoying when dealing with many libraries that also depend on other libraries. It forces you to know all the dependencies up front and hurts the modularity of your site.

I’ve read that an include directive was not ..er.. included in the language because they didn’t want to specify what it meant. The argument goes like this: Javascript is a nicely generic language and so if it was used as a scripting language of a video game (for instance) it would be annoying if the spec said “include” was a URL. I don’t see why that precludes adding the include directive to the language. I would argue that it’s better to have it in the spec with the argument being “implementation defined” and let the w3c guys say that it’s a URL and the game guys make it a virtual file inside some zip archive.

The other argument I’ve heard is that the language designer wanted something nicer than a simple “#include” style of include—something like the class importing of java or a “use” directive of perl. I can understand that. But I would argue that not having anything is much worse than having a simple file/URL based include system.

A third argument I’ve heard is that including files is gross since you have to protect against multiple inclusions with big ugly protection “if” statements like every C header file you’ve ever seen. I don’t buy this either—Objective C solved this 20 years ago. Objective C has “#include” and “#import”. “#include” includes the file verbatim and “#import” includes it only if has not been imported before. It’s very simple and it covers all the cases that might occur.

Problem 2: “this” is wrong in callbacks

Javascript programs, being functional and non-threaded, end up having a lot of callbacks. Especially if you use things like timers and XMLHttpRequest objects. If you pass a function to something and it runs it, your function gets run with all your local variables (since it’s a closure) but the “this” points to something stupid like the callee or the global context (I don’t remember but it’s not your local “this”).

I’m not sure why it does this but I imagine it makes some part of the language designer or language implementer’s life easier. The problem is that it makes every programmers life hell. Yes, it’s easy to get around: just make a local variable and set it to “this”. Look around greenfelt.net and you’ll see variable named “me” and “_this” littering the code. But I shouldn’t have to do that at all—the language should just do the right thing (maybe that’s the perl side of me talking).

My argument against this is that it is *never* what you want as a developer. *Never!* Why force every program to have stupid little work arounds just because it makes the spec of the language cleaner at some level? I think the spec writers and language implementors need to suck it up and take one for the team here.

How much time have I spent debugging a problem caused by the stupid “this” pointing to the wrong context? Days, I’m sure.

Problem 3: Inheritence sucks

The javascript object model is simple. I generally like it. But its simplicity causes problems when trying to do inheritence. I looked around for ways to do inheritence and everyone seems to have a different way of doing it. And all the methods seem to have some drawback. In the end I ended up settling on a method that seemed to minimize code, but it’s still ugly:

The base class:

function baseclass(a,b) {
    if (arguments.length) this.init(a,b);
}
baseclass.prototype.init = function(a,b) {
    ...
}

And the inheriting class:

function subclass(a,b) {
    baseclass.prototype.init.call(this,a,b);
    ...
}
subclass.prototype = new baseclass();

This allows for “new baseclass(a,b)” and “new subclass(a,b)”. But there’s a lot of scaffolding there. It really needs some good syntactic sugar.

From what I’ve read they’re trying to rectify this in the new version of javascript being developed.

I can only hope they also fix the other 2 problems.


Addendum

[Mon Mar 6 01:09:49 PST 2006]

Thanks to a comment from Pasha I’ve been experimenting with a different way of working around problem #2 using a the apply() function:

function wrap(context, f) {
    return function() {
        f.apply(context, arguments);
    };
}

Usage:

div.onclick = wrap(this, function() { ... });

I’m liking this better than my previous work around, the “var me=this” closure variable trick. It’s better because it actually makes “this” be correct and so the inner code doesn’t have to use a different variable name. The downside is you have to pass “this” into the wrap() function—it would be better if it could climb up the call chain and get “this” out of whatever context came before it.