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

查看:32
本文介绍了将 .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

<小时>

答案

从这里的回复来看,很明显没有使用 new 运算符调用 .apply() 的内置方法.但是,人们针对该问题提出了许多非常有趣的解决方案.

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

我的首选解决方案是 来自 Matthew 的这个Crumley(我已经修改它以传递 arguments 属性):

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);
    }
})();

推荐答案

使用 ECMAScript5 的 Function.prototype.bind 事情变得非常干净:

With ECMAScript5'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的解决方案是唯一始终有效的解决方案,即使使用像这样的特殊构造函数日期:

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 作为参数 0 传递,所以它将是我们的 anything.

Let's wrap that in a function. Cls is passed as argument 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天全站免登陆