-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Closed
Description
Found by reading code and wondering "what does this button do?"
Apparently Intl.* constructors do not actually have to be called with new, and can be called with anything as the target.
Using Intl
or undefined
as the target (first param of call
) produces the same result as calling new.
Specifically allowing the target to be Intl
or undefined
(see Intl.js code, snippet below) allows for calling as Intl.NumberFormat
and assigning the same to a var and then calling, producing the same results.
// function NumberFormat
if (this === Intl || this === undefined) {
// after the call to the same function using new, this === NumberFormat, but any other value of this will be allowed past this check, leading to strange behavior
return new NumberFormat(locales, options);
}
## Source
let options = {style:'currency', currency:'USD'};
print(new Intl.NumberFormat('en-us', options).format(42));
print(Intl.NumberFormat.call(Intl, 'en-us', options).format(42));
print(Intl.NumberFormat.call(undefined, 'en-us', options).format(42));
print(Intl.NumberFormat('en-us', options).format(42)); /* call(Intl, ...) */
nf = Intl.NumberFormat;
print(nf('en-us', options).format(42)); /* call(undefined, ...) */
┌──────────────────┬────────┐
│ ch-1.7.1 │ $42.00 │
│ ch-master-latest │ $42.00 │
│ d8 │ $42.00 │
│ jsc │ $42.00 │
│ node │ $42.00 │
│ node-ch │ │
│ sm │ │
└──────────────────┴────────┘
Using anything else apparently returns the object passed in:
## Source
let options = {style:'currency', currency:'USD'};
let a = Intl.NumberFormat.call({}, 'en-us', options);
print(a);
print(a.format(42));
┌──────────────────┬───────────────────────────────────────────────────────────────┐
│ d8 │ [object Object] │
│ jsc │ $42.00 │
│ sm │ │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ ch-1.7.1 │ [object Object] │
│ ch-master-latest │ TypeError: Object doesn't support property or method 'format' │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ node-ch │ {} │
│ │ TypeError: Object doesn't support property or method 'format' │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ node │ NumberFormat {} │
│ │ $42.00 │
└──────────────────┴───────────────────────────────────────────────────────────────┘
## Source
let options = {style:'currency', currency:'USD'};
let a = Intl.NumberFormat.call(function foo() {}, 'en-us', options);
print(a);
print(a.format(42));
┌──────────────────┬───────────────────────────────────────────────────────────────┐
│ d8 │ [object Object] │
│ jsc │ $42.00 │
│ sm │ │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ ch-1.7.1 │ function foo() {} │
│ ch-master-latest │ TypeError: Object doesn't support property or method 'format' │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ node-ch │ [Function: foo] │
│ │ TypeError: Object doesn't support property or method 'format' │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ node │ NumberFormat {} │
│ │ $42.00 │
└──────────────────┴───────────────────────────────────────────────────────────────┘
## Source
let options = {style:'currency', currency:'USD'};
let a = Intl.NumberFormat.call(new (function foo() {}), 'en-us', options);
print(a);
print(a.format(42));
┌──────────────────┬───────────────────────────────────────────────────────────────┐
│ d8 │ [object Object] │
│ jsc │ $42.00 │
│ sm │ │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ ch-1.7.1 │ [object Object] │
│ ch-master-latest │ TypeError: Object doesn't support property or method 'format' │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ node-ch │ foo {} │
│ │ TypeError: Object doesn't support property or method 'format' │
├──────────────────┼───────────────────────────────────────────────────────────────┤
│ node │ NumberFormat {} │
│ │ $42.00 │
└──────────────────┴───────────────────────────────────────────────────────────────┘
Which leads to fun things like formatting a Date with something you would have hoped was a NumberFormat object
## Source
let a = Intl.NumberFormat.call(Intl.DateTimeFormat, 'en-us', { style: 'percent' });
print(a);
print((a.format || (new a).format)(new Date(2017,0,1)));
┌──────────────────┬────────────────────────────────────────────────────────────────────────┐
│ d8 │ [object Object] │
│ jsc │ 148,325,760,000,000% │
│ sm │ │
├──────────────────┼────────────────────────────────────────────────────────────────────────┤
│ ch-1.7.1 │ function DateTimeFormat() { [native code] } │
│ ch-master-latest │ ?1?/?1?/?2017 │
├──────────────────┼────────────────────────────────────────────────────────────────────────┤
│ node-ch │ { [Function: DateTimeFormat] __relevantExtensionKeys: [ 'ca', 'nu' ] } │
│ │ 1/1/2017 │
├──────────────────┼────────────────────────────────────────────────────────────────────────┤
│ node │ NumberFormat {} │
│ │ 148,325,760,000,000% │
└──────────────────┴────────────────────────────────────────────────────────────────────────┘