Reputation: 18169
I do not understand the behavior of Intl.DateTimeFormat
.
It does not expose the behavior I would expect from a JavaScript object. I would like to understand why?
The following snippet demonstrates that the format
method on DateTimeFormat
can't be overridden. How is this possible?
const itDateTimeFormat1 = new window.Intl.DateTimeFormat('it-CH');
const originalFormat = itDateTimeFormat1.format;
itDateTimeFormat1.format = function(date){ return 'Overriden! ' + originalFormat(date)};
console.log(itDateTimeFormat1.format(new Date())); // -> 13/7/2017
Also deriving from DateTimeFormat
via prototypal inheritance seems not possible. The following snippet throws an error:
const itDateTimeFormat2 = new window.Intl.DateTimeFormat('it-CH');
const wrappedDateTimeFormat = Object.create(itDateTimeFormat2);
wrappedDateTimeFormat.format = function(date){ return 'Overriden! ' };
console.log(wrappedDateTimeFormat.format(new Date()));
// Firefox:
// TypeError: Intl.DateTimeFormat.prototype.format called on value that's not an object initialized as a DateTimeFormat
// Chrome:
// Uncaught TypeError: Method format called on incompatible receiver #<DateTimeFormat>
Why is DateTimeFormat
not behaving like a "normal" JavaScript object?
How is it possible for DateTimeFormat
to prevent overriding a method?
How is it possible for DateTimeFormat
to prevent overriding on a derived object?
Upvotes: 2
Views: 1314
Reputation: 4050
Well, since it's a built-in object - it is good to have it frozen. But you can do things like this just in Javascript with Object.defineProperty.
See the following snipper. You can prevent override of your properties with writeable: false
. (I've used JS 5.1)
var MyCoolConstructor = function () {
Object.defineProperty(this, 'nonWriteable', {
enumerable: false,
configurable: false,
writable: false,
value: function () {return 42;}
});
};
var instance = new MyCoolConstructor();
console.log(instance.nonWriteable()); //42
instance.nonWriteable = function () {return 'overriden';}
console.log(instance.nonWriteable()); //42
How to prevent prototypal inheritance?
Check this simple snippet. You can just check if current context have the same prototype as your constructor.
var MyCoolConstructor = function () {
this.foo = function () {
if (Object.getPrototypeOf(this) === MyCoolConstructor.prototype) {
return 42;
} else {
throw new Error('bad this');
}
};
};
var instance = new MyCoolConstructor();
console.log(instance.foo());
//42
var instance2 = Object.create(instance);
console.log(instance2.foo())
//Bad this
Derived object method override can also be prevented in the same manner, they derive property config. Check this snippet which is combination of previous 2
var MyCoolConstructor = function () {
Object.defineProperty(this, 'foo', {
value: function () {
if (Object.getPrototypeOf(this) === MyCoolConstructor.prototype) {
return 42;
} else {
throw new Error('bad this');
}
},
writeable: false
});
};
var instance = new MyCoolConstructor();
console.log(instance.foo());
//42
var derivedInstance = Object.create(instance);
derivedInstance.foo = function () {
return 'overriden';
};
console.log(derivedInstance.foo());
//Bad this. Can't be overridden because foo property is not writeable
If you really want to override smth, Object.defineProperty
come to the rescue.
const itDateTimeFormat2 = new window.Intl.DateTimeFormat('it-CH');
const wrappedDateTimeFormat = Object.create(itDateTimeFormat2);
Object.defineProperty(wrappedDateTimeFormat, 'format', {value: function(date) { return 'Overridden!' + date.toString(); }})
wrappedDateTimeFormat.format()
//"Overriden! ..."
As we've seen with the examples. System objects behaves just like normal configured javascript objects
Upvotes: 2