В 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]]
Когда [[Construct]] внутреннийметод объекта функции F, созданный с использованием функции bind
, вызывается со списком аргументов ExtraArgs, предпринимаются следующие шаги:
- Пусть target будет значением F [[TargetFunction]] внутреннее свойство.
- Если у цели нет внутреннего метода [[Construct]], генерируется исключение TypeError.
- Пусть boundArgs будет значением внутреннего свойства [[BoundArgs]] для F.
- Пусть args будет новым списком, содержащим те же значения, что и для boundArgs списка в том же порядке, за которым следуют те же значения, что и для списка ExtraArgs в том же порядке.
- Возвращает результат вызова[[Construct]] внутренний метод назначения аргументов какАргументы.
, но большинство реализаций 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;
}