As the the ECMAScript 4 (JavaScript 2) specification comes closer to its final form, a number of aspects have, already, congealed and able to be disseminated and learned. Specifically, the bug fixes to ECMAScript 3 that are being included in ECMAScript 4 are quite refined at this point.
The bug fixes to JavaScript came from a number of places. Generally, however, they have come about due to aspects being under-specified, causing confusion to occur amongst language implementors.
Additionally, bug fixes have come about from universal conventions that have been adopted by implementors causing them to be ported back into the specification.
A full list of the bug fixes can be found on the ECMAScript 4 wiki.
You’ll, generally, find that a number of these fixes are already included in some browser implementations (generally more-so in Spidermonkey-based ones, than others). However, a couple of these are implemented in no browser, at this point (like this propagation) and still others are implemented in all browsers (like single character substrings).
More than anything else, however, these changes represent an attempt to bring greater levels of common-use compatibility to the language (allowing more web pages to work) and bringing backwards compatibility inline with ECMAScript 3 implementations, rather than with the specification.
Generally, you should use this as a guide of what’s to come, not as of what you can universally use, right now.
arguments
is now a ‘real array’
arguments
is now a ‘real array’, as opposed to an array-like object, with the following distinctions:
arguments.toString()
behaves like an object’s.toString()
, rather than an array’s.- If you manipulate the
.length
property its contents aren’t deleted (if the length is reduced), nor are new items added (if the length is increased).
This means that you can now do simple things like:
arguments.slice(1);
Whereas, previously, you would have to do:
Array.prototype.slice.call( arguments, 1 );
typeof /regexp/ == 'object'
This change makes it such that callable objects return a typeof of ‘object’ rather than of ‘function’.
The result of this fix is best exhibited by an uncommon feature in Spidermonkey that’s caused a lot of confusion. Many don’t know this, but you can do this with a regular expression (in Firefox, et. al.):
/test/('test') // is equivalent to: /test/.exec('test')
In this case, a regular expression is an object that is also callable (thus the typeof was actually ‘function’ rather than ‘object’). So while fixing typeof of a RegExp, in Spidermonkey, isn’t a true ECMScript 4 bug fix, it’s indicative of the larger problem being solved. In reality, most users expect the typeof value to be ‘object’ in this case. The adjustment is that callable non-functions now have a typeof of ‘object’, allowing this to behave as one would expect:
typeof /regexp/ == 'object'
this
propagation
If you now define a function inside another, its this
is bound to the this
of the outer function, as opposed to the global object (e.g. window
).
This makes the following code possible:
function User(name){ this.name = name; function test(){ alert(this.name); } test(); }
Whereas, previously, you would’ve had to have used a closure to store a reference to this
and retrieve it within test()
. The resulting code would have looked something like this:
function User(name){ var self = this; this.name = name; function test(){ alert(self.name); } test(); }
undefined
, NaN
and Infinity
undefined
, NaN
and Infinity
are now immutable global properties. Some implementations already took some of these into account, but now it’s being standardized.
Thus you can do:
1/0 == Infinity 1/'foo' == NaN
RegExp Reconstruction
/regexp/
creates a new, unique, object every time it’s evaluated. For example, the following evaluated to true, previously, even though it shouldn’t have:
var test = []; for ( var i = 0; i < 2; i++ ) test.push( /test/ ); test[0] == test[1] >> true
This now brings RegExp literals in line with all other literals (as to how they are evaluated). This bug led a number of people astray – and in the end it was the third most duplicated JavaScript bugs.
Trailing Commas
When initialising an object, trailing commas are ignored (some implementations already support this).
var obj = { a: 1, b: 2, };
It’s now being made expressly clear that a trailing comma in an array initialisation does not create an extra undefined entry at the end of the array. JScript currently gets this implementation detail wrong, even though it was specified in ECMAScript 3. The correct result is as follows:
[a, b,].length >> 2 (not 3)
Single-Character Substrings
You can now get single characters out of a string by using [index]
, instead of the .charAt(index)
method. This way was already widely implemented.
'string'[0] >> 's'
Undefined Looping
You can now do a for .. in
loop against an undefined object (or a null
) without an exception being thrown. For example:
for ( var i in null ) {} for ( var i in undefined ) {}
This was, originally, intended to throw an error in ECMAScript 3 but this has been reverted due to web pages coming to expect this to work.
Comparison Order
When performing a comparison of two items the order in which they were compared was not guaranteed. It is now specified that the values be retrieved in left-to-right order.
For example, this is what could have happened previously:
var a = { valueOf: function(){ alert('a'); return 1; } }; var b = { valueOf: function(){ alert('b'); return 1; } }; a<b >> alert('a') >> alert('b') >> false a>b >> alert('b') >> alert('a') >> false
for .. in
order
When looping through the properties of an object the order in which they were returned was left up to the implementation – now it’s required that the properties be returned in the order in which they were created. The final result would allow for the following, intuitive, result:
var obj = {1: 2, p: 'hi', '_':'under'}; for ( var i in obj ) alert(i); >> alert('1') >> alert('p') >> alert('_')
Implementations since JavaScript 1 have generally done it this way (and implementations that haven’t have found that web pages generally require this convention). Thus, it’s being made expressly clear now that this is the case.
Blake Kaplan (October 31, 2007 at 11:06 am)
Hey John,
It’s worth noting that the regexp call syntax is a SpiderMonkey extension. Neither Safari nor Opera supports it and I don’t think that IE does either, though I can’t check at the moment.
Andrea Giammarchi (October 31, 2007 at 11:59 am)
You’re right Blake. I saw /this/(syntax) widely used inside Narcissus project from many months.
However I hope in JS 2 its behaviour will be like /test/.test() and not like /test/.exec() just because exec saves matches informations, not so useful for each quick regexp test.
Finally, it’s a really interesting post. JS2 is on the air and I really appreciate developers who spoke about that (instead of despize them as “someone” is still doing). So, well done John!
Sebastian Redl (October 31, 2007 at 12:49 pm)
>> undefined, NaN, and Infinity in the global object should be DontDelete and ReadOnly.
Sounds a bit dangerous if the ReadOnly is enforced. In particular, many libraries simulate an actual undefined by doing
window.undefined = window.undefined;
If an implementation throws an error on this line before all others have caught up to the point where the faulty versions are effectively extinct, this would break a lot of code.
So for compatibility, implementations would have to allow at least assigning undefined to itself.
Don Albrecht (October 31, 2007 at 2:18 pm)
I’m glad to see the trailing comma situation being resolved. While it can happen from sloppy coding a lot of the time, its currently slightly difficult to debug in certain instances. It might just be me, but it’s pretty easy to overlook them when skimming for other errors.
Dean Edwards (October 31, 2007 at 2:38 pm)
It would be nice if we could apply these changes to ECMA-262. The fact that Microsoft are not subscribing to ES4 means that we still have no bug-free specification that everyone can agree on.
http://blogs.msdn.com/ie/archive/2007/10/30/ecmascript-3-and-beyond.aspx
Mike Shaver (October 31, 2007 at 3:55 pm)
Assigning to a ReadOnly property is silently ignored, per ECMA, and doesn’t generate an error. So assigning undefined to itself will continue to work just fine — if it were to break such libraries, we wouldn’t see the change being made! :)
Mike Shaver (October 31, 2007 at 4:09 pm)
Dean: any application of them would require an ECMAScript 4, as that’s what the next revision of ECMAScript (aka ECMA-262) will be called. But having it in the specification is unfortunately not a guarantee that that it will be consistently implemented in IE or elsewhere; the number of 262-3 bugs remaining in such implementations is sad testament to that.
It’s also not clear what Microsoft *would* subscribe to, as there hasn’t been any concrete counter-proposal from them that addresses even their own stated concerns, so there isn’t a reliable vector into IE’s support. That’s why we have ScreamingMonkey underway, of course…
Mark Holton (October 31, 2007 at 4:27 pm)
Great info, thanks for the information and discussion as always. This helps put the new updates into context.. I esp like the ‘this’ propogation update in ES4. Very cool.
(Pretty good discussion on the msdn blog as well. MSFT is so brutal [shaking head in continued amazement])
Laurens Holst (October 31, 2007 at 7:24 pm)
Correct me if I’m wrong, but iirc you still won’t have much use for “1/’foo’ == NaN” because comparing with NaN always yields false :). But I get what you mean.
And talking about commas in arrays, as for {a:1, b:1,}, that leads to a parsing error in IE while it is accepted by Firefox. Something I’ve run into quite a lot. Intuitively, IE’s behaviour sounds correct, but if {} is consistent with [] it should probably also be made ‘expressly clear’. :)
~Grauw
Garrett (November 1, 2007 at 12:00 am)
@lauren, yeah, you’re right about NaN.
NaN === NaN; // false
isNaN, I think, should go on Number.
Number.isNaN( NaN ); // Just my opinion.
Good info overall. I can’t wait ’till I can stop feeling dumb for having a trailing comma. I actually often put the comma first to try to avoid the problem.
props = {
bork : 1, //
}
@john
“JScript currently gets this implementation detail wrong, even though it was specified in ECMAScript 3.”
It’s not specified, unfortunately.
Here’s what is specified:
ArrayLiteral :
[ Elisionopt ]
[ ElementList ]
[ ElementList , Elisionopt ]
Elision is: ,
So you can have:
[,]; // Elision, length = 1
[ 1, 2 ]; // ElementList, length = 2
[ 1, 2, ,]; // ElementList followed by Elision, length = 3
That’s the ones that are defined in 262r3
There’s really no provision for an extra comma.
Great info, nonetheless.
Giuseppe Raso (November 1, 2007 at 4:56 am)
I have a question ( a bit stupid ) about the this propagation. If we use the function test as a class, what happens? “this” will refer to the instance of test or to the instance of User?
Douglas Crockford (November 1, 2007 at 11:50 am)
I like these fixes. I even proposed some of them. They should definitely be included in whatever is ultimately called ECMAScript Fourth Edition.
Garrett (November 1, 2007 at 2:45 pm)
Why should RegExp be callable?
1) It does not fulfill any use case; it just provides a shortcut to .exec().
2) It does so at the expense of complicating the object’s interface.
ECMA-262r3 requires typeof to return “function” for objects that implement [[Call]]. Making RegExp implement [[Call]] puts it in that category.
The problem can be avoided by not defining objects that take on unnecessary and inappropriate responsibilities. SRP.
The alternative to callalbe RegExp is the existing RegExp.prototype.exec() method.
I could go on, but my point is that fulfilling use-cases is important.
Dustin Diaz (November 1, 2007 at 6:48 pm)
Regardless of whether or not other browsers are going to start adding these fixes, they’re still cool to see they’re in the works for one of our favorite browsers. My favorite is indeed ‘this’ propagation. It should have worked like that all along. Furthermore, these are all things I’m in favor of and it doesn’t drastically change the language (like some other features that have been proposed).
Daniel James (November 3, 2007 at 8:49 am)
According to the wiki page this propagation only happens for functions that are called by name from the outer function, which I think is a bit less than what you said. If my reading is correct, people might be disappointed since it doesn’t seem to apply for anonymous functions called by forEach loops.
I realise that this propagation can’t be used in all cases as it would ‘break the web’, as they like to say.
ilahi (March 7, 2008 at 12:49 pm)
Regardless of whether or not other browsers are going to start adding these fixes, they’re still cool to see they’re in the works for one of our favorite browsers. My favorite is indeed ‘this’ propagation. It should have worked like that all along. Furthermore, these are all things I’m in favor of and it doesn’t drastically change the language (like some other features that have been proposed)..