通过调用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(",") + ");");