【问题标题】:How to pass arbitrary arguments to a javascript constructor [duplicate]如何将任意参数传递给javascript构造函数[重复]
【发布时间】:2014-03-26 16:35:34
【问题描述】:

我试图弄清楚如何使用任意数量的参数调用构造函数(从其他函数调用传递参数)。

我在 javascript 中有一组基础对象和派生对象。基础对象上的方法之一称为makeNew(),它的工作是创建一个与调用它的任何对象具有相同类型的新对象,并处理与普通构造函数在新对象上处理的所有相同参数。这个makeNew() 方法的要点是,还有其他方法想要创建一个与当前对象相同类型的新对象,但他们不知道那是什么类型,因为它可能是继承自基类。请注意,我不想要当前对象的完整克隆,而是想要一个相同类型的新对象,但使用构造函数的不同初始参数进行初始化。

makeNew() 的简单版本是这样的:

set.prototype.makeNew = function() {
    return new this.constructor();
}

这适用于创建与当前对象类型相同的空对象,因为如果它是继承的对象类型,那么this.constructor 将是继承的构造函数,它将生成正确的对象类型。

但是,当我想将任意参数传递给构造函数以便普通的构造函数参数可以传递给makeNew() 时,我不知道该怎么做。我试过这个:

set.prototype.makeNew = function() {
    return new this.constructor.apply(this, arguments);
}

但是,该代码在 Chrome 中给了我这个错误:

Error: function apply() { [native code] } is not a constructor

知道如何将任意参数传递给构造函数吗?

【问题讨论】:

  • 我不明白第一个版本有什么问题。 return new this.constructor(arguments); 应该可以工作。
  • @TastySpaceApple - 将单个参数传递给构造函数,该参数是单个类似数组的对象,这不是构造函数期望传递其多个参数的方式。
  • @cookiemonster - 我将不得不在您引用的帖子中尝试该技术。这是一个有趣的 hack。
  • 如果你的意思是带有F 临时构造函数的那些,那基本上就是Object.create()(部分)垫片。
  • @cookiemonster - 我不喜欢您发布的链接的一件事是,它给您留下了一个名为“F”的对象,乍一看实际上不会导致编程问题,但是在通常创建的对象的基础上创建了额外的继承级别。它仍然是一个 hack,而不是一个完全干净的解决方案。仅供参考,我在这里对其进行了测试:jsfiddle.net/jfriend00/rXyXJ

标签: javascript constructor


【解决方案1】:

回答我自己的问题。各种讨论让我找到了几个可能的解决方案。

如果您控制所有涉及的对象,则可以将所有活动代码从构造函数中取出,并将其放入 .init() 方法中并遵循该设计模式。然后,您可以这样做:

function set() {
    this.init.apply(arguments);
}

set.prototype.init = function() {
    // process all constructor arguments here
}

set.prototype.makeNew = function() {
    // create object of the same type as what we are
    // even if it's a derived object type
    var newSet = new this.constructor()

    // apply any constructor arguments
    newSet.init.apply(newSet, arguments);

    return newSet;
}

所有派生类也必须遵循相同的.init() 设计模式,包括调用基类,如果它们自己添加或处理任何构造函数参数,而不仅仅是让基类处理构造函数参数。


实际上,如果一个空的构造函数没有做太多事情并且没有任何问题被调用两次,你甚至可以这样做:

function set() {
    // do whatever object initialization from arguments
    // that the object wants to do
}

set.prototype.makeNew = function() {
    // create object of the same type as what we are
    // even if it's a derived object type
    // first time called without any constructor arguments
    var newSet = new this.constructor();

    if (arguments.length) {
        // apply any constructor arguments
        // by simply calling the constructor again without "new"
        // and this time passing it the object we already made
        // and the arguments we have
        this.constructor.apply(newSet, arguments);
    }

    return newSet;
}

【讨论】:

  • 将特定于在没有参数的情况下调用没有不良副作用的构造函数,但在这种情况下它应该可以正常工作。
  • @cookiemonster - 是的,你是对的。我还发现了一种更简单的方法,如果构造函数不介意先在没有参数的情况下被调用两次,然后在使用参数的情况下调用。
【解决方案2】:

如果您可以更改构造函数,这种方式对我来说似乎是合法的,它依赖于 javascript 中构造函数、对象和函数之间的微妙差异。

function set(arg1, arg2, arg3){
    // entered the constructor, `this` = new object (like using Object.create)
    if(arg1 instanceof Array){ //if arg1 is an array
        this.constructor.apply(this, arg1); //use apply to spread it
        // apply now refers to the constructor as a function
        // notice the reference to `this.constructor`. 
        // This is because `this` no longer refers to the function, 
        // but to the newly created object.
    }else{
        // the actual function.
        console.log(arg2);
    }
}
new set([1,2,3])

现在由您来决定如何在原型继承中使其工作......在我看来,您必须将此代码复制到每个类中。或者继承它:

function set(arg1, arg2, arg3){
    if(set.prototype.initCheck.call(this, arg1)){ //this will have to be copied to every inherited class...
        ///the actual constructor
    }
}
set.prototype.initCheck = function(arg1){
    if(arg1 instanceof Array){ //if arg1 is an array
        this.constructor.apply(this, arg1); //use apply to spread it
        return false;
    }
    return true;
}
new set([1,2,3])

【讨论】:

  • 实际上,您提醒我一个原因,您可能会选择在构造函数本身中不放置初始化代码,而是在构造函数调用的init() 方法中放置。这样您就不必使用参数调用构造函数。您可以调用空白构造函数,获取一个新的空对象,然后在新对象上调用this.init.apply(this, arguments) 以获得与使用参数调用构造函数完全相同的结果。这是一个约定,但遵循该约定可以解决这个问题(如果您控制所有涉及的对象)。
  • 嗯,这是个好主意。
【解决方案3】:

我看到的唯一方法是您必须在中间创建一个方法来将数组传播到参数中。

比如这个方法,先创建新对象,然后使用apply展开数组。

Function.prototype.construct = function(argArray) {
    var constr = this;
    var inst = Object.create(constr.prototype); //creates a new empty object
    constr.apply(inst, argArray); // triggers the constructor (as a function)
    return inst;
};

那么你可以使用

return set.construct(array)

http://www.2ality.com/2011/08/spreading.html阅读更多(更多)

注意: Object.create 现在可能适用于旧浏览器。使用来自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create的polyfill

【讨论】:

  • 这就是这样做的方法。还有其他一些骇人听闻的方法,但这是最干净的。
  • 谢谢。我以前见过这样的事情。我希望不要依赖Object.create(),所以旧的 IE 8 仍然可以运行我的代码。
  • 假的?那还能用吗?
  • 您可以从developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… 添加polyfill,这将为旧浏览器提供支持..
  • @Xotic750:当然,这种技术不依赖于任何无法填充的行为。
猜你喜欢
  • 2023-04-03
  • 2011-12-22
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 2012-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多