11

我想改变标准 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;
}

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

4

4 回答 4

34

它永远不会被调用的原因是因为您正在更改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;
}

然后您可以像这样创建新日期:

var d = MyDate();

编辑:而不是使用Date.apply我宁愿使用以下instantiate函数,它允许您将参数应用于构造函数

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

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

这就是我将如何实现新的日期构造函数:

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

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);
于 2012-12-12T12:34:55.053 回答
3

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

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

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

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

参考:

于 2016-09-15T14:59:13.427 回答
1

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

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

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

于 2017-04-17T23:21:58.933 回答
0

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

DateStub - 允许存根 Date 函数。
如果你有'新日期(...)。...' 洒在你的代码中,你想测试它,这是给你的。它也适用于“时刻”。

/**
 * 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:  AAkcasu@gmail.com
 */

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


于 2019-01-18T04:54:44.580 回答