在 ES5 中,你可以通过 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 说
15.3.4.5.2 [[构造]]
当函数对象的 [[Construct]] 内部方法,使用 bind 函数创建的 F 使用参数列表 ExtraArgs 调用时,将执行以下步骤:
- 令 target 为 F 的 [[TargetFunction]] 内部属性的值。
- 如果目标没有 [[Construct]] 内部方法,则抛出 TypeError 异常。
- 令 boundArgs 为 F 的 [[BoundArgs]] 内部属性的值。
- 令 args 为一个新列表,其中包含与列表 boundArgs 相同的值,顺序相同,后跟与列表 ExtraArgs 相同的值,顺序相同。
- 返回调用目标的 [[Construct]] 内部方法的结果,提供 args 作为参数。
但是Function.prototype.bind 的大多数实现尝试将语言特性反向移植到 ES3 实现上并不能正确处理用作构造函数的绑定函数,所以如果您不确定您的代码是否在真正的 ES5 实现上运行,那么你必须回到黑客的三角:
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;
}