我有一个构造函数,我不知道它需要多少个参数,例如:
function someCtor(a,b,c){
var that = this;
that.a = a;
that.b = b;
that.c = c;
}
我需要创建一个函数,该函数将使用动态数量的参数返回该构造函数的实例:
function makeNew(ctor, arguments){
// this doesn't work, but it shows what I'm trying to achieve
return new ctor.apply(arguments);
}
我想使用该函数将动态参数传递给构造函数,如下所示:
var instanceOfCtor = makeNew(someCtor, [5,6,7]);
如何实现此功能?
答案 0 :(得分:5)
注意:请参阅最后的ES2015兼容性说明。
首先创建一个对象,将其底层原型设置为构造函数的prototype
属性通过Object.create
引用的对象,然后通过Function#apply
调用构造函数:
function makeNew(ctor, arguments){
var obj = Object.create(ctor.prototype);
var rv = ctor.apply(obj, arguments);
return rv && typeof rv === "object" ? rv : obj;
}
注意最后的一点点,所以我们正确地模拟new
运算符:当你通过new
调用构造函数时,如果它返回一个非null
对象引用,最终成为new
表达式的结果;如果它返回任何其他内容(或什么都没有),则new
创建的对象就是结果。所以我们效仿。
即使在ES5之前的浏览器上,您也可以模仿足够的Object.create
来执行此操作:
if (!Object.create) {
Object.create = function(proto, props) {
if (typeof props !== "undefined") {
throw new Error("The second argument of Object.create cannot be shimmed.");
}
function ctor() { }
ctor.prototype = proto;
return new ctor;
};
}
如果您调用的构造函数是通过ES2015的class
语法创建的,则上述操作无效,因为您无法以这种方式调用ES2015 class
构造函数。例如:
class Example {
constructor(a, b) {
this.a = a;
this.b = b;
}
}
const e = Object.create(Example.prototype);
Example.apply(e, [1, 2]); // TypeError: Class constructor Example cannot be invoked without 'new' (or similar)
好消息是只会在ES2015 +兼容的JavaScript引擎上发生,并且只有构造函数是通过class
创建的;坏消息是它可能发生。
那么你如何制作makeNew
防弹?
事实证明这很容易,因为ES2015还添加了Reflect.construct
,它完全符合您的要求makeNew
,但它以与class
构造函数兼容的方式执行和function
构造函数。因此,您可以使用功能检测Reflect.construct
并使用它(如果它存在(ES2015 JavaScript引擎,因此构造函数可能已使用class
创建)并且如果它不存在则回退到上面(ES2015之前)引擎,周围不会有任何class
构造函数:
var makeNew; // `var` because we have to avoid any ES2015+ syntax
if (typeof Reflect === "object" && Reflect && typeof Reflect.construct === "function") {
// This is an ES2015-compatible JavaScript engine, use `Reflect.construct`
makeNew = Reflect.construct;
} else {
makeNew = function makeNew(ctor, arguments){
var obj = Object.create(ctor.prototype);
var rv = ctor.apply(obj, arguments);
return rv && typeof rv === "object" ? rv : obj;
};
}
这是纯粹的ES5语法,因此在ES5引擎上运行,但如果它存在则使用ES2015的Reflect.construct
。