使用.apply()与'new'运算符。这可能吗? [英] Use of .apply() with 'new' operator. Is this possible?

查看:132
本文介绍了使用.apply()与'new'运算符。这可能吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在JavaScript中,我想创建一个对象实例(通过 new 运算符),但传递任意数量的参数到构造函数。这是可能吗?

In JavaScript, I want to create an object instance (via the new operator), but pass an arbitrary number of arguments to the constructor. Is this possible?

我想做的是这样的东西(但是下面的代码不起作用):

What I want to do is something like this (but the code below does not work):

function Something(){
    // init stuff
}
function createSomething(){
    return new Something.apply(null, arguments);
}
var s = createSomething(a,b,c); // 's' is an instance of Something






答案

从这里的回复中可以清楚地看到,没有内置的方式调用 .apply ) new 运算符。但是,人们提出了一些非常有趣的解决方案。

From the responses here, it became clear that there's no in-built way to call .apply() with the new operator. However, people suggested a number of really interesting solutions to the problem.

我的首选解决方案是来自Matthew Crumley的这篇文章(我修改它通过参数 property):

My preferred solution was this one from Matthew Crumley (I've modified it to pass the arguments property):

var createSomething = (function() {
    function F(args) {
        return Something.apply(this, args);
    }
    F.prototype = Something.prototype;

    return function() {
        return new F(arguments);
    }
})();


推荐答案

使用ECMAScipt5的 Function.prototype.bind 事情变得很干净:

With ECMAScipt5's Function.prototype.bind things get pretty clean:

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

This and the eval-based solution are the only ones that always work, even with special constructors like Date:

var date = newCall(Date, 2012, 1);
console.log(date instanceof Date); // true






编辑

有点解释:
我们需要在一个函数中运行 new 参数个数。 bind 方法允许我们这样做:

A bit of explanation: We need to run new on a function that takes a limited number of arguments. The bind method allows us to do it like so:

var f = Cls.bind(anything, arg1, arg2, ...);
result = new f();

anything 参数无关紧要,因为 new 关键字重置 f 的上下文。然而,由于语法原因它是需要的。现在,对于 bind 调用:我们需要传递一个可变数量的参数,所以这是诀窍:

The anything parameter doesn't matter much, since the new keyword resets f's context. However, it is required for syntactical reasons. Now, for the bind call: We need to pass a variable number of arguments, so this does the trick:

var f = Cls.bind.apply(Cls, [anything, arg1, arg2, ...]);
result = new f();

让我们将它包装在一个函数中。 Cls 作为arugment 0传递,因此它将成为我们的任何

Let's wrap that in a function. Cls is passed as arugment 0, so it's gonna be our anything.

function newCall(Cls /*, arg1, arg2, ... */) {
    var f = Cls.bind.apply(Cls, arguments);
    return new f();
}

其实,临时 f 变量不需要:

Actually, the temporary f variable is not needed at all:

function newCall(Cls /*, arg1, arg2, ... */) {
    return new (Cls.bind.apply(Cls, arguments))();
}

最后,我们应该确保 bind 是我们需要的。 ( Cls.bind 可能已被覆盖)。所以用 Function.prototype.bind 替换它,我们得到上面的最终结果。

Finally, we should make sure that bind is really what we need. (Cls.bind may have been overwritten). So replace it by Function.prototype.bind, and we get the final result as above.

这篇关于使用.apply()与'new'运算符。这可能吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆