Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript: override Date.prototype.constructor

I'd like to change the behaviour of the standard Date object. Years between 0..99 passed to the constructor should be interpreted as fullYear (no add of 1900). But my following function doesn't work

var oDateConst = Date.prototype.constructor; // save old contructor

Date.prototype.constructor = function () {
    var d = oDateConst.apply(oDateConst, arguments); // create object with it
    if ( ((arguments.length == 3) || (arguments.length == 6))
        && ((arguments[0] < 100) && (arguments[0] >= 0))) {
        d.setFullYear(arguments[0]);
    }
    return d;
}

Why does it never get called? How would you solve this problem?

like image 202
Lauber Bernhard Avatar asked Dec 12 '12 12:12

Lauber Bernhard


2 Answers

With reference to the technique mentioned in Matthew Albert's post, apart from the point which Dan Hlavenka posted, there is one more test scenario which fails. See the following code:

typeof Date() == typeof new Date()     //Should be false, but it returns true

In a legacy project, there is a chance the above scenario could break few scenarios. Apart from the above and what Dan Hlavenka pointed, I agree that this is the most complete solution so far.

like image 186
Hardik Shah Avatar answered Nov 15 '22 17:11

Hardik Shah


The reason it never gets called is because you're changing the constructor property on Date.prototype. However you're probably still creating a date using the code new Date(). So it never uses your constructor. What you really want to do is create your own Date constructor:

function MyDate() {
    var d = Date.apply(Date, arguments);
    if ((arguments.length == 3 || arguments.length == 6)
        && (arguments[0] < 100 && arguments[0] >= 0)) {
        d.setFullYear(arguments[0]);
    return d;
}

Then you can create your new date like this:

var d = MyDate();

Edit: Instead of using Date.apply I would rather use the following instantiate function which allows you to apply arguments to a constructor function:

var bind = Function.bind;
var unbind = bind.bind(bind);

function instantiate(constructor, args) {
    return new (unbind(constructor, null).apply(null, args));
}

This is how I would implement the new date constructor:

function myDate() {
    var date = instantiate(Date, arguments);
    var args = arguments.length;
    var arg = arguments[0];

    if ((args === 3 || args == 6) && arg < 100 && arg >= 0)
        date.setFullYear(arg);
    return date;
}

Edit: If you want to override the native Date constructor then you must do something like this:

Date = function (Date) {
    MyDate.prototype = Date.prototype;

    return MyDate;

    function MyDate() {
        var date = instantiate(Date, arguments);
        var args = arguments.length;
        var arg = arguments[0];

        if ((args === 3 || args == 6) && arg < 100 && arg >= 0)
            date.setFullYear(arg);
        return date;
    }
}(Date);
like image 39
Aadit M Shah Avatar answered Nov 15 '22 15:11

Aadit M Shah