将.apply()与'new'运算符一起使用。这可能吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将.apply()与'new'运算符一起使用。这可能吗?相关的知识,希望对你有一定的参考价值。

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

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

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。然而,人们提出了一些非常有趣的解决方案。

我首选的解决方案是this one from Matthew Crumley(我已将其修改为通过arguments属性):

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

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

使用ECMAScript 5,qazxsw poi的东西变得非常干净:

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-based solution这样的特殊构造函数:

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(); 作为arugment 0传递,所以它将是我们的Cls

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 Something() {
    // init stuff
    return this;
}
function createSomething() {
    return Something.apply( new Something(), arguments );
}
var s = createSomething( a, b, c );

是的,它有点难看,但它解决了问题,而且很简单。

另一答案

如果您对基于eval的解决方案感兴趣

function instantiate(ctor) {
    switch (arguments.length) {
        case 1: return new ctor();
        case 2: return new ctor(arguments[1]);
        case 3: return new ctor(arguments[1], arguments[2]);
        case 4: return new ctor(arguments[1], arguments[2], arguments[3]);
        //...
        default: throw new Error('instantiate: too many parameters');
    }
}

function Thing(a, b, c) {
    console.log(a);
    console.log(b);
    console.log(c);
}

var thing = instantiate(Thing, 'abc', 123, {x:5});
另一答案

另请参阅CoffeeScript如何做到这一点。

function createSomething() { var q = []; for(var i = 0; i < arguments.length; i++) q.push("arguments[" + i + "]"); return eval("new Something(" + q.join(",") + ")"); }

变为:

s = new Something([a,b,c]...)
另一答案

这有效!

var s;
s = (function(func, args, ctor) {
  ctor.prototype = func.prototype;
  var child = new ctor, result = func.apply(child, args);
  return Object(result) === result ? result : child;
})(Something, [a, b, c], function(){});
另一答案

这个构造函数方法可以使用和不使用var cls = Array; //eval('Array'); dynamically var data = [2]; new cls(...data); 关键字:

new

它假设支持function Something(foo, bar){ if (!(this instanceof Something)){ var obj = Object.create(Something.prototype); return Something.apply(obj, arguments); } this.foo = foo; this.bar = bar; return this; } ,但如果你支持旧的浏览器,你可以随时填充。 Object.create

这是一个See the support table on MDN here

另一答案

没有ES6或polyfill的解决方案:

JSBin to see it in action with console output

产量 演示{arg1:“X”,arg2:“Y”,arg3:“Z”} ......或“更短”的方式:

var obj = _new(Demo).apply(["X", "Y", "Z"]);


function _new(constr)
{
    function createNamedFunction(name)
    {
        return (new Function("return function " + name + "() { };"))();
    }

    var func = createNamedFunction(constr.name);
    func.prototype = constr.prototype;
    var self = new func();

    return { apply: function(args) {
        constr.apply(self, args);
        return self;
    } };
}

function Demo()
{
    for(var index in arguments)
    {
        this['arg' + (parseInt(index) + 1)] = arguments[index];
    }
}
Demo.prototype.tagged = true;


console.log(obj);
console.log(obj.tagged);

编辑: 我认为这可能是一个很好的解决方案:

var func = new Function("return function " + Demo.name + "() { };")();
func.prototype = Demo.prototype;
var obj = new func();

Demo.apply(obj, ["X", "Y", "Z"]);
另一答案

您不能使用this.forConstructor = function(constr) { return { apply: function(args) { let name = constr.name.replace('-', '_'); let func = (new Function('args', name + '_', " return function " + name + "() { " + name + "_.apply(this, args); }"))(args, constr); func.constructor = constr; func.prototype = constr.prototype; return new func(args); }}; } 运算符调用具有可变数量参数的构造函数。

你可以做的是稍微改变构造函数。代替:

new

改为:

function Something() {
    // deal with the "arguments" array
}
var obj = new Something.apply(null, [0, 0]);  // doesn't work!

或者如果必须使用数组:

function Something(args) {
    // shorter, but will substitute a default if args.x is 0, false, "" etc.
    this.x = args.x || SOME_DEFAULT_VALUE;

    // longer, but will only put in a default if args.x is not supplied
    this.x = (args.x !== undefined) ? args.x : SOME_DEFAULT_VALUE;
}
var obj = new Something({x: 0, y: 0});
另一答案

CoffeeScript中的function Something(args) { var x = args[0]; var y = args[1]; } var obj = new Something([0, 0]);

Matthew Crumley's solutions

要么

construct = (constructor, args) ->
    F = -> constructor.apply this, args
    F.prototype = constructor.prototype
    new F
另一答案
createSomething = (->
    F = (args) -> Something.apply this, args
    F.prototype = Something.prototype
    return -> new Something arguments
)()

如果您的目标浏览器不支持ECMAScript 5 function createSomething() { var args = Array.prototype.concat.apply([null], arguments); return new (Function.prototype.bind.apply(Something, args)); } ,则代码将不起作用。但这不太可能,请参阅Function.prototype.bind

另一答案

修改@Matthew回答。在这里,我可以传递任意数量的参数来照常运行(不是数组)。 'Something'也没有硬编码:

compatibilty table
另一答案

这是一个通用的解决方案,可以使用参数数组调用任何构造函数(除了在被称为函数时表现不同的本机构造函数,如Function.prototype.bindStringNumber等):

Date

通过调用function construct(constructor, args) {

以上是关于将.apply()与'new'运算符一起使用。这可能吗?的主要内容,如果未能解决你的问题,请参考以下文章

将 .apply() 与“new”运算符一起使用。这可能吗?

pandas-批量运算,map

python中,dataframe或series对象可以对列进行运算么(加减乘除)

java Operator ‘/‘ cannot be applied to ‘java.math.BigInteger‘, ‘int‘

python DataFrame列运算

麻烦.apply()带'int'列的方法