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

通过调用prototype.constructor.apply实例化JavaScript对象,javascript,reflection,Javascript,Reflection,让我从一个具体的例子开始,说明我正在尝试做什么 我有一个由年、月、日、小时、分钟、秒和毫秒组成的数组,格式为[2008,10,8,00,16,34,254]。我想使用以下标准构造函数实例化日期对象: new Date(year, month, date [, hour, minute, second, millisecond ]) 如何将数组传递给此构造函数以获取新的日期实例?[更新:我的问题实际上超出了这个特定示例的范围。我想要一个内置JavaScript类的通用解决方案,如Date、Arr

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

我有一个由年、月、日、小时、分钟、秒和毫秒组成的数组,格式为
[2008,10,8,00,16,34,254]
。我想使用以下标准构造函数实例化日期对象:

new Date(year, month, date [, hour, minute, second, millisecond ])
如何将数组传递给此构造函数以获取新的日期实例?[更新:我的问题实际上超出了这个特定示例的范围。我想要一个内置JavaScript类的通用解决方案,如Date、Array、RegExp等,其构造函数超出了我的能力。]

我正在尝试做如下事情:

var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
var d = Date.prototype.constructor.apply(this, comps);
我可能需要一个“
新的”
”在那里的某个地方。以上只是返回当前时间,就好像我调用了“
(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!
但它不适用于内在的Date类:

var d = new Date();
Date.prototype.constructor.call(d, 1000);
document.write(d); // Still returns current time :(
它也不适用于数字:

var n = new Number(42);
Number.prototype.constructor.call(n, 666);
document.write(n); // Returns 42

也许这对于内在对象是不可能的?我正在使用Firefox进行测试。

以下是您解决特定情况的方法:-

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)
然而,您正在寻找一个更通用的解决方案。创建构造函数方法的建议代码是让它
返回此

因此:-

function Target(x , y) { this.x = x, this.y = y; return this; }
可以构建:-

var x = Target.apply({}, [1, 2]);
然而,并非所有的实现都是这样工作的,尤其是因为原型链可能是错误的:-

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
编辑

对不起,我肯定几年前我就这么做了,现在我要坚持:

变量d=新日期(组合[0]、组合[1]、组合[2]、组合[3]、组合[4]、组合[5]、组合[6])

编辑:

但是请记住,javascript日期对象使用索引长达数月,因此上面的数组意味着


2008年11月8日00:16:34:254

我自己做了更多的调查,并得出结论,这是一个不可能的壮举,因为Date类是如何实现的

var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
var d = eval("new Date(" + comps.join(",") + ");");
我已经检查了源代码,看看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)
当日期用作函数时(作为
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
我不认为在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;
Date.prototype
是一个日期实例,其内部值为
NaN
,因此

alert(Date.prototype); // Always returns "Invalid Date"
                       // on Firefox, Opera, Safari, Chrome
                       // but not Internet Explorer
我没有让我们失望。它的工作方式稍有不同,可能会将内部值设置为
-1
,因此Date.prototype始终返回略早于历元的日期


更新 我终于深入研究了ECMA-262本身,结果证明,我试图实现的(使用Date对象)是——根据定义——不可能的:

15.9.2作为函数调用的日期构造函数

当日期被称为 函数而不是构造函数, 它返回一个字符串,表示 当前时间(UTC)

注意功能 调用日期(…)与 对象创建表达式
新日期(…)
用同样的论点

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

所有的 参数是可选的;有什么论据吗 提供的产品已被接受,但仍在使用中 完全被忽视了。一串是 创建并返回,就像 表达式
(新日期()).toString()


虽然不那么优雅,但这里有一个解决方案:

function GeneratedConstructor (methodName, argumentCount) {
    var params = []

    for (var i = 0; i < argumentCount; i++) {
        params.push("arguments[" + i + "]")
    }

    var code = "return new " + methodName + "(" + params.join(",") +  ")"

    var ctor = new Function(code)

    this.createObject = function (params) {
        return ctor.apply(this, params)
    }
}
function createInstance(Constructor, args){
    var TempConstructor = function(){};
    TempConstructor.prototype = Constructor.prototype;
    var instance = new TempConstructor;
    var ret = Constructor.apply(instance, args);
    return ret instanceof Object ? ret : instance;
}

console.log( createInstance(Date, [2008, 10, 8, 00, 16, 34, 254]) )
这将返回以下内容:

var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
var d = Date.prototype.constructor.apply(this, comps);
1982年4月23日星期五00:00:00 GMT-0800(太平洋标准时间)

它确实还是…有点难看。但它至少可以方便地隐藏混乱,并且不假设编译后的代码本身可以被垃圾收集(因为这可能取决于实现,并且可能是bug的一个区域)

干杯,
斯科特·麦考伊(Scott S.McCoy)

我很难称之为优雅,但在我的测试(FF3、Saf4、IE8)中,它可以工作:

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

与此相反:

var d=新日期(arr[0]、arr[1]、arr[2]、arr[3]、arr[4]、arr[5])

试试这个:


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

你可以用公然的,公然的滥用评估:

var newwrapper = function (constr, args) {
  var argHolder = {"c": constr};
  for (var i=0; i < args.length; i++) {
    argHolder["$" + i] = args[i];
  }

  var newStr = "new (argHolder['c'])(";
  for (var i=0; i < args.length; i++) {
    newStr += "argHolder['$" + i + "']";
    if (i != args.length - 1) newStr += ", ";
  }
  newStr += ");";

  return eval(newStr);
}

享受=)

我知道已经很久了,但我有这个问题的真正答案。这绝非不可能。有关通用解决方案,请参阅

var F = function(){};
F.prototype = Date.prototype;
var d = new F();
Date.apply(d, comps);

下面是另一个解决方案:

function GeneratedConstructor (methodName, argumentCount) {
    var params = []

    for (var i = 0; i < argumentCount; i++) {
        params.push("arguments[" + i + "]")
    }

    var code = "return new " + methodName + "(" + params.join(",") +  ")"

    var ctor = new Function(code)

    this.createObject = function (params) {
        return ctor.apply(this, params)
    }
}
function createInstance(Constructor, args){
    var TempConstructor = function(){};
    TempConstructor.prototype = Constructor.prototype;
    var instance = new TempConstructor;
    var ret = Constructor.apply(instance, args);
    return ret instanceof Object ? ret : instance;
}

console.log( createInstance(Date, [2008, 10, 8, 00, 16, 34, 254]) )
这就是你如何做到的:

function applyToConstructor(constructor, argArray) {
    var args = [null].concat(argArray);
    var factoryFunction = constructor.bind.apply(constructor, args);
    return new factoryFunction();
}

var d = applyToConstructor(Date, [2008, 10, 8, 00, 16, 34, 254]);
它将与任何构造函数一起工作,而不仅仅是内置的或可以兼作函数(如Date)的构造函数

但是,它确实需要Ecmascript 5.bind函数。垫片可能无法正常工作


顺便说一句,另一个答案建议从构造函数中返回
this
。这使得使用经典继承来扩展对象是非常困难的,所以我认为它是一个反模式。

< P>它将与ES6扩展算子一起工作。
function gettime()
{
    var q = new Date;
    arguments.length && q.setTime( ( arguments.length === 1
        ? typeof arguments[0] === 'number' ? arguments[0] : Date.parse( arguments[0] )
        : Date.UTC.apply( null, arguments ) ) + q.getTimezoneOffset() * 60000 );
    return q;
};

gettime(2003,8,16)

gettime.apply(null,[2003,8,16])
你只需:

const arr = [2018, 6, 15, 12, 30, 30, 500];
const date = new Date(...arr);

console.log(date);

对于ES6语法,至少有两种方法可以实现这一点:

var comps=[2008,10,8,00,16,34,254];
//与spread操作符一起
变量d1=新数据