More ES5 backwards-incompatible changes: the global properties undefined, NaN, and Infinity are now immutable

(preemptive clarification: coming in Firefox 3.7 and not Firefox 3.6, which is to say, a good half year away from now rather than Real Soon Now)

JavaScript and the undefined, Infinity, and NaN “keywords”

Consider the following JavaScript program: what do you think it does?

print("undefined before: " + undefined);
undefined = 17;
print("undefined after:  " + undefined);

The above program will print this output:

undefined before: undefined
undefined after:  17

Surely you can’t be serious!

A sane person might think that this program isn’t even a program. Doesn’t undefined always refer to the primitive value undefined? After all, this “program” isn’t one, nor would be the same one for true or false, mutatis mutandis:

print("null before: " + null);
null = 17; // !!! NullLiteral is not a LeftHandSideExpression
print("null after:  " + null);

I am serious…and don’t call me Shirley

Curiously, the program that assigns to undefined is a valid JavaScript program, but programs that assign to null, true, and false are not. Why not? The latter are all keywords with intrinsic meaning within the language; undefined, on the other hand, is just a normal property of the global object. According to ECMA-262 3rd edition, if you assign a different value to undefined, that different value becomes the new value of undefined.

This is a clear botch in ES3. undefined should have been a keyword in JavaScript from the beginning; similarly, the global properties Infinity and NaN probably should have been keywords as well (or perhaps the properties should not have existed, given that Math.Infinity and Math.NaN exist and are immutable). ECMA-262 5th edition doesn’t quite go so far as to change these three properties into keywords due to backwards compatibility concerns (making that change would be guaranteed to break any programs that even tried to assign to those names, regardless whether the program relied on that assignment for correctness). Instead, it changes these properties to be read-only, in the same way that the various numeric properties on the Math object are read-only. Assigning to these properties in ES5 won’t do anything (unless you opt into strict mode, in which case a TypeError exception will be thrown after we fix bug 537873), but at least it won’t definitely and completely break existing programs that relied on this.

We’ve made this change in SpiderMonkey, and it is now in trunk builds of Firefox, slated for the eventual Firefox 3.7 release. Download a nightly build from nightly.mozilla.org and test out the change for yourself (use the profile manager if you want to keep your current Firefox settings and install untouched). This change should have no effect on the vast, vast majority of web developers who don’t try to change the values of these properties; as for the [civility and my religion require I redact this description] developers who did change the value of the global undefined, NaN, or Infinity properties, well…you had it coming.

The bottom line

The global properties undefined, Infinity, and NaN will be read-only and immutable in Firefox 3.7. Assigning to these properties will do nothing (except in strict mode where a TypeError exception will be thrown once we fix a bug) rather than changing their values. This shouldn’t break the vast, vast, vast majority of scripts out there — but there’s no way to guarantee it will break no one, so we think it’s worth announcing this backwards-incompatible change as proactively as possible.


Haircuts for job hunters?

I can think of no stronger an argument to the average person for sweeping United States tax simplification and reform than this video (a variation of which I saw while watching the Sugar Bowl on New Year’s Day — a game which, to be honest, was almost sickeningly boring after the first half):

Is a haircut a job-hunting expense?

Do you know the answer to the question the video poses under the current tax system? Answering the question is left as an exercise for the reader; it’d take me more effort to find out the answer than I’m willing to make. Good luck reading IRS documentation to figure out the answer! (Food for thought: which segments of society are most likely to know or learn about, and take advantage of, this deduction, particularly given that it requires itemizing deductions, among other restrictions? What classes of society benefit most and least?)

It will be of little avail to the people, that the laws are made by men of their own choice, if the laws be so voluminous that they cannot be read, or so incoherent that they cannot be understood.

James Madison, The Federalist No. 62

Instead, consider the answer under the far simpler Hall-Rabushka flat tax, occupying a single postcard-sized form (plus one for your company if you’re self-employed): the question is meaningless. Whether you get the haircut for personal pleasure or for job-search reputability, it wouldn’t affect your income tax in the slightest. More generally, an individual’s income taxes under the flat tax don’t depend at all on how he spends his money. (See Section 202, referencing Section 201 and Section 101; the law’s text, also linked from the above page, fits in seven nearly pocket-sized pages, so it’s easy to navigate it and only slightly less easy to understand its requirements. Analysis and understanding of its rationales is more difficult but is well within the grasp of an intelligent taxpayer with a numerical bent.)

In the meantime, Congress, please keep preserving the existing tax system and even making it more convoluted, complex, and distortionary — taxpayers love you for it!


Review queue zero

Tags: , , , , — Jeff @ 16:00

I have zero outstanding review requests against me, placing me in a state in which I have not been since at least the first few months of the year. Yay! If you have review requests to make, and they could reasonably be addressed to me, feel free to do so. No one else is further ahead in that game than I am — for the moment. Carpe diem!


Merry Christmas!

As an expression of the festive holiday spirit, while being mindful of the current economy, I direct you to a timeless economics paper on The Deadweight Loss of Christmas by Joel Waldfogel.

Put simply, when it comes to gift-giving, the only person who knows precisely the value of a potential gift is its receiver. If the receiver values the gift at less than the value the giver expended to acquire it, we have deadweight loss: economic inefficiency present when allocation of goods is not Pareto optimal (that is, some other allocation would leave both parties better off). Had the giver instead given that same value in a more fungible form (the epitome of which is generally reached in cash), the receiver could have acquired the value present in the intended gift and the value present in the excess, thus maximizing his utility from the gift value. Where does that lost value in the excess go, if the inefficient gift is given? It is lost; neither giver nor receiver is fully satisfied. The giver overallocated his resources toward satisfying the receiver (or, if you prefer, allocated them in a way which did not maximize received value); the receiver’s utility was not maximized.

So, in the future (perhaps not this Christmas, but in future ones, or for birthdays, or for other times when you might ordinarily give presents), do your friends’ utility curves and your pocketbook a favor: give the gift of cash, the gift that will give them exactly what they want. (And, if you still think you know what your friends want better than they do, simply suggest the ways you think they would best maximize their utility while spending it. Another idea: give early to maximize net present value of the money, also allowing them to take advantage of fleeting sales that may no longer be available after Christmas or some other occasion.) You will increase the market efficiency of a baroque, inefficient ritual, and you will improve the economy in the most efficient manner while doing so.

Update: One comment directs me to today’s Dilbert strip, which expresses the above rather more pithily (particularly noting that gift-giving inefficiency is likely heightened for workplace gifts where deep knowledge of the other person’s desires is potentially less common). :-D


ECMA-262 ed. 5 backwards-incompatible change coming to SpiderMonkey and to Gecko-based browsers

(preemptive clarification: coming in Firefox 3.7 and not Firefox 3.6, which is to say, a good half year away from now rather than Real Soon Now)

ES5 and compatibility

The fifth edition of ECMA-262, the next iteration of the JavaScript language, is broadly backwards-compatible with existing JavaScript code. Generally, code that worked in past browsers that implemented the specification will continue to work in new browsers as they implement the new edition of the specification. However, there are a few exceptions. The most obvious one is ES5′s strict mode, where specially-tagged scripts and functions will cause parsing and execution of their contents to occur under stricter requirements than in the past. For example, this code would have executed “as intended” in ES3, but in ES5 the definition of a variable named “arguments” inside a function in strict mode is a syntax error (none of the code even executes):

function strictModeError()
  "use strict";
  var arguments = 17; // stupid, but permissible, in ES3
  return arguments;
if (strictModeError() !== 17)
  throw new Error("up is down");

The above isn’t more than a theoretical problem as it is expected old code wouldn’t have accidentally opted into strict mode. Not all of ES5′s incompatible changes, however, are so benign.

ES5 compatibility with ES3 extensions

One unusual area of compatibility concerns not ES3, but extensions to ES3. One of the more profound changes in ES5 is the introduction of getters and setters, in which what appears syntactically to be a property when used actually will invoke function calls “under the hood”. Most major JS engines support this extension to ES3:

var o =
    get field() { return this._field; },
    set field(f)
      if (typeof f != "number")
        throw new Error("not a number");
      this._field = f;
    _field: 0
print(o.field); // 0
o.field = 5;
print(o.field); // 5
try { o.field = "0"; } catch (e) { /* throws: not a number */ }
print(o.field); // 5
o.field = 17;
print(o.field); // 17

This syntax is in ES5, partly because it addresses a need in a reasonable way but mostly because many developers will already be familiar with it. (Getters and setters were also available programmatically; ES5′s solution is different but more flexible.) However, not all aspects of getters and setters are present in ES5 in the same way they were in extensions to ES3 engines.

Assigning to getter-only properties in ES5

Consider the previous example, slightly tweaked:

var o =
    get field() { return this._field; },
    _field: 17
print(o.field); // 17
o.field = 5;  // ???
print(o.field); // ???

In this case the field property is read-only: you could analogize it to element.childNodes.length, which has a value which it makes no sense to attempt to change. What should happen, then, if you attempt to change it? Current browsers throw a TypeError when you try this. ES5, however, chooses to remain (arguably) more faithful to ES3 and instead makes setting a property that only has a getter do nothing — except in strict mode, where a TypeError exception will be thrown.

SpiderMonkey, the JavaScript engine embedded in Gecko browsers, has just made the switch from its previous always-throw behavior to ES5′s only-throw-if-in-strict-mode behavior when an attempt is made to set a property that only has a getter. (This will also apply to DOM properties like the aforementioned element.childNodes.length.) In the future, if you try to set a property that only has a getter, no exception will be thrown unless you’ve opted into strict mode. This change is now in trunk builds of Firefox; download a nightly build from nightly.mozilla.org and test out the change for yourself (use the profile manager if you want to keep your current Firefox settings and install untouched). If your site relies on an exception being thrown, this change could break it, and we’re hoping that an extended period of time to test the change will help developers iron out any reliance on this non-standard behavior. This change will first appear in Firefox 3.7, which probably won’t be released until the second half of 2010 or so. Firefox 3.6 preserves current behavior where an exception is always thrown, so you should have plenty of time to update your site in response to this change.

The bottom line

Firefox 3.6 and earlier throw an exception whenever you attempt to set a property represented only by a getter (this includes DOM properties defined as readonly). Firefox 3.7 will only throw a TypeError when assigning to a property represented by only a getter if the assignment occurs in ES5 strict mode code. This change will also apply to attempts to set readonly DOM properties like element.childNodes.length. If you’re relying on an exception being thrown in either case, change the assignment location code so that it works when no TypeError exception is thrown.

« NewerOlder »