简体   繁体   English

JavaScript:一般根据其类型创建对象并调用其构造函数

[英]JavaScript: Creating an object generically from its type and calling its constructor

Say there's a JavaScript object with a childType property, which is an object constructor, and you want to write a generic addChild method. 假设有一个带有childType属性的JavaScript对象,该对象是对象构造函数,并且您想编写一个通用的addChild方法。 It needs to create an instance of this.childType, and invoke its constructor, passing whatever arguments were passed into addChild. 它需要创建this.childType的实例,并调用其构造函数,并将传递给addChild的所有参数传递给它。

To be clear, the point is for each collection to create child objects itself, using the inherited generic addChild method and its known childType, not to validate the type of passed-in child objects. 明确地说,重点是每个集合使用继承的泛型addChild方法及其已知的childType自身创建子对象,而不是验证传入的子对象的类型。

I didn't think this would be hard to do, but I've tried various things and only failed so far. 我不认为这很难做到,但我尝试了各种尝试,但到目前为止都失败了。

UPDATE: 更新:

I've updated the demo plunk with the best solution I know of so far, and included some comments about the issue in the code. 我已经用到目前为止所知的最好的解决方案更新了演示插件 ,并在代码中包含了有关该问题的一些注释。 The failed attempts discussed below are no longer there. 下面讨论的失败尝试不再存在。 This solution requires all potential chold objects to have a 'new Agnostic constructor', so it's still not ideal. 此解决方案要求所有潜在的暂挂对象都具有“新的Agnostic构造函数”,因此仍然不理想。 Further thoughts are most welcome. 欢迎进一步的想法。


I know this line is wrong, for two reasons: 我知道这行是错误的,原因有两个:

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

First is that I think some sort of 'this' needs to be passed to the call function, and I don't see what it'd be there. 首先,我认为需要将某种“ this”传递给call函数,但我不知道它会是什么。

Second is probably a result of the first, the error reported in the browser: 第二个可能是第一个结果,即浏览器中报告的错误:

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

I think this should be doable, but the syntax for creating that child object from this.childType and passed arguments escapes me so far. 我认为这应该可行,但是到目前为止,从this.childType和传递的参数创建该子对象的语法使我无所适从。 Can someone point out the missing brain cell here? 有人可以指出这里缺少的脑细胞吗?

Probably the cleanest approach is to make Dog ' new Agnostic' (see Effective JavaScript Item 33). 也许最干净的方法是使Dog成为“ new不可知论者”(请参阅​​有效的JavaScript项目33)。 Notice how the Dog constructor will now return a new Dog regardless of how you call it. 注意,无论您如何调用, Dog构造函数现在将如何返回新的Dog

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

  this.name = name;
  return this;
};

And used 'apply' instead of call. 并使用“应用”而不是调用。

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

While it does seem like a bit of ceremony for all your constructors, it actually is a pretty good idea anyways. 对于您的所有构造函数来说,这似乎有点仪式,但实际上,这是一个非常不错的主意。 I've found myself making most of my objects new agnostic. 我发现自己做了我的大部分对象的new不可知。

And new and arguments agnostic oneliner would look like this :) new arguments无关的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;
}

You can try Object.create(); 您可以尝试Object.create(); http://plnkr.co/edit/KjqQfev1k42bH0jAUrrA?p=preview http://plnkr.co/edit/KjqQfev1k42bH0jAUrrA?p=preview

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

This changes your structure but it save you from having to write resilient constructor methods. 这将改变您的结构,但使您不必编写弹性的构造函数方法。 Also note that you pass in the prototype as the childItemType 还要注意,您将原型作为childItemType

Collection.call(this, Dog.prototype);

And instead of passing in arguments to the child object's constructor you pass in a properties object: 而不是将参数传递给子对象的构造函数,而是传递属性对象:

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

The property object is the trade off here. 属性对象是这里的权衡。 More about Object.create and the 'second argument' http://dailyjs.com/2012/06/04/js101-object-create/ 有关Object.create和“第二个参数”的更多信息http://dailyjs.com/2012/06/04/js101-object-create/

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

No, this indeed tries to use Function.prototype.call as a constructor (as the exception message tells you). 不,这确实试图将Function.prototype.call用作构造函数(正如异常消息告诉您的那样)。 However, there is no reason to use call at all: 但是,根本没有理由使用call

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

First is that I think some sort of 'this' needs to be passed to the call function, and I don't see what it'd be there. 首先,我认为需要将某种“ this”传递给call函数,但我不知道它会是什么。

Yes, call expects something to be used as the thisArg for the function. 是的, call希望将某些东西用作该函数的thisArg Yet, you seem to want apply as you have an argumens array args . 但是,您似乎想要apply因为您有一个argumens数组 args

So on what does the constructor get applied on? 那么,构造函数应用于什么呢? It's the new instance - just as like the new operator does it. 这是新实例-就像new运算符一样。 So to mimick it, we would use 所以要模仿它,我们将使用

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

For solutions that work without Object.create and also check the return type of the constructor, see Use of .apply() with 'new' operator. 有关在没有Object.create情况下工作并且还检查构造函数的返回类型的解决方案,请参见将.apply()与'new'运算符一起使用。 Is this possible? 这可能吗? .

Correct answer is 正确答案是

var child = new this.childItemType(args);

When you want to create an object of a class/function in javascript then you can directly do new ClassName(arguments) , call and apply are normally used to call any method of a class/function with a different scope 当您想在javascript中创建类/函数的对象时,可以直接执行new ClassName(arguments) ,call和apply通常用于调用具有不同范围的类/函数的任何方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM