I'm trying to subclass/extend the native Date object, without modifying the native object itself.
I've tried this:
var util = require('util'); function MyDate() { Date.call(this); } util.inherits(MyDate, Date); MyDate.prototype.doSomething = function() { console.log('Doing something...'); }; var date = new MyDate(); date.doSomething(); console.log(date); console.log(date.getHours());
and this:
function MyDate() { } MyDate.prototype = new Date(); MyDate.prototype.doSomething = function() { console.log("DO"); } var date = new MyDate(); date.doSomething(); console.log(date);
In both cases, the date.doSomething()
works, but when I call any of the native methods such as date.getHours()
or even console.log(date)
, I get 'TypeError: this is not a Date object.'
Any ideas? Or am I stuck to extending the top-level Date object?
It is used to work with dates and times. The Date object is created by using new keyword, i.e. new Date(). The Date object can be used date and time in terms of millisecond precision within 100 million days before or after 1/1/1970.
"The expression new Date() returns the current time in internal format, as an object containing the number of milliseconds elapsed since the start of 1970 in UTC.
The most used method to get the date in JavaScript is the new Date() object. By default, when you run new Date() in your terminal, it uses your browser's time zone and displays the date as a full text string, like Fri Jul 02 2021 12:44:45 GMT+0100 (British Summer Time).
Looking at the v8 code, in date.js:
function DateGetHours() { var t = DATE_VALUE(this); if (NUMBER_IS_NAN(t)) return t; return HOUR_FROM_TIME(LocalTimeNoCheck(t)); }
And looks like DATE_VALUE is a macro that does this:
DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError());
So, seems like v8 won't let you subclass Date.
This can be done in ES5. It requires modifying the prototype chain directly. This is done using __proto__
or Object.setPrototypeOf()
. I'm using __proto__
in the sample code since that's most widely supported (although the standard is Object.setPrototypeOf
).
function XDate(a, b, c, d, e, f, g) { var x; switch (arguments.length) { case 0: x = new Date(); break; case 1: x = new Date(a); break; case 2: x = new Date(a, b); break; case 3: x = new Date(a, b, c); break; case 4: x = new Date(a, b, c, d); break; case 5: x = new Date(a, b, c, d, e); break; case 6: x = new Date(a, b, c, d, e, f); break; default: x = new Date(a, b, c, d, e, f, g); } x.__proto__ = XDate.prototype; return x; } XDate.prototype.__proto__ = Date.prototype; XDate.prototype.foo = function() { return 'bar'; };
The trick is that we actually instantiate a Date
object (with the correct number of arguments) which gives us an object with it's internal [[Class]]
set correctly. Then we modify it's prototype chain to make it an instance of XDate.
So, we can verify all this by doing:
var date = new XDate(2015, 5, 18) console.log(date instanceof Date) //true console.log(date instanceof XDate) //true console.log(Object.prototype.toString.call(date)) //[object Date] console.log(date.foo()) //bar console.log('' + date) //Thu Jun 18 2015 00:00:00 GMT-0700 (PDT)
This is the only way I know of to subclass date because the Date()
constructor does some magic to set the internal [[Class]]
and most date methods require that to be set. This will work in Node, IE 9+ and almost all other JS engines.
Similar approach can be used for subclassing Array.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With