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

让我从一个我想要做的具体例子开始。

我有一组年,月,日,时,分,秒和毫秒的组件在[ 2008, 10, 8, 00, 16, 34, 254 ] 。 我想使用下面的标准构造函数实例化一个Date对象:

 new Date(year, month, date [, hour, minute, second, millisecond ]) 

我如何将我的数组传递给这个构造函数来获得一个新的Date实例? [ 更新 :我的问题实际上超出了这个具体的例子。 我想要一个内置的JavaScript类的一般解决scheme,如Date,Array,RegExp等,其构造函数是我无法实现的。 ]

我正在尝试做如下的事情:

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

我可能需要在那里有一个“ new ”的地方。 上面只是返回当前时间,就好像我已经调用了“ (new Date()).toString() ”。 我也承认,我可能会完全按照上面的方向错误的方向:)

注意 :没有eval() ,也没有一个一个地访问数组项目。 我很确定我应该能够使用该数组。


更新:进一步的实验

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

我可以用我自己的class级做这个:

 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 

也许这对内在物体是不可能的? 我正在用BTWtesting。

我已经做了更多的自己的调查,并得出这样的结论, 这是一个不可能的壮举 ,由于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) 

当Date被用作一个函数(或者Date()或者Date.prototype.constructor() ,它们是完全一样的),它默认将当前时间作为一个string以区域设置格式返回。 这是不pipe传入的参数:

 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的Date实例,因此,

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

IE并没有让我们失望。 它做的事情有点不同,可能将内部值设置为-1以便Date.prototype始终在时期之前返回一个date。


更新

我终于深入到ECMA-262本身,事实certificate,我试图实现(与Date对象)是 – 根据定义 – 是不可能的:

15.9.2作为函数调用的Date构造函数

当Date被作为一个函数而不是一个构造函数调用时,它将返回一个表示当前时间(UTC)的string。

注意函数调用Date(…) new Date(…)与具有相同参数的对象创buildexpression式new Date(…)不等效。

15.9.2.1date([年份[,月份[,date[,小时[,分钟[,秒[,毫秒]]]]]]])

所有的参数都是可选的; 任何提供的参数都被接受,但完全被忽略。 通过expression式(new Date()).toString()来创build并返回string。

我几乎没有称这个优雅,但在我的testing(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 ) );

这是你可能如何解决具体的情况:

 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) 

但是你正在寻找一个更一般的解决scheme。 build立一个构造函数方法的build议代码是让它return this

因此: –

 function Target(x , y) { this.x = x, this.y = y; return this; } 

可以构build:

 var x = Target.apply({}, [1, 2]); 

然而,并不是所有的实现都是这样工作的,至less因为原型链是错误的:

 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 

这不是优雅的,但这是一个解决scheme:

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

这种工作方式应该是非常明显的。 它通过代码生成创build一个function。 这个例子对于你创build的每个构造函数都有一个固定数量的参数,但这是有用的。 在大多数情况下,你有最多数量的争论。 这也比其他一些例子好,因为它允许你生成一次代码,然后重新使用它。 所生成的代码利用了javascript的variables参数function,这样您就可以避免为每个参数命名(或者将它们拼出到列表中,并将parameter passing给您生成的函数)。 这是一个工作的例子:

 var dateConstructor = new GeneratedConstructor("Date", 3) dateConstructor.createObject( [ 1982, 03, 23 ] ) 

这将返回以下内容:

星期五四月23 1982 00:00:00 GMT-0800(PST)

这确实还是…有点难看。 但它至less可以方便地隐藏混乱,并不认为编译的代码本身可以收集垃圾(因为这可能取决于实施,并且是错误的可能区域)。

干杯,Scott S. McCoy

这是你如何做到的:

 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)的内build函数或构造函数。

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

顺便说一下,其他答案中的一个build议将this从构造函数中返回。 这可能会使使用经典inheritance扩展对象非常困难,所以我会认为它是一个反模式。

你可以做到这一点公然, 公然滥用评估:

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

样本用法:

 function Point(x,y) { this.x = x; this.y = y; } var p = __new(Point, [10, 20]); alert(px); //10 alert(p instanceof Point); //true 

享受=)。

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

我知道这已经很久了,但是我对这个问题有真正的答案。 这是不可能的。 有关通用解决scheme,请参阅https://gist.github.com/747650

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

这是另一个解决scheme:

 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 d = new Date(comps [0],comps [1],comps [2],comps [3],comps [4],comps [5],comps [6]);

编辑:

但要记住一个javascriptdate对象使用索引数月,所以上面的数组意味着

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

 var comps = [ 2008, 10, 8, 00, 16, 34, 254 ]; var d = eval("new Date(" + comps.join(",") + ");");