jbandi
jbandi

Reputation: 18169

Understanding Intl.DateTimeFormat as a JavaScript object

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

Answers (1)

Andrey
Andrey

Reputation: 4050

Original answer

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

Edit 2

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

Not cool hacks in 3.. 2.. 1..

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! ..."

Conclusion

As we've seen with the examples. System objects behaves just like normal configured javascript objects

Upvotes: 2

Related Questions