Skip to content

Intl.[ctor] called with unusual target produces strange behavior #3692

@dilijev

Description

@dilijev

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%                                                   │
└──────────────────┴────────────────────────────────────────────────────────────────────────┘

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions