在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

这个问题的答案

从这里的响应可以清楚地看出,没有内置的方法可以使用new操作符调用.apply()。然而,人们对这个问题提出了许多非常有趣的解决方案。

我更喜欢的解决方案是来自Matthew Crumley的这个(我修改了它来传递arguments属性):

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

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

当前回答

是的,我们可以,javascript在本质上更多的是原型继承。

function Actor(name, age){
  this.name = name;
  this.age = age;
}

Actor.prototype.name = "unknown";
Actor.prototype.age = "unknown";

Actor.prototype.getName = function() {
    return this.name;
};

Actor.prototype.getAge = function() {
    return this.age;
};

当我们创建一个带有"new"的对象时,我们创建的对象继承getAge(),但如果我们使用apply(…)或call(…)来调用Actor,那么我们为"this"传递了一个对象,但我们传递的对象不会继承自Actor.prototype

除非,我们直接通过apply或调用Actor。原型但是....“this”指向“Actor”。this.name将写入:actor。prototype.name。从而影响所有用Actor创建的其他对象…因为我们覆盖的是原型而不是实例

var rajini = new Actor('Rajinikanth', 31);
console.log(rajini);
console.log(rajini.getName());
console.log(rajini.getAge());

var kamal = new Actor('kamal', 18);
console.log(kamal);
console.log(kamal.getName());
console.log(kamal.getAge());

让我们试试apply

var vijay = Actor.apply(null, ["pandaram", 33]);
if (vijay === undefined) {
    console.log("Actor(....) didn't return anything 
           since we didn't call it with new");
}

var ajith = {};
Actor.apply(ajith, ['ajith', 25]);
console.log(ajith); //Object {name: "ajith", age: 25}
try {
    ajith.getName();
} catch (E) {
    console.log("Error since we didn't inherit ajith.prototype");
}
console.log(Actor.prototype.age); //Unknown
console.log(Actor.prototype.name); //Unknown

通过传递Actor。prototype to Actor.call()作为第一个参数,当Actor()函数运行时,它执行this.name=name,因为“this”将指向Actor。原型,this.name =名称;意味着Actor.prototype.name =名称;

var simbhu = Actor.apply(Actor.prototype, ['simbhu', 28]);
if (simbhu === undefined) {
    console.log("Still undefined since the function didn't return anything.");
}
console.log(Actor.prototype.age); //simbhu
console.log(Actor.prototype.name); //28

var copy = Actor.prototype;
var dhanush = Actor.apply(copy, ["dhanush", 11]);
console.log(dhanush);
console.log("But now we've corrupted Parent.prototype in order to inherit");
console.log(Actor.prototype.age); //11
console.log(Actor.prototype.name); //dhanush

回到最初的问题,如何使用新的操作符与应用,这里是我的....

Function.prototype.new = function(){
    var constructor = this;
    function fn() {return constructor.apply(this, args)}
    var args = Array.prototype.slice.call(arguments);
    fn.prototype = this.prototype;
    return new fn
};

var thalaivar = Actor.new.apply(Parent, ["Thalaivar", 30]);
console.log(thalaivar);

其他回答

这个答案有点晚了,但我想任何看到这个的人都可以使用它。有一种方法可以使用apply返回一个新对象。尽管它需要对对象声明做一点更改。

function testNew() {
    if (!( this instanceof arguments.callee ))
        return arguments.callee.apply( new arguments.callee(), arguments );
    this.arg = Array.prototype.slice.call( arguments );
    return this;
}

testNew.prototype.addThem = function() {
    var newVal = 0,
        i = 0;
    for ( ; i < this.arg.length; i++ ) {
        newVal += this.arg[i];
    }
    return newVal;
}

testNew( 4, 8 ) === { arg : [ 4, 8 ] };
testNew( 1, 2, 3, 4, 5 ).addThem() === 15;

为了让第一个if语句在testNew中工作,你必须返回这个;在函数的底部。以代码为例:

function Something() {
    // init stuff
    return this;
}
function createSomething() {
    return Something.apply( new Something(), arguments );
}
var s = createSomething( a, b, c );

更新:我改变了我的第一个例子的总和参数的数量,而不是只有两个。

你为什么要把事情弄得这么复杂。在new之后使用匿名函数,该函数返回带有应用数组的构造函数。

function myConstructor(a,b,c){
    this.a = a;
    this.b = b;
    this.c = c;
}

var newObject = new myConstructor(1,2,3);   // {a: 1, b: 2, c: 3}

var myArguments = [1,2,3];
var anotherObject = new function(){
    return myConstructor.apply(this,myArguments);
  }; // {a: 1, b: 2, c: 3}
function createSomething() {
    var args = Array.prototype.concat.apply([null], arguments);
    return new (Function.prototype.bind.apply(Something, args));
}

如果您的目标浏览器不支持ECMAScript 5。绑定,代码将无法工作。这是不太可能的,但见兼容性表。

这是一个通用的解决方案,可以调用任何构造函数(除了本机构造函数,当作为函数调用时行为不同,如String, Number, Date等)与参数数组:

function construct(constructor, args) {
    function F() {
        return constructor.apply(this, args);
    }
    F.prototype = constructor.prototype;
    return new F();
}

通过调用construct(Class,[1,2,3])创建的对象将与用new Class(1,2,3)创建的对象相同。

您还可以创建一个更具体的版本,这样就不必每次都传递构造函数。这也稍微更有效,因为它不需要每次调用时都创建内部函数的新实例。

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

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

这样创建和调用外部匿名函数的原因是为了防止函数F污染全局命名空间。它有时被称为模块模式。

(更新)

对于那些想在TypeScript中使用它的人,因为如果F返回任何东西,TS会给出一个错误:

function construct(constructor, args) {
    function F() : void {
        constructor.apply(this, args);
    }
    F.prototype = constructor.prototype;
    return new F();
}

你可以把init的东西移到Something的原型的一个单独的方法中:

function Something() {
    // Do nothing
}

Something.prototype.init = function() {
    // Do init stuff
};

function createSomething() {
    var s = new Something();
    s.init.apply(s, arguments);
    return s;
}

var s = createSomething(a,b,c); // 's' is an instance of Something