JavaScript:覆盖 Date.prototype.constructor

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13839318/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 19:54:52  来源:igfitidea点击:

JavaScript: override Date.prototype.constructor

javascriptdateconstructorprototype

提问by Lauber Bernhard

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

我想更改标准 Date 对象的行为。0..99传递给构造函数之间的年数应解释为fullYear(无添加1900)。但我的以下功能不起作用

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?

为什么它永远不会被调用?你会如何解决这个问题?

回答by Aadit M Shah

The reason it never gets called is because you're changing the constructorproperty 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:

它永远不会被调用的原因是因为你改变了constructor财产Date.prototype。但是,您可能仍在使用代码创建日期new Date()。所以它从不使用你的构造函数。您真正想做的是创建自己的 Date 构造函数:

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.applyI would rather use the following instantiatefunction which allows you to apply arguments to a constructor function:

编辑:除了使用的Date.apply,我宁愿使用下面的instantiate功能,让你的论点也适用于一个构造函数

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 构造函数,则必须执行以下操作:

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);

回答by Matthew Albert

Piggybacking on Aadit M Shah's native date constructor override - this should be a reply but I don't have enough SO rep for that - as @sakthi mentioned, you'd lose your native Date methods by doing it that way. This sucks a bit because Date methods are non-enumerable, so you have to implement a bit of a hack to clone them.

捎带 Aadit M Shah 的原生日期构造函数覆盖 - 这应该是一个回复,但我没有足够的 SO 代表 - 正如@sakthi 提到的,这样做会丢失你的原生 Date 方法。这有点糟糕,因为 Date 方法是不可枚举的,所以你必须实现一些技巧来克隆它们。

First off, I'd recommend notdoing it this way unless you have to. In my case, I was working in an application with a bunch of legacy code that was constructing dates using the format "m-d-yyyy", which works in chrome but not safari. I couldn't just do a find/replace in the app, because there were lots of instances where the date strings were being pulled from the service layer or the database. So, I decided to override the Date constructor in the case where there's a datestring argument in "m-d-yyyy" format. I wanted it to be as minimally-invasive as possible, so that it functions as a normal Date otherwise.

首先,除非万不得已,否则我建议要这样做。就我而言,我正在使用具有一堆遗留代码的应用程序工作,这些代码使用“md-yyyy”格式构建日期,该格式适用于 chrome 但不适用于 safari。我不能只是在应用程序中进行查找/替换,因为有很多实例是从服务层或数据库中提取日期字符串。因此,我决定在存在“md-yyyy”格式的日期字符串参数的情况下覆盖 Date 构造函数。我希望它的侵入性尽可能小,否则它就可以作为正常的 Date 使用。

Here are my changes - it should allow you to override date with some changes to the constructor, but everything else the same. You're going to want to change the MyDate constructor before instantiate is called to do whatever you want the constructor to handle. This will happen BEFORE the system Date constructor gets applied.

这是我的更改 - 它应该允许您通过对构造函数进行一些更改来覆盖日期,但其他一切都相同。您将需要在调用实例化之前更改 MyDate 构造函数以执行您希望构造函数处理的任何操作。这将在应用系统日期构造函数之前发生。

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

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

Date = function (Date) {

    // copy date methods - this is a pain in the butt because they're mostly nonenumerable
    // Get all own props, even nonenumerable ones
    var names = Object.getOwnPropertyNames(Date);
    // Loop through them
    for (var i = 0; i < names.length; i++) {
        // Skip props already in the MyDate object
        if (names[i] in MyDate) continue;
        // Get property description from o
        var desc = Object.getOwnPropertyDescriptor(Date, names[i]);
        // Use it to create property on MyDate
        Object.defineProperty(MyDate, names[i], desc);
    }

    return MyDate;

    function MyDate() {
        // we only care about modifying the constructor if a datestring is passed in
        if (arguments.length === 1 && typeof (arguments[0]) === 'string') {
            // if you're adding other date transformations, add them here

            // match dates of format m-d-yyyy and convert them to cross-browser-friendly m/d/yyyy
            var mdyyyyDashRegex = /(\d{1,2})-(\d{1,2})-(\d{4})/g;
            arguments[0] = arguments[0].replace(mdyyyyDashRegex, function (match, p1, p2, p3) {
                return p1 + "/" + p2 + "/" + p3;
            });
        }

        // call the original Date constructor with whatever arguments are passed in here
        var date = instantiate(Date, arguments);

        return date;
    }
}(Date);

references:

参考:

回答by Hardik Shah

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:

参考 Matthew Albert 的帖子中提到的技术,除了 Dan Hlavenka 发表的观点之外,还有一个测试场景失败了。请参阅以下代码:

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.

在遗留项目中,上述场景可能会破坏少数场景。除了上述和 Dan Hlavenka 所指出的,我同意这是迄今为止最完整的解决方案。

回答by Aydin Akcasu

Here is a solutions that is very flexible. It handles (I believe), all the different cases.

这是一个非常灵活的解决方案。它处理(我相信)所有不同的情况。

DateStub - Allows for stubbing out the Date function.
If you have 'new Date(...). ...' sprinkled throughout your code, and you want to test it, this is for you. It also works with 'moments'.

DateStub - 允许删除日期函数。
如果您有“新日期(...)”。...' 散布在您的代码中,您想测试它,这是给您的。它也适用于“时刻”。

/**
 * DateStub - Allows for stubbing out the Date function.  If you have
 *      'new Date(...)....' sprinkled throughout your code,
 *      and you want to test it, this is for you.
 *
 * @param {string} arguments Provide 0 to any number of dates in string format.
 *
 * @return a date object corresponding to the arguments passed in.
 *      If you pass only one date in, this will be used by all 'new Date()' calls.
 *      This also provides support for 'Date.UTC()', 'Date.now()', 'Date.parse()'.
 *      Also, this works with the moments library.
 *
 * Examples:
    {   // Test with 1 argument
        Date = DateStub('1/2/2033');        // Override 'Date'

        expect(new Date().toString())
            .toEqual('Sun Jan 02 2033 00:00:00 GMT-0500 (EST)');
        expect(new Date().toString())
            .toEqual('Sun Jan 02 2033 00:00:00 GMT-0500 (EST)');

        Date = DateStub.JavaScriptDate;     // Reset 'Date'
    }
    {   // Call subsequent arguments, each time 'new Date()' is called
        Date = DateStub('1/2/1111', '1/2/1222'
                        , '1/2/3333', '1/2/4444');  // Override 'Date'

        expect(new Date().toString())
            .toEqual('Mon Jan 02 1111 00:00:00 GMT-0500 (EST)');
        expect(new Date().toString())
            .toEqual('Sun Jan 02 1222 00:00:00 GMT-0500 (EST)');
        expect(new Date().toString())
            .toEqual('Fri Jan 02 3333 00:00:00 GMT-0500 (EST)');
        expect(new Date().toString())
            .toEqual('Sat Jan 02 4444 00:00:00 GMT-0500 (EST)');

        Date = DateStub.JavaScriptDate;     // Reset 'Date'
    }
    {   // Test 'Date.now()'.  You can also use: 'Date.UTC()', 'Date.parse()'
        Date = DateStub('1/2/2033');

        expect(new Date(Date.now()).toString())
                .toEqual('Sun Jan 02 2033 00:00:00 GMT-0500 (EST)');

        Date = DateStub.JavaScriptDate;     // Reset 'Date'
    }
 *
 * For more info:  [email protected]
 */

const DateStub =
    function () {
        function CustomDate(date) {
            if (!!date) { return new DateStub.JavaScriptDate(date); }
            return getNextDate();
        };
        function getNextDate() {
            return dates[index >= length ? index - 1 : index++];
        };

        if (Date.name === 'Date') {
            DateStub.prototype = Date.prototype;
            DateStub.JavaScriptDate = Date;

            // Add Date.* methods.
            CustomDate.UTC = Date.UTC;
            CustomDate.parse = Date.parse;
            CustomDate.now = getNextDate;
        }

        var dateArguments = (arguments.length === 0)
            ? [(new DateStub.JavaScriptDate()).toString()] : arguments
            , length = dateArguments.length
            , index = 0
            , dates = [];
        for (var i = 0; i < length; i++) {
            dates.push(new DateStub.JavaScriptDate(dateArguments[i]));
        }

        return CustomDate;
    };

module.exports = DateStub;

// If you have a test file, and are using node:
// Add this to the top:  const DateStub = require('./DateStub');