0

Possible Duplicate:
How do I make JavaScript Object using a variable String to define the class name?

I want to be able to call the new on things programmatically. So for example, if I have these two classes:

class Bird
   constructor: (@name) ->

class Snake
   constructor: (@name) ->

I can call s = new Snake("Sam"). Now, what I want to do is

bird_class = "Bird"
b = new bird_class()

and be able to construct a bird object from a string. Is this possible in CoffeeScript or Javascript?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Alexis
  • 23,545
  • 19
  • 104
  • 143
  • 1
    If you do use the approach in the answer of the above-linked question, realize that you will need to export your classes, e.g., `window.Bird = Bird`. – icktoofay Jan 20 '13 at 01:31
  • @icktoofay thanks for the heads up. Is there a way to do this without exporting the classes? With the above method, is what is happening this: A script is included. The script add a reference with the `window.Bird = Bird`. Then with coffeescript i'm calling `new window['Bird']`? It works, but it would be interesting if there were another way to do it. – Alexis Jan 20 '13 at 01:36
  • Yes, but without `eval` (which can be dangerous and hinders possible optimizations), you'll have to put them all in an object [like numbers1311407's answer shows](http://stackoverflow.com/a/14420852/200291). – icktoofay Jan 20 '13 at 01:39
  • @icktoofay is there a way to expand the var `bird_class` to get the value so that I could then call eval on it? The issue is that I only have the variable `bird_class` to work with – Alexis Jan 20 '13 at 01:53
  • @icktoofay `eval("new " + bird_class + '()')` but I guess I would have to be really sure that `bird_class` is who they say they are. If I was sure that `bird_class` was a known class type, is this safe? – Alexis Jan 20 '13 at 01:56
  • 1
    @Scoop: Yes, but I'd do it like this: `bird_class = eval bird_class_name; my_bird = new bird_class(arg1, arg2)` or in one statement, `new (eval bird_class_name)(arg1, arg2)`. The arguments can, of course, be omitted, but somewhat more surprisingly, if there are no arguments, you can omit the argument parentheses. (You cannot omit the parentheses around `(eval bird_class_name)`.) – icktoofay Jan 20 '13 at 01:59

2 Answers2

2

If you kept your classes in an object you could always:

var animals = {
  Snake: Snake,
  Bird: Bird
};

new animals["Snake"]();

As per your comment, some pattern like this would let you register classes on the fly. It's raw, but might give you an idea:

var zoo = {
  animals: {},

  add: function (animal) {
    this.animals[animal.name] = animal;
  },

  make: function(name) {
    if (this.animals[name]) {
      return new this.animals[name]();
    }
  }
};

function Snake() {};
zoo.add(Snake);
var snake = zoo.make('Snake');
numbers1311407
  • 33,686
  • 9
  • 90
  • 92
  • I need to be able to do this without knowing before hand a list of the classes – Alexis Jan 20 '13 at 01:35
  • You define them somewhere, right? Part of the definition process could be registering them with `animals`, or whatever. – numbers1311407 Jan 20 '13 at 01:39
  • ahh interesting. I see what you're saying. Is making the `zoo` better then appending each of the classes on to `window`? – Alexis Jan 20 '13 at 01:47
  • In most situations you'll want to avoid adding a lot of variables to window. It's usually better to keep things under wraps, on objects or in modules. For one, you'll have no chance of colliding with other code in the window namespace that way. Plus with a factory type solution like this you'd be better able to keep all the classes under wraps. e.g. you want to see the number of animal classes defined?: `zoo.animals.length` – numbers1311407 Jan 20 '13 at 01:52
  • haha sorry I guess that'd be `Object.keys(zoo.animals).length`... fooled by my own typo (I had animals as an array) – numbers1311407 Jan 20 '13 at 02:00
0

I'm not a Coffeescripter, but in JavaScript you can do this:

 function MyClass() { ... constructor ... };
 function MyOtherClass() { ... other constructor ... };

 var theConstructor = something ? MyClass : MyOtherClass;

 var obj = theConstructor();

You can just grab a reference to a constructor and treat that as a value, juggling it around however you like.

If you want to do it by name, well, that's a little tricky. Short of using eval() (which you can do if you want, and if you can determine that it's not risky for your project), it's not possible to get a reference like that to a symbol in JavaScript. It does work however if it's a reference to a property of an object.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Besides `eval()`, it is also possible to use the `window` (or `this` reference to `window`): `b = new window[bird_class]()`, though it suffers from the same concerns as `eval()` (though is marginally safer). – Brett Zamir Jan 20 '13 at 01:45
  • how could it be done with `eval`? Is there a way to expand the var `bird_class` to the value of the var so that I could call eval on it? – Alexis Jan 20 '13 at 01:49
  • @Scoop: `eval('a')` will get you the value of the variable named `a`, but it is somewhat dangerous given user input since it can also evaluate other expressions. – icktoofay Jan 20 '13 at 01:56
  • @Scoop: It also makes it very difficult to impossible to safely minify your code, since the minifier won't know all the possible things you're passing to `eval`. – icktoofay Jan 20 '13 at 01:57