通过调用prototype.constructor.apply来实例化JavaScript对象

Ate*_*ral 54 javascript reflection

让我从一个具体的例子开始,我正在尝试做什么.

我在表单中有一系列年,月,日,小时,分钟,秒和毫秒组件[ 2008, 10, 8, 00, 16, 34, 254 ].我想使用以下标准构造函数实例化Date对象:

new Date(year, month, date [, hour, minute, second, millisecond ])
Run Code Online (Sandbox Code Playgroud)

如何将我的数组传递给此构造函数以获取新的Date实例?[ 更新:我的问题实际上超出了这个具体的例子.我想要内置JavaScript类的一般解决方案,如Date,Array,RegExp等,其构造函数超出我的范围.]

我正在尝试做类似以下的事情:

var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
var d = Date.prototype.constructor.apply(this, comps);
Run Code Online (Sandbox Code Playgroud)

我可能new在某处需要一个" ".上面只是返回当前时间,好像我已经调用了" (new Date()).toString()".我也承认我可能完全与上述方向错误:)

注意:请不要eval(),也不要逐个访问数组项.我很确定我应该可以按原样使用数组.


更新:进一步的实验

由于还没有人能够提出一个有效的答案,我已经做了更多的游戏.这是一个新发现.

我可以用自己的班级做到这一点:

function Foo(a, b) {
    this.a = a;
    this.b = b;

    this.toString = function () {
        return this.a + this.b;
    };
}

var foo = new Foo(1, 2);
Foo.prototype.constructor.apply(foo, [4, 8]);
document.write(foo); // Returns 12 -- yay!
Run Code Online (Sandbox Code Playgroud)

但它不适用于内在的Date类:

var d = new Date();
Date.prototype.constructor.call(d, 1000);
document.write(d); // Still returns current time :(
Run Code Online (Sandbox Code Playgroud)

它也不适用于Number:

var n = new Number(42);
Number.prototype.constructor.call(n, 666);
document.write(n); // Returns 42
Run Code Online (Sandbox Code Playgroud)

也许这对内在物体来说是不可能的?我正在使用Firefox BTW进行测试.

Ate*_*ral 64

我已经对自己做了更多的调查,并得出结论,由于Date类的实现方式,这是一个不可能的壮举.

我查看了SpiderMonkey源代码,了解Date的实现方式.我认为这一切归结为以下几行:

static JSBool
Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble *date;
    JSString *str;
    jsdouble d;

    /* Date called as function. */
    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
        int64 us, ms, us2ms;
        jsdouble msec_time;

        /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
         * so compute ms from PRMJ_Now.
         */
        us = PRMJ_Now();
        JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
        JSLL_DIV(ms, us, us2ms);
        JSLL_L2D(msec_time, ms);

        return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
    }

    /* Date called as constructor. */
    // ... (from here on it checks the arg count to decide how to create the date)
Run Code Online (Sandbox Code Playgroud)

当Date用作函数时(或者是Date()或者Date.prototype.constructor(),它们完全相同),它默认以当地时间格式返回当前时间作为字符串.这与传入的任何参数无关:

alert(Date()); // Returns "Thu Oct 09 2008 23:15:54 ..."
alert(typeof Date()); // Returns "string"

alert(Date(42)); // Same thing, "Thu Oct 09 2008 23:15:54 ..."
alert(Date(2008, 10, 10)); // Ditto
alert(Date(null)); // Just doesn't care
Run Code Online (Sandbox Code Playgroud)

我不认为在JS级别可以做任何事情来绕过这个.这可能是我在这个主题中追求的结束.

我也发现了一些有趣的东西:

    /* Set the value of the Date.prototype date to NaN */
    proto_date = date_constructor(cx, proto);
    if (!proto_date)
        return NULL;
    *proto_date = *cx->runtime->jsNaN;
Run Code Online (Sandbox Code Playgroud)

Date.prototype是一个内部值为的Date实例NaN,因此,

alert(Date.prototype); // Always returns "Invalid Date"
                       // on Firefox, Opera, Safari, Chrome
                       // but not Internet Explorer
Run Code Online (Sandbox Code Playgroud)

IE不会让我们失望.它做的事情有点不同,可能会设置内部值,-1以便Date.prototype始终在epoch之前稍微返回一个日期.


更新

我终于挖到了ECMA-262本身,事实证明,我想要实现的(使用Date对象)是 - 根据定义 - 不可能:

15.9.2日期构造函数被称为函数

当Date作为函数而不是构造函数调用时,它返回表示当前时间(UTC)的字符串.

注意函数调用Date(…)不等同于new Date(…) 具有相同参数的对象创建表达式.

15.9.2.1日期([年[,月份[,日期[,小时[,分钟[,秒[,ms]]]]]]]])

所有参数都是可选的; 提供的任何参数都被接受但完全被忽略.创建一个字符串,就像表达式一样返回(new Date()).toString().

  • 现在看来,现在可以存在`Function.prototype.bind`的原生实现:`var d = new(Date.bind.apply(Date,[2008,10,8,50,16,34,254]) );`将产生一个`Date`实例. (14认同)
  • @CrescentFresh的解决方案使用null:`var d = new(Date.bind.apply(Date,[null,2014,2,28,23,30]));`返回一个对象:`Date {Fri Mar 28 2014 23:30:00 GMT + 0100}` (2认同)

小智 14

我很难称之为优雅,但在我的测试中(FF3,Saf4,IE8),它的工作原理如下:

var arr = [ 2009, 6, 22, 10, 30, 9 ];

而不是这个:

var d = new Date( arr[0], arr[1], arr[2], arr[3], arr[4], arr[5] );

试试这个:

var d = new Date( Date.UTC.apply( window, arr ) + ( (new Date()).getTimezoneOffset() * 60000 ) );


Ant*_*nes 8

您可以通过以下方式解决具体案例: -

function writeLn(s)
{
    //your code to write a line to stdout
    WScript.Echo(s)
}

var a =  [ 2008, 10, 8, 00, 16, 34, 254 ]

var d = NewDate.apply(null, a)

function NewDate(year, month, date, hour, minute, second, millisecond)
{
    return new Date(year, month, date, hour, minute, second, millisecond);
}

writeLn(d)
Run Code Online (Sandbox Code Playgroud)

但是,您正在寻找更通用的解决方案.用于创建构造函数方法的建议代码是拥有它return this.

因此: -

function Target(x , y) { this.x = x, this.y = y; return this; }
Run Code Online (Sandbox Code Playgroud)

可以建造: -

var x = Target.apply({}, [1, 2]);
Run Code Online (Sandbox Code Playgroud)

然而,并非所有实现都以这种方式工作,尤其是因为原型链是错误的: -

var n = {};
Target.prototype = n;
var x = Target.apply({}, [1, 2]);
var b = n.isPrototypeOf(x); // returns false
var y = new Target(3, 4);
b = n.isPrototypeOf(y); // returns true
Run Code Online (Sandbox Code Playgroud)