【问题标题】:JavaScript: Creating an object generically from its type and calling its constructorJavaScript:从它的类型创建一个对象并调用它的构造函数
【发布时间】:2013-03-15 18:57:43
【问题描述】:

假设有一个带有 childType 属性的 JavaScript 对象,它是一个对象构造函数,并且您想编写一个通用的 addChild 方法。它需要创建 this.childType 的实例,并调用它的构造函数,将传入的任何参数传递给 addChild。

需要明确的是,重点是每个集合自己创建子对象,使用继承的泛型 addChild 方法及其已知的 childType,而不是验证传入的子对象的类型。

我不认为这很难做到,但是我尝试了各种方法,但到目前为止都失败了。

更新:

我已经用迄今为止我所知道的最佳解决方案更新了demo plunk,并在代码中包含了一些关于该问题的 cmets。下面讨论的失败尝试不再存在。此解决方案要求所有潜在的 chold 对象都具有“新的不可知论构造函数”,因此仍然不理想。欢迎进一步的想法。


我知道这条线是错误的,有两个原因:

var child = new this.childItemType.call(args);

首先,我认为需要将某种“this”传递给调用函数,但我不知道它会在那里。

第二个可能是第一个的结果,浏览器报错:

Uncaught TypeError: function call() { [native code] } is not a constructor

我认为这应该是可行的,但是到目前为止,从 this.childType 和传递的参数创建该子对象的语法让我无法理解。有人能指出这里丢失的脑细胞吗?

【问题讨论】:

  • var child = new this.childType(args);?
  • var child = new this[this.childItemType](args); ?
  • 我已经用 Clint 的“新的不可知论构造函数”解决方案更新了 plunk,据我所知,这是最实用的,即使它有缺点。我还添加了一些进一步澄清的 cmets。如果他在这里发布,我会将其标记为答案。
  • var child; childItemType.call(child, args) 也是有效的。

标签: javascript object constructor


【解决方案1】:

可能最简洁的方法是让 Dog 'new Agnostic'(参见 Effective JavaScript Item 33)。请注意Dog 构造函数现在将如何返回一个新的Dog,无论您如何调用它。

var Dog = function (name)
{
  if(!(this instanceof Dog)){
    return new Dog(name);
  }

  this.name = name;
  return this;
};

并使用“应用”而不是调用。

var child = this.childItemType.apply(null,arguments);

虽然对于所有构造函数来说这似乎有点仪式感,但无论如何这实际上是一个不错的主意。我发现自己让我的大部分对象 new 不可知论。

【讨论】:

  • 我以前没有见过这种技术,在这里效果很好,谢谢。我仍然希望我们都只是有一个沉闷的时刻,并且有一些方法可以在没有特殊构造函数的情况下做到这一点,但还没有人在这里提出它,所以这是我所知道的最好的方式。再次感谢,真的很有帮助,在我们说话的时候,Effective JavaScript 正在向我走来......
  • 我很高兴这个解决方案对你有用。我真的很喜欢 Effective JS,它写得很好而且内容丰富。我在学习 JS 方面还有很长的路要走。你的问题帮助我尝试“应用”我最近的一些研究。 (好悲伤……双关语……该睡觉了)。
【解决方案2】:

newarguments 不可知的 oneliner 看起来像这样 :)

var Dog = function( name ) {
    if ( !(this instanceof Dog) ) { return new (Dog.bind.apply( Dog, [null].concat( Array.prototype.slice.apply( arguments ) ) )); }

    this.name = name;

    return this;
}

【讨论】:

    【解决方案3】:

    你可以试试Object.create();http://plnkr.co/edit/KjqQfev1k42bH0jAUrrA?p=preview

    var child = Object.create(this.childItemType,props);
    

    这会改变你的结构,但它使你不必编写弹性构造方法。另请注意,您将原型传递为childItemType

    Collection.call(this, Dog.prototype);
    

    并且不是将参数传递给子对象的构造函数,而是传递一个属性对象:

    smiths.addChild({name:{value:'Rover'}});
    

    属性对象是这里的权衡。更多关于 Object.create 和“第二个参数”http://dailyjs.com/2012/06/04/js101-object-create/

    【讨论】:

    • 我认为直到 IE9,你是对的,部分语法只是奇怪。对我来说仍然很有趣的是,我正在尝试做的事情变得有些棘手。
    【解决方案4】:

    var child = new this.childItemType.call(args);

    不,这确实尝试使用Function.prototype.call 作为构造函数(正如异常消息告诉您的那样)。但是,完全没有理由使用call

    var child = new this.childItemType(arg, arg2, …);
    

    首先,我认为需要将某种“this”传递给调用函数,但我不知道它会在那里。

    是的,call 期望将某些东西用作函数的 thisArg。然而,你似乎想要apply,因为你有一个argumens array args

    那么构造函数应用于什么?这是新的实例——就像new operator 一样。所以为了模仿它,我们会使用

    var child = Object.create(this.childItemType.prototype);
    this.childItemType.apply(child, args);
    

    对于没有Object.create 的解决方案以及检查构造函数的返回类型,请参阅Use of .apply() with 'new' operator. Is this possible?

    【讨论】:

    • 我不太喜欢 Object.create 语法或它的不兼容性,但你指出的更简洁的先前问题有一些非常有价值的解决方案,在某些意义上比我刚刚接受的更好在我看到这个之前。确实两者都是有效的方法。非常感谢。
    【解决方案5】:

    正确答案是

    var child = new this.childItemType(args);
    

    当你想在javascript中创建一个类/函数的对象时,你可以直接做new ClassName(arguments),call和apply通常用于调用不同范围的类/函数的任何方法

    【讨论】:

    • 谢谢,但看起来这只狗的名字是 ['Rover'],而不是 'Rover'。添加了一些其他不可行的想法。
    • 抱歉,我实际上对此表示反对,因为它不起作用,以后来这里的任何人都应该清楚地知道这一点。
    猜你喜欢
    • 2018-07-06
    • 2012-12-28
    • 2015-10-08
    • 1970-01-01
    • 2011-05-13
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    • 1970-01-01
    相关资源
    最近更新 更多