3

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!

8
  • 2
    What's the point of the factory function if you have to pass it both a reference to the required "class" and the argument list? Commented Jun 27, 2013 at 12:00
  • I have a really long list of classes that I want to initialize. I don't want to manually write code for initializing them instead I just pass and array which contains the reference of the class and the parameters that must be supplied to constructor. Commented Jun 27, 2013 at 12:06
  • Yes, but given that both of your current possible solutions are basically one-line functions (not counting the return statement) you may as well just have that one line in whatever loop processes the array. (Although yes, I know you're not happy with those solutions.) Commented Jun 27, 2013 at 12:13
  • Actually that is what I am doing. The factory method is called for each item in the list. Commented Jun 27, 2013 at 12:16
  • I understand that. I'm saying the factory function seems redundant when its (very short) body could go directly in your loop. I suppose you don't want to change your contructors so that they can accept an array and you can just use Solution 1? Commented Jun 27, 2013 at 12:32

5 Answers 5

4

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.

Sign up to request clarification or add additional context in comments.

4 Comments

var factory = Function.prototype.apply.bind(Function.prototype.new); :-)
btw don't forget voting to close as a duplicate if you've already found that question…
@Bergi You could also do var factory = Function.apply.bind(Function.new); because Function itself is an instance of Function.prototype. What a conundrum.
Oops of course. I had thought you were relying on non-standard generics at first :-/
3

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.

3 Comments

i needed it for a more basic situation, so I did var obj = Object.create(MyClass.prototype); return MyClass.apply(obj, Array.prototype.slice.call(arguments)) || obj;
@VictorFerreira: Not sure where the difference to my code is? Notice that the slice is unncessary
the difference is that I am not receiving any arguments in the function
1

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;

5 Comments

What is Eatable? In my question Eatable is just a variable and not a class.
But you could use this idea with your Food(), Snacks(), etc. classes: define each one as an empty constructor with an associated init() function...
@Tushar Mathur : I just used Eatable as a class definition just to demonstrate how a separate init method can be invoked with apply and pass parameters as arguments.
@nnnnnn Why would I want to modify the existing classes with an empty constructor? This is not a solution, its an alternative which will cause changes in all the classes.
Why would you want to? Because this is one way to implement your concept of a factory function that can pass through arguments. I was just trying to answer your first comment by explaining how this concept related to your classes, not saying this is necessarily the best possible option for you.
0

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.

6 Comments

I want to create a new object of Food, Drinks etc. depending upon what has been passed as an argument in the eatable variable. Eatable is not a class in it self.
"Should be"? No, that's just one way to do it - even the article you linked to mentions other ways that don't involve sub-classing. In JS it is reasonably common to go with duck typing...
Why would you want to force polymorphism? There is absolutely no relationship between the classes.
Wouldn't this work with eatable.apply(new eatable(), argumentList); - assuming all of the contructors that eatable might refer to will "work" with no arguments.
@nnnnnn Are you asking me this?
|
0

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
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.