The Weirdest JavaScript syntax

I have bad news. The following is valid, spec compliant JS syntax:

foo("bar") = 42;

What in the Crockford is going on?

Working on my JS to C compiler I've spent a fair bit of time reading and implementing the ECMAScript spec, specifically ES5 strict mode as it's small(er). It's in ES5 and earlier that this weird syntax exists - it was removed in ES2015. If you read the ES1-5 specs on function calls, you'll find functions can return an internal spec type called a Reference

one difficulty: function calls are permitted to return references. This possibility is admitted purely for the sake of host objects. No built-in ECMAScript function defined by this specification returns a reference and there is no provision for a user-defined function to return a reference.

The Reference spec type is effectively a base (object or environment) and a name, e.g the window object as a base and "foo" as a name. 'Host objects' are APIs not defined by the language spec, but exposed to user programs by a specific implementation, e.g a specific browser JS DOM, or Node's APIs.

Reference is a spec type rather than a JS type: it's merely a description of behaviour in various spec algorithms, so it'd never be visible in JS programs nor assignable where normal JS values are. For instance, the spec wouldn't have allowed for behaviour like this:

var someRef = windowTitle() 
someRef = "pear"
console.log(window.title) // pear

The upshot is the spec allows functions to evaluated an assignable value in the left hand of an assignment. A imaginary spec-compliant DOM API could therefore expose a windowTitle() setter, that'd work like:

windowTitle() = "pear"
console.log(window.title) // pear

 History

I had to find out more - when and why did this end up in the spec? Digging through the archives, this feature was part of the very first ES1 spec from 1997, which was capturing all the craziness that emerged from the early days of JS. The wording stays unchanged in the ES5 spec.

I had to track down some examples of this feature being used. I found lots of references to people saying it was required by "old DOM APIs", in IE specifically. Then, in the ESDiscuss archive, Brendan Eich gave an example in a discussion about cleaning up the spec for the never-to-be-released ES4:

Indeed, IE JScript (for VBScript compatibility) long ago added support for "Reference" type return values from certain native ("host object") methods. This enabled domNode.item(i) = j, e.g. SpiderMonkey added support because someone embedding its open source wanted to support code written for JScript.

So the complete history of this feature goes: VBScript to IE JScript to Mozilla JavaScript to the spec.

I also found a complete code example of the kind of, I assume, VBScript inspired APIs this'd support (can't find an official API guide for this though, frustratingly):

var sh = WScript.CreateObject("Wscript.Shell");
var env = sh.Environment("PROCESS");
env("TEST") = "testvalue";

Absolutely no reason this couldn't have been implemented via sh.Environment("Process").Set("TEST","testvalue"), but one fateful day in the 90s someone made a different call. That it affected five generations of the language spec teaches us syntax is the slowest thing to change out there: language designers beware!

Enjoy this? Subscribe to my RSS feed or follow me on Twitter.