When we first introduced our plans for Internet Explorer Platform Previews we said that “developers and people interested in standards and web development can try out new platform functionality and provide early feedback.” We are now getting such feedback on a daily basis and are using it to improve IE9. However, sometimes the impact of the feedback extends beyond just IE9. Here is the story of how some recent feedback regarding the third IE9 Platform Preview resulted in a correction to the new ECMAScript 5th Edition (ES5) standard for JavaScript.
The ES5 standard became official in December 2009 and the third IE9 Platform Preview is the first widely distributed implementation of some subtle details of the ES5 specification. ES5 was designed to be highly compatible with existing websites and the Ecma International TC39 technical committee worked to avoid any non-security related changes that might break existing JavaScript code. However, perfection generally does not exist in the world of software so with the third IE9 Platform Preview we were very interested to see if any ES5-related compatibility problems with existing sites would show up.
Soon after releasing this platform preview, we received reports that some web-apps that use the jQuery framework did not work correctly in the preview. We tracked the problem to a specific jQuery API method that in some cases passed a caller provided value to Object.prototype.toString
without first checking if the value was null or undefined. Specifically, some calls to this jQuery method:
isFunction: function( obj ) { return toString.call(obj) === "[object Function]"; },
failed with an exception: “TypeError: Object expected”. Further analysis showed that toString
in the above code was the built-in method Object.prototpe.toString
and that the failures occurred when isFunction
was being called with undefined
as its argument. Why does an exception occur in IE9 and not in previous versions of IE or other browsers? It is because the third IE9 Platform Preview in standards mode actually conforms to the ES5 specification for Object.prototype.toString
.
According to the prior editions of the ECMAScript specification, calling any built-in method using null
or undefined
as the this
value passes the “global object” (in browsers this is the DOM window
object) to the method as its this
value. This opens a number of potential security holes for frameworks that aim to support mash-ups in a secure way.
The ES5 specification changed this so that null
or undefined
is not replaced with the window
object and the definition for each built-in method was updated specifically to deal with receiving these values as their this
value. The ECMAScript technical committee tried to do this in a way that preserves backwards compatibility for normal usage and throws an exception in cases where that is not possible. Object.prototype.toString
was specified in ES5 to throw such an exception. This created the compatibility problem described above.
This problem can be easily corrected by modifying the jQuery code with the additions shown in red:
isFunction: function( obj ) { return obj && toString.call(obj) === "[object Function]"; },
The jQuery team actually intends to make this change. However, such a change will not correct the thousands of locally hosted copies of jQuery that already exist on the Web. With the broad use of jQuery, it is clear that the ES5 specification contains a significant compatibility problem. It is fairly obvious how we can modify the IE9 ES5 implementation to eliminate the problem. We can just return the same string value ("[object Object]"
) that IE8 returns in this situation. Such a fix does not reintroduce any of the security problems that ES5 strives to eliminate. However, we do not want to unilaterally introduce such a variation into our implementation of a new standard. It does not help either compatibility or interoperability if IE fixes this problem one way, and other browsers either don’t fix it or fixed it a different way.
As soon as we understood the problem and the possible solution, I raised this on the TC39 es5-discuss mailing list as a backwards compatibility issue. My first post on the issue went out at 5:51 PM on June 25, a Friday. By 10 PM there were responses from TC39 members representing Apple, Mozilla, and Google. We all agreed that this was a compatibility issue that needed to be fixed, and that in this case throwing the exception was unnecessary and undesirable. We also initially agreed that the idea of returning the same string value as ES3 for these cases sounded like a good idea. However, in further messages over the weekend we realized that browsers currently don’t all return "[object Object]"
in this situation, some other values that were observed include "[object Window]"
and "[object Global]"
. A proposal was made to return "[object null]"
and "[object undefined]"
. This seemed to be a better solution as it not only fixes use cases such as jQuery’s but it also explicitly distinguishes null
and undefined
from actual objects. It is also better for browser interoperability because it requires that all browsers produce identical results rather than the ES3 situation that allowed different browsers to produce different results.
By Tuesday, the consensus on the mailing list was to follow this final proposal. As soon as that agreement was reached, I passed the revised Object.prototype.toString
specification on to our IE9 JavaScript development team so they could make the fix in time to widely test it with the next IE9 platform preview build. Mozilla has also indicated that they will be incorporating this fix into a future Firefox beta. I also updated the official TC39 Erratum for ES5 so this specification change is recorded there (section 15.2.4.2).
Web standards are complex software artifacts and like all software, they contain bugs. Sometimes the best way to find and fix compatibility bugs is to implement and deploy the standard on widely used browsers. This generally takes place in the context of early releases such as the IE9 platform preview builds. So, when you as a web developer are providing feedback on such releases you aren’t just providing feedback on a specific browser you are also providing feedback on the new and emerging standards that it implements. Of course, for this feedback to be worthwhile, browser implementers and standards authors need to be able and willing to quickly respond to feedback that identifies significant problems. The rapid response to the ES5 jQuery toString
problem and other issues on es5-discuss show how browser implementers and other TC39 members can and do work closely together to create a more compatible and interoperable Web. But it all starts with your feedback, so please keep it coming.
Allen Wirfs-Brock
Microsoft JavaScript Language Architect