The Long Awaited(?) JavaScript Essay, and the Disk Clock Bug

I don’t think I ever announced that I actually fixed the problem running Disk Clock with OS X 10.5. The fix involved explode, so I figured I should finally finish this essay, so people would know what I was talking about.

An essay on the properties of JavaScript, some most excellent but in the greater part those most annoying. Including a discussion on the limited affordances for the enumeration of objects, which satisfy neither for normal use nor for the creation of tools for runtime inspection. Including also a discussion of scopes – the limited ability to inspect scope objects and the trials of creating convenient namespaces. Containing a novel method of importing namespaces both awful and wonderful, and it’s employment in working around changes in the WebKit JavaScript engine.

Note: this started with some previous entries, which were possibly rewritten a bit and expanded upon. The writing of this essay began on 2008-02-10, and was expanded on 2008-03-11 and 2008-06-03

DontEnum

Javascript has the ability to enumerate the members of an arbitrary object, however some of the system methods don’t show up. This is useful so, i.e., the Array::splice method doesn’t show up when you iterate through an array. Of course this breaks rather badly when you try to add you own methods to arrays, only have them show up as items in every array.

The ivory tower people will tell you that for-in is inappropriate for arrays – use counting ‘for’ instead. I’ll agree with that in theory, but I beg to disagree in practice: if you add methods to array, other people’s code will break. You can say those people are misguided (among a lot of other things one might say), but it doesn’t change the fact anyone trying to use both both pieces of code is going to have to choose one.

In any case, there is no work around for the times when you actually do want to enumerate the hidden methods. In order to expose these operations on system objects in NakedJS, I’d have to hard-code a list of all the DontEnum properties. And come Javascript 2/ECMAScript 4, DontEnum becomes available to users, so complete browsing is totally hosed.

Is there a MOP in the house?

I finally finished The Art of the Metaobject Protocol. I think the main theme is this: by exposing key parts of the object system’s implementation, it becomes possible to implement features like Smalltalk’s browsing editor. Javascript falls down on at least two accounts here – DontEnum, above, and the fact that there is no standard way to discover the prototype object. Mozilla has a __proto__ extension, but it’s VM specific.

Dynamic Typing

NakedJS Property editing and specifying function parameters would be greatly enhanced by being able to use appropriate editors for the data required – but Javascript is a dynamically typed language; I might be able to look at the current type for limited use with properties, but function parameters are a lost cause without some kind of custom annotation.

Scope Objects

I don’t know if this is even an issue for naked javascript, but one of the little things that annoy me in general is the fact that the global object (usually window) is accessible (and therefore enumerable) while context objects of functions aren’t. You can get a little mileage out of eval(), but that doesn’t help with the enumeration problem.

One use of this is the ‘publish’ operation I use in a few places to take a locally defined symbol and add it to an object that will be returned as an interface object. You can write publish using eval(), but the publish operation has to be redefined in every context because the eval gets bound to it’s context of definition.

function publish(symbol) {
  app[symbol] = eval(symbol);
}
publish('publish'); // usage example, I don't usually do this.

All this to save:

app['symbol'] = symbol;

I guess I just hate repeating myself?

Javascript Goes Boom

The publish operation is used to construct a set of nested namespaces that contain all my library code. Now I’ve just got to access it somehow. By itself, this would require a typing out a lot of namespace prefixes CGD.JS.mix, CGD.ARRAY.forEach, etc. Remember how I don’t like repeating myself?

function explode(what) {
  return "" +
  "for (var i in " + what + ") {" +
  "  if (" + what + ".hasOwnProperty(i)) {" +
  "    eval('var ' + i + ' = " + what + "[i];');" +
  "  }" +
  "}";
}

Which is used like this:

eval(CGD.JS.explode(‘CGD’));
eval(CGD.JS.explode(‘CGD.JS’));
eval(CGD.JS.explode(‘CGD.ARRAY’));

Which could, if not for the sake of clarity, be written:

eval(CGD.JS.explode(‘CGD’));
eval(JS.explode(‘JS’));
eval(explode(‘ARRAY’));

Yes, this is an eval in an eval. It uses Javascript’s two types of quotes to avoid a whole bunch of escape characters. Essentially, this would have been a lot easier with LISP style macros, but you take what you’ve got. Here it is again for ease of reference:

function explode(what) {
  return "" +
  "for (var i in " + what + ") {" +
  "  if (" + what + ".hasOwnProperty(i)) {" +
  "    eval('var ' + i + ' = " + what + "[i];');" +
  "  }" +
  "}";
}

Essentially, it is a function which returns a big long string. That string has had a symbol baked into it, which is why it’s a function and not just a var. The string encodes a loop over an object, presumably one being used as a namespace. It takes each property of that object, and essentially inlines the earlier publish operation by building up a string incorporating the property name. Don’t forget that the necessary object name has already been baked into the the internal string by the explode function. The string is evaled.

The result of explode has to be evaled in the variable scope in which you want the things defined. It only works if the inner eval is in the target scope, and the only way for that to happen is for the outer eval to be in the target scope.

The end result is a hack of the best or worst kind, depending on you point of view. The use of eval utterly defies the Javascript prophets and probably about every book ever written on the subject. The end result is more beautiful code because I don’t have type CGD.ARRAY all over the place.

The Forgotten With?

With Naked Javascript

Javascript has a with operator – but everybody who claims to be anybody in the Javascript world says don’t use it. This comes tantalizingly close to solving the local scope object problem. It doesn’t give you access to the local scope object, but it effectively allows you to insert another object on top of the scope stack, so you can ignore the ‘real’ scope object and define vars (properties) in your space. I haven’t actually done this yet. (And now I can’t because it won’t work in WebKit…)

With Exploding Namespaces

In principal, the with operator could obviate the need for my explode hack. It doesn’t buy me much with the gurus, however, because the same people who say don’t use eval say don’t use with. While it doesn’t involve string metaprogramming, it would effectively involve nesting one file-sized block around the entire program. There are also some seemingly dire warnings about increasing the size of the scope chain and making it take longer to get to global methods.

Of course you shouldn’t be defining global objects. There are all those pesky system methods however. Often times I’ll be explicit with these anyway, and qualify it with window, the global object in browsers. Of course, I’ve aliased it to god (Global Object Definitions, duh ;^) ) just in case I ever find myself running on something other than a browser.

If you know a little Javascript, and don’t understand all this scoping nonsense, I can recommend this article on closures as good down and dirty explication of how Javascript really works.

About That Disk Clock ‘bug’

Somewhat late in Disk Clock’s evolution, after I did most of the writing about scopes here, I decided that with might actually run a bit more with the ‘JavaScript zen’ than exploding things. I had pulled a bunch of time conversion factors (MS_S = 1000, etc.) into an library object for later re-use, but now I had a naming problem (TIME.UNIT.MS_S?). I really only needed them in one scope, where they had formally been declared as vars. So I wrapped a with around the scope, and everything was fine.

Until I published it and the OS X 10.5 reports stared popping up. The time unit conversion got used in a closure exported from my with scope, which started the animation system running; the program crashed just when it was about to start doing something interesting. Fortunately, the symbol not defined message left a line number in the logs, and it didn’t take long to figure out that the symbol involved was in a somewhat unique state.

When Apple went from the WebKit in OS X 10.4/Safari 3.0, to the version in 10.5/3.1, they optimized the JavaScript Engine. One of those changes broke changed the interaction of with with closures. The closures document indicates the old way was right, but I haven’t had a chance to dive into the WebKit code yet and figure out what happened. Here’s a test case. I don’t have Safari 3.0 anymore, but Firefox reports ‘context’ while Safari 3.1 reports outer and inner as ‘global’.

function setup()
{
  exportedFunction('outer');
  return 0;
}

contextObject = {
  property: 'context'
};

var property = 'global';

var exportedFunction = function() {
  with (contextObject) {
    function inner(calledFrom) {
      alert(calledFrom + ": " + property);
    }
    inner('inner');
    alert('direct: ' + property);
    return inner;
  }
}();">
Posted Tuesday, June 3rd, 2008 under Essay.

2 comments