通过调用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=新数据