简体   繁体   中英

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. It needs to create an instance of this.childType, and invoke its constructor, passing whatever arguments were passed into 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.

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. 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.

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. Can someone point out the missing brain cell here?

Probably the cleanest approach is to make Dog ' new Agnostic' (see Effective JavaScript Item 33). Notice how the Dog constructor will now return a new Dog regardless of how you call it.

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.

And new and arguments agnostic oneliner would look like this :)

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(); 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

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/

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). However, there is no reason to use call at all:

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.

Yes, call expects something to be used as the thisArg for the function. Yet, you seem to want apply as you have an argumens array args .

So on what does the constructor get applied on? It's the new instance - just as like the new operator does it. 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. 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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