问题描述
我正在寻找一种基于 (a) 构造函数的名称和 (b) 包含参数的数组来构造任意 JavaScript 对象的方法.我在 stackoverflow 的另一个线程中发现了这个函数(由 Matthew Crumley 编写?):
I'm looking for a way to construct arbitrary JavaScript objects based on (a) the name of the constructor, and (b) an array containing the arguments. I found this function (by Matthew Crumley ?) in an other thread on stackoverflow:
function construct(constructor, args) {
function F() { return constructor.apply(this, args); }
F.prototype = constructor.prototype;
return new F();
}
这适用于用 JavaScript 编写的构造函数,但如果我尝试构造(日期,[...]),它会因类型错误而失败.我还不知道是否有更多的本机构造函数无法处理.我的问题是……
This works well with constructors written in JavaScript, but it fails with a TypeError if I try construct(Date, [...]). I don't know yet if there are more native constructors that this function can't handle. My questions are then ...
- 较新版本的 JavaScript (ECMAScript 5) 中是否有可以解决我的问题的函数?
- 如果没有,有什么方法可以检查有问题的构造函数,看看是否可以使用上述函数?(如果不能,我可能不得不使用 eval("new "+cname+"("+arglist+")").)
/乔恩
推荐答案
在 ES5 中,你可以通过 bind
.
In ES5, you can do it via bind
.
function construct(constructor, args) {
return new (constructor.bind.apply(constructor, [null].concat(args)));
}
之所以有效是因为 bind
仍然使用 [[Construct]] 抽象运算符,当绑定函数出现在 new
的右侧时,每个 http://es5.github.com/#x15.3.4.5.2 说
which works because bind
still uses the [[Construct]] abstract operator when the bound function appears to the right of new
per http://es5.github.com/#x15.3.4.5.2 which says
当使用一个参数列表 ExtraArgs 调用函数对象的 [[Construct]] 内部方法时,使用 bind
函数创建的 F,执行以下步骤:
- 令 target 为 F 的 [[TargetFunction]] 内部属性的值.
- 如果目标没有 [[Construct]] 内部方法,则抛出 TypeError 异常.
- 令 boundArgs 为 F 的 [[BoundArgs]] 内部属性的值.
- 让 args 成为一个新列表,其中包含与列表 boundArgs 相同的值,其后跟与列表 ExtraArgs 相同的值.
- 返回调用 target 提供 args 作为参数的 [[Construct]] 内部方法的结果.
但是Function.prototype.bind
的大多数实现尝试将语言功能向后移植到 ES3 实现上,但无法正确处理用作构造函数的绑定函数,因此如果您不确定自己的代码在真正的 ES5 实现上运行,那么你必须回到黑客三角:
but most implementations of Function.prototype.bind
that attempt to back-port the language feature onto ES3 implementations do not correctly handle bound functions used as a constructor, so if you're not sure your code is running on a real ES5 implementation then you have to fall back to the triangle of hackery:
function applyCtor(ctor, args) {
// Triangle of hackery which handles host object constructors and intrinsics.
// Warning: The goggles! They do nothing!
switch (args.length) {
case 0: return new ctor;
case 1: return new ctor(args[0]);
case 2: return new ctor(args[0], args[1]);
case 3: return new ctor(args[0], args[1], args[2]);
case 4: return new ctor(args[0], args[1], args[2], args[3]);
case 5: return new ctor(args[0], args[1], args[2], args[3], args[4]);
case 6: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
case 7: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
case 8: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
case 9: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
case 10: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
case 11: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]);
case 12: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]);
}
// End triangle of hackery
// Create a throwaway subclass of ctor whose constructor does nothing.
function TemporarySubclass() {}
TemporarySubclass.prototype = ctor.prototype;
var instance = new TemporarySubclass();
instance.constructor = ctor; // Patch constructor property
// Run the constructor. This assumes that [[Call]] internal method is the same as
// [[Construct]]. It might work with some builtins/host objects where "new` would not.
var returnValue = ctor.apply(instance, args);
// If the constructor returned a non-primitive value, return it instead.
switch (typeof returnValue) {
case 'object':
// If ctor is Array, it reaches here so we don't use broken Array subclass.
// Ditto for Date.
if (returnValue) { return returnValue; }
break;
case 'function':
return returnValue;
}
// Return the constructed instance.
return instance;
}
这篇关于如何构造 JavaScript 对象(使用“apply")?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!