如何调用具有动态参数数量的构造函数

How to call a constructor with dynamic amount of arguments?

本文关键字:参数 动态 数数 构造函数 何调用 调用      更新时间:2023-09-26

我有一个构造函数,但我不知道它需要多少个参数,例如:

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]);

如何实现这个函数?

注意:请参阅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兼容性说明

如果你调用的构造函数是通过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如果它存在。