【问题标题】:Dynamically control arguments while creating objects in javascript在 javascript 中创建对象时动态控制参数
【发布时间】:2013-06-27 11:56:22
【问题描述】:

我在 javascript 中有多个 可食用 类,例如:食物、饮料、零食。 每个此类都需要一组不同的参数。我有另一个工厂类,它创建一个发送给它的可食用项目的实例。

我无法弄清楚我们如何使用这个工厂动态选择可食用项目并传递参数(以数组形式)?

我想出了两个解决方案 - 解决方案 1:

var factory = function(eatable, argumentList){
  var obj = new eatable(argumentList);
  return obj
};

这是个问题,因为argumentList是一个数组。

解决方案 2

var factory = function(eatable, argumentList){
  var obj =  eatable.apply({}, argumentList);
  return obj
};

这并没有真正创建可食用类型的对象。

我真正想要的效果 假设我能够将 argumentList 转换为 js 参数类型对象 -

var obj = new eatable(argumentList.toArguments());
obj instanceOf eatable; // should return true

请帮忙!

【问题讨论】:

  • 如果您必须将对所需“类”的引用和参数列表都传递给工厂函数,那么它的意义何在?
  • 我有一长串要初始化的类。我不想手动编写代码来初始化它们,而是传递一个包含类引用和必须提供给构造函数的参数的数组。
  • 是的,但是鉴于您当前可能的两种解决方案基本上都是单行函数(不包括 return 语句),您也可以在处理数组的任何循环中只使用这一行。 (虽然是的,但我知道您对这些解决方案不满意。)
  • 其实这就是我正在做的。为列表中的每个项目调用工厂方法。
  • 我明白这一点。我是说当它的(非常短的)主体可以直接进入你的循环时,工厂函数似乎是多余的。我想您不想更改您的构造函数,以便他们可以接受一个数组,而您可以使用解决方案 1?

标签: javascript oop factory


【解决方案1】:

啊,是的。我以前遇到过这个问题 - 你在 JavaScript 中 can't use new and apply together。之前有人问过类似的问题:Use of .apply() with 'new' operator. Is this possible?

问题很明显——new 是关键字,而不是函数;而apply 只能用于函数。如果new 是一个函数而不是keyword,那么我们可以将它与apply 结合使用。

要了解如何做到这一点,让我们创建一个名为 new 的函数,它的作用与关键字 new 的作用完全相同:

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

    return function() {
        Factory.prototype = this.prototype;
        return new Factory(this, arguments);
    };
}());

现在不要像下面这样调用构造函数:

var object = new constructor(arg1, ...);

您可以如下调用构造函数:

var object = constructor.new(arg1, ...);

你问这样做有什么好处?嗯,真的很简单。因为new 现在是一个函数而不是关键字,所以您可以将它与apply 结合使用,如下所示:

var object = Function.new.apply(constructor, [arg1, ...]);

因此你的可食用工厂函数现在变为:

var factory = function(eatable, argumentList) {
    var obj = Function.new.apply(eatable, argumentList);
    return obj;
};

编辑:如果您的所有工厂函数都采用eatable 构造函数和argumentList 并返回new.apply(eatable, argumentList),那么正如Bergi 在他的评论中指出的那样,您可以将factory 定义为改为:

var factory = Function.apply.bind(Function.new);

希望这会有所帮助。

【讨论】:

  • var factory = Function.prototype.apply.bind(Function.prototype.new); :-)
  • 顺便说一句,如果您已经发现该问题,请不要忘记投票关闭作为重复项......
  • @Bergi 你也可以使用var factory = Function.apply.bind(Function.new);,因为Function 本身就是Function.prototype 的一个实例。真是个难题。
  • 当然可以。一开始我还以为你依赖non-standard generics :-/
  • 这是完美的解决方案!
【解决方案2】:

您可以使用Object.create正确设置原型链:

function factory(eatable, argumentList){
    var obj = Object.create(eatable.prototyope);
    return eatable.apply(obj, argumentList) || obj;
}

这基本上就是new operator 所做的。

【讨论】:

  • 我需要它来解决更基本的情况,所以我做了var obj = Object.create(MyClass.prototype); return MyClass.apply(obj, Array.prototype.slice.call(arguments)) || obj;
  • @VictorFerreira:不确定我的代码的不同之处在哪里?请注意,slice 是不必要的
  • 不同之处在于我在函数中没有收到任何参数
【解决方案3】:

你可以定义一个函数init来初始化对象。

function Eatable(){

}

Eatable.prototype.init = function(/** arg1, arg2, arg3 **/){
    //  initialize object
}

在工厂函数中

var eatable = new Eatable();
eatable.init.apply(eatable, /** pass arguments array here **/);
return eatable;

【讨论】:

  • 什么是可食用的?在我的问题中,Eatable 只是一个变量而不是一个类。
  • 但是你可以在你的Food()Snacks() 等类中使用这个想法:将每个类定义为一个带有关联init() 函数的空构造函数...
  • @Tushar Mathur :我只是将Eatable 用作类定义,只是为了演示如何使用apply 调用单独的init 方法并将参数作为参数传递。
  • @nnnnnn 为什么要使用空的构造函数修改现有类?这不是一个解决方案,它会导致所有类发生变化。
  • 您为什么要这样做?因为这是实现可以传递参数的工厂函数概念的一种方法。我只是想通过解释这个概念与你的课程的关系来回答你的第一条评论,而不是说这对你来说一定是最好的选择。
【解决方案4】:

您必须提供要应用的上下文,上下文是您尝试将参数应用到的对象。您当前传递的上下文 {} 是 Object 类型

var factory = function(eatable, argumentList){
  var obj =  eatable.apply(new Eatable(), argumentList);
  return obj
};

我不能使用没有多态性的工厂,所以如果你没有以它们扩展 Eatalbe 对象的方式创建那些 eatables,你将无法做到。

【讨论】:

  • 我想根据可食用变量中作为参数传递的内容创建一个新的食物、饮料等对象。 Eatable 本身并不是一个类。
  • “应该”?不,这只是一种方法-即使您链接到的文章也提到了其他不涉及子分类的方法。在 JS 中,使用鸭子打字是相当普遍的......
  • 为什么要强制多态性?类之间绝对没有关系。
  • 这不适用于eatable.apply(new eatable(), argumentList); - 假设eatable 可能引用的所有构造函数都将在没有参数的情况下“工作”。
  • @nnnnnn 你在问我这个吗?
【解决方案5】:

实现这一点的另一种方法如下 -

var _bind = Function.prototype.bind;
var factory = function(_constructor, _argumentList){
  var obj = _bind.apply(_constructor, [null].concat(_argumentList));
  return obj
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-11
    • 2021-07-30
    • 2023-03-14
    相关资源
    最近更新 更多