-
Notifications
You must be signed in to change notification settings - Fork 111
Description
The 1st Edition of ECMA 402 specified the [[Call]] behavior for Intl
constructors; e.g. Intl.DateTimeFormat.call(this [, locales [, options]])
to return the this
context object that was passed-in. In the 2nd Edition, the [[Call]] behavior no longer states that the passed-in context object should be retuned. This change is a potential compatibility hazard.
As the developer and maintainer of the popular FormatJS i18n libraries, I've begun receiving issues from developers testing Chrome Canary (49) that their dates and numbers were failing for format, causing an Error to be thrown.
The high-level framework integration libs that are part of FormatJS — react-intl, ember-intl, handlebars-intl — are used by many web apps, including many of Yahoo's web apps. All these libraries use the underlying intl-format-cache which memoizes the Intl
constructors because they are expensive to create. The memoization technique essentially does the following:
function constructIntlInstance(IntlConstructor) {
return function () {
var args = Array.prototype.slice.call(arguments);
var instance = Object.create(IntlConstructor.prototype);
IntlConstructor.apply(instance, args);
return instance;
};
}
Note: That this code depends on the following invariant:
var instance = Object.create(IntlConstructor.prototype);
instance === IntlConstructor.call(instance); // true
It is dependent on the the Intl
constructors being .call()
-able and the returning the context object passed-in. This .call()
behavior for the Intl
constructors is supported in all ECMA 402 1st Edition implementations.
After receiving an issue report about this code causing an Error to be thrown Chrome Canary (49), I dug in and found this recent V8 change which updates V8's implementation to match ECMA 402 2nd Edition, thus removing the code that returns the passed-in context object when the Intl
constructors are .call()
-ed.
Today, I've released intl-format-cache@2.0.5
which changes the memoization implementation to make sure the [[Construct]] behavior always happens by invoking the Intl
constructors with new
. Essentially doing the following:
function constructIntlInstance(IntlConstructor) {
return (...args) => new IntlConstructor(...args);
}
In ES5 an equivalent would be:
function constructIntlInstance(IntlConstructor) {
return function () {
var args = Array.prototype.slice.call(arguments);
return new (Function.prototype.bind.apply(IntlConstructor, [null].concat(args)))();
};
}
While this issue is now "fixed" in intl-format-cache, developers must upgrade their dependencies and re-deploy their apps. I will help to communicate this change, but I'm worried that removing the 1st Edition [[Call]] behavior will break many apps/sites 😞
How should we move forward to prevent end-users from having broken experiences?
Edited based on @rwaldron's feedback.