С ECMAScript5 Function.prototype.bind
все становится довольно чисто:
function newCall(Cls) {
return new (Function.prototype.bind.apply(Cls, arguments));
// or even
// return new (Cls.bind.apply(Cls, arguments));
// if you know that Cls.bind has not been overwritten
}
Может использоваться следующим образом:
var s = newCall(Something, a, b, c);
или даже напрямую:
var s = new (Function.prototype.bind.call(Something, null, a, b, c));
var s = new (Function.prototype.bind.apply(Something, [null, a, b, c]));
Это и решение на основе eval - единственные, которые всегда работают, даже со специальными конструкторами, такими как Date
:
var date = newCall(Date, 2012, 1);
console.log(date instanceof Date); // true
редактировать
Немного объяснений:
Нам нужно запустить new
для функции, которая принимает ограниченное количество аргументов. Метод bind
позволяет нам сделать это так:
var f = Cls.bind(anything, arg1, arg2, ...);
result = new f();
Параметр anything
не имеет большого значения, поскольку ключевое слово new
сбрасывает контекст f
. Однако это требуется по синтаксическим причинам. Теперь для вызова bind
: нам нужно передать переменное количество аргументов, так что вот что получается:
var f = Cls.bind.apply(Cls, [anything, arg1, arg2, ...]);
result = new f();
Давайте обернем это в функцию. Cls
передается как arugment 0, так что это будет наш anything
.
function newCall(Cls /*, arg1, arg2, ... */) {
var f = Cls.bind.apply(Cls, arguments);
return new f();
}
На самом деле временная переменная f
вообще не нужна:
function newCall(Cls /*, arg1, arg2, ... */) {
return new (Cls.bind.apply(Cls, arguments))();
}
Наконец, мы должны убедиться, что bind
действительно то, что нам нужно. (Cls.bind
возможно, было перезаписано). Поэтому замените его на Function.prototype.bind
, и мы получим окончательный результат, как указано выше.