Reputation: 11002
I have multiple eatable classes in javascript eg: food, drinks, snacks. Each of this class requires a different set of parameters. I have another factory class which creates an instance of the eatable item that is sent to it.
I am not able to figure out how can we dynamically select the eatable item and pass the arguments (which are in an array form) using this factory?
I have come up with two solutions - Solution 1:
var factory = function(eatable, argumentList){
var obj = new eatable(argumentList);
return obj
};
This is a problem because argumentList is an array.
Solution 2
var factory = function(eatable, argumentList){
var obj = eatable.apply({}, argumentList);
return obj
};
this does not really create an object of the eatable type.
The effect that I really want Say I am able to convert the argumentList into a js argument type object then -
var obj = new eatable(argumentList.toArguments());
obj instanceOf eatable; // should return true
Please help!
Upvotes: 3
Views: 683
Reputation: 11002
One more way to achieve this is as follows -
var _bind = Function.prototype.bind;
var factory = function(_constructor, _argumentList){
var obj = _bind.apply(_constructor, [null].concat(_argumentList));
return obj
};
Upvotes: 0
Reputation: 74234
Ah, yes. I've encountered this problem before - you can't use new
and apply
together in JavaScript. A similar question has been asked before: Use of .apply() with 'new' operator. Is this possible?
The problem is quite apparent - new
is a keyword, not a function; and apply
can only be used on a function. If new
was a function instead of a keyword
then we could use it in conjuction with apply
.
To understand how to do so let's create a function called new
which does exactly what the keyword new
does:
Function.prototype.new = (function () {
function Factory(constructor, args) {
return constructor.apply(this, args);
}
return function() {
Factory.prototype = this.prototype;
return new Factory(this, arguments);
};
}());
Now instead of calling a constructor as follows:
var object = new constructor(arg1, ...);
You can call a constructor as follows:
var object = constructor.new(arg1, ...);
What's the advantage of doing so you ask? Well it's simple really. Because new
is now a function instead of a keyword you can use it in conjunction with apply
as follows:
var object = Function.new.apply(constructor, [arg1, ...]);
Hence your eatable factory function now becomes:
var factory = function(eatable, argumentList) {
var obj = Function.new.apply(eatable, argumentList);
return obj;
};
Edit: If all your factory function does is take an eatable
constructor and an argumentList
and return new.apply(eatable, argumentList)
then as Bergi pointed out in his comment you could define factory
as follows instead:
var factory = Function.apply.bind(Function.new);
Hope this helped.
Upvotes: 4
Reputation: 665276
You can use Object.create
to set up the prototype chain correctly:
function factory(eatable, argumentList){
var obj = Object.create(eatable.prototyope);
return eatable.apply(obj, argumentList) || obj;
}
This is basically what the new
operator does.
Upvotes: 3
Reputation: 25165
You can define a function init
to initialize the object .
function Eatable(){
}
Eatable.prototype.init = function(/** arg1, arg2, arg3 **/){
// initialize object
}
In factory function
var eatable = new Eatable();
eatable.init.apply(eatable, /** pass arguments array here **/);
return eatable;
Upvotes: 1
Reputation: 6871
You have to provide context to apply, The context is the object you are trying to apply the arguments to. The context you are currently passing {}
is of type Object
var factory = function(eatable, argumentList){
var obj = eatable.apply(new Eatable(), argumentList);
return obj
};
I can not use factories with out polymorphism so if you didn't create those eatables in way they extend an Eatalbe
object you will not be able to do it.
Upvotes: 0