78

Here's what I'm trying to do -- this is pseudo code and doesn't work. Does anyone know how to accomplish this for real:

// Define the class
MyClass = Class.extend({});

// Store the class name in a string
var classNameString = 'MyClass';

// Instantiate the object using the class name string
var myObject = new classNameString();
mikemaccana
  • 81,787
  • 73
  • 317
  • 396
Kirk Ouimet
  • 23,368
  • 41
  • 108
  • 164

8 Answers8

69

Would it work if you did something like this:

var myObject = window[classNameString];

..?

peirix
  • 32,055
  • 22
  • 90
  • 125
  • That worked. peirix you are awesome. You think it's cross-browser compatible? – Kirk Ouimet Sep 02 '09 at 06:39
  • 1
    Think you meant `window[classNameString]` (no quotes). Breaks as soon as `MyClass` is moved to a lower (i.e. `function`) scope. @kirk: yes this is cross-browser. – Crescent Fresh Sep 02 '09 at 07:17
  • 1
    How do you pass arguments to the constructor using this method? – James McMahon Sep 04 '12 at 20:17
  • 3
    @JamesMcMahon In that case you'd have to use `window[classNameString](args)`. But as Crescent Fresh mentions, be careful, as this might break in some cases. – peirix Sep 05 '12 at 08:47
  • @peirix That's great! Any idea if I can do that with classes loaded with requirejs?? – Renaud Feb 13 '13 at 15:21
  • @Reno I don't know. I haven't tried, but that should be easy to try out. I would guess that there would be no problems, since it's basically just getting variables/classes from a global level. – peirix Feb 14 '13 at 07:30
  • @peirix I got it working, it goes like this : `require([myModuleName], function(myModule) { var myModuleInstance = new myModule(); });` where `myModuleName` is the string containing the name of the class I want to instantiate. – Renaud Feb 14 '13 at 17:29
  • @Mike That's probably because you don't have the global `window` object in node.js. And, that wasn't really part of the question either. I think it should work with the `global` object in node.js instead of `window` https://nodejs.org/api/globals.html – peirix May 13 '15 at 11:59
  • Doesn't seem to work in node.js with global either. Not sure why., – Mike May 14 '15 at 14:25
  • @Mike Probably better to start a new question on it, rather than discussing it here (: – peirix May 18 '15 at 11:47
  • 9
    This doesn't seem to be working for me. After some trial and error I found that this works: `var obj = new Home(id);`, but then this doesn't: `var obj = new window["Home"](id);`. I'm trying to get this to work: `new window[x](id);` where `x = "Home"`... The error I am getting is `Uncaught TypeError: window[x] is not a constructor` – Abraham Murciano Benzadon Jul 12 '17 at 18:05
  • not working in case of `window[prop.pipeVal]('en-US')` – Pardeep Jain May 08 '18 at 10:03
56

Here's a more robust solution that will work with namespaced functions:

var stringToFunction = function(str) {
  var arr = str.split(".");

  var fn = (window || this);
  for (var i = 0, len = arr.length; i < len; i++) {
    fn = fn[arr[i]];
  }

  if (typeof fn !== "function") {
    throw new Error("function not found");
  }

  return  fn;
};

Example:

my = {};
my.namespaced = {};
(my.namespaced.MyClass = function() {
  console.log("constructed");
}).prototype = {
  do: function() {
    console.log("doing");
  }
};

var MyClass = stringToFunction("my.namespaced.MyClass");
var instance = new MyClass();
instance.do();
Yuriy Nemtsov
  • 3,589
  • 2
  • 27
  • 43
  • Why `(windows || this)`, isn't window always going to be defined? – James McMahon Sep 06 '12 at 15:54
  • 11
    @JamesMcMahon: The world as we know is no longer the reality. Tenants like nodejs have also come to occupy our planet! :) – Mrchief May 06 '13 at 20:33
  • Since strict mode has come along ([*ECMA-262 ed 5*](http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262%205th%20edition%20December%202009.pdf) in 2009), `window || this` may return *undefined* in a non-browser environment if *this* isn't set by the call (which it isn't in the example). – RobG Jun 01 '17 at 23:13
  • Hi how to use this and make it work for classes that are imported?? e.g. import Grid from '../../mazes/components/Grid' – preston Sep 12 '19 at 03:46
34

BTW: window is the reference to the global Object in browser JavaScript. Which is also this, and should work even in non-browser environments such as Node.js, Chrome extensions, transpiled code etc.

var obj = new this[classNameString]();

The limitation is that the class being called must be in the global context. If you want to apply the same to a scoped class you need to do:

var obj = (Function('return new ' + classNameString))()

However, there really is no reason to use a string. JavaScript functions are themselves objects, just like strings which are objects also.

Edit

Here is a better way to get the global scope that works in strict mode as well as non-browser JS environments:

var global;
try {
  global = Function('return this')() || (42, eval)('this');
} catch(e) {
  global = window;
}

// and then
var obj = new global[classNameString]

From: How to get the global object in JavaScript?

Community
  • 1
  • 1
bucabay
  • 4,909
  • 2
  • 22
  • 34
  • 9
    It's only `this` if called from a global context. `window` works regardless of the context you are in, so I see no reason to prefer `this` over `window`. – devios1 Oct 18 '11 at 16:55
  • 5
    Like the answer states, the environment you are in is not necessarily a browser. Therefore, using `this` is preferable, because in a non-browser environment `window` may be undefined, or not what you expect. – XedinUnknown Jan 03 '16 at 17:27
  • @XedinUnknown Ususally when you write a piece of javascript you know if it's for clientside or let's say for node. If you know it's for a browser, then there is no disadvantages to prefer `window` over `this`. – Sebi Apr 12 '16 at 18:01
  • 2
    @Sebi, nowhere in the question is it implied that the environment is clientside. Also, clientside doesn't necessarily mean "browser" either. The answers are providing a way to fulfill the OP's requirements by explicitly referencing the global context - that's the whole point. I'm pointing out that the only reliable way to reference the global context from the global context is `this`, not `window`. Furthermore, the most reliable way to reference the global context NOT from the global context is `top`. Also, maybe good to inject it into the closure. – XedinUnknown Apr 14 '16 at 15:21
  • 1
    @devios1 If you test the code you'll notice it works from any context not just the global context. That's why we do `Function('return this')()` instead of `return this`. The former automatically switches to the global context. This works regardless of context. window can be overwritten, and is browser only. It's good to not know code execution context to write cross platform code. The answer I gave is cross platform and cannot be overwritten and therefore much better than using `window`. It will work in transpiled code, like with webpack, gulp and in node, extensions etc. Please test it first. – bucabay Apr 25 '17 at 12:16
  • @devios1 I think the confusion here is that the function must be in the global scope. The calling context can be any scope. Which is the same for using `window` without the issues with `window`. The calling context can be any scope. – bucabay Apr 25 '17 at 12:36
  • If you're using tools like gatsby, it could be server side or browser side. – Pete - MSFT Mar 25 '20 at 05:29
12

If MyClass is global, you can access it as a property of window object (assuming your code runs in a browser) using subscript notation.

var myObject = new window["MyClass"]();
Chetan S
  • 22,610
  • 2
  • 60
  • 78
9

If classNameString come from secure source you can use

var classNameString = 'MyClass';
var myObject = eval("new " + classNameString + "()");

This solution works with namespaces and is independent on platform (browser/server).

Misaz
  • 2,922
  • 1
  • 24
  • 39
  • This is identical to the deleted answer from 3 years before. – RobG Jun 01 '17 at 23:22
  • 3
    I don't know about it and don't know why it was deleted? – Misaz Jun 02 '17 at 15:13
  • It was deleted because `eval` opens up a security risk for client-side users. Unless you're using javascript in closed production, the best advice is to not use `eval`. It's good practice to not use it in the first place because it may become a bad habit. – Jacksonkr Jul 05 '17 at 18:44
  • 2
    @Jacksonkr, does that also mean I shouldn't drink a beer in the first place because it may become a bad habit? :) – Vince Horst Jul 14 '17 at 19:50
  • 3
    Per your analogy: Using `eval` when you've heard it's a bad idea is like driving drunk. "Nothing happened ...hic... last time I drove drink ..hic.. in fact I drive better drunk! ...hic..." By the time something goes wrong it's too late. Don't be that guy. – Jacksonkr Jul 14 '17 at 21:25
  • 8
    To advocate NEVER using a function, eval or otherwise, is just bad advice. If it was a function without any use cases, it would've been depreciated and removed as a feature by now. – MacroMan Jul 11 '18 at 10:09
  • 1
    Exactly MacroMan. For it to be deleted as an answer is ridiculous. – Lee Sep 18 '18 at 08:08
  • Despite this solution is dangerous, it actually works in every environment, specifically in node.js. – loretoparisi Feb 13 '19 at 15:59
  • 1
    @loretoparisi unfortunately its the only one working for me until I find a safer working solution :( – PowerAktar Jan 07 '20 at 17:05
  • There is nothing wrong with using eval. It isn't anything like driving drunk. Just don't let users pass their own input to eval. Do that and you'll be fine. If eval is bad then so is setTimeout which is frequently used and new Function("...") which is in another working answer posted here. – PHP Guru Nov 15 '20 at 20:32
3

Here is improved version of Yuriy's method that also handles objects.

var stringToObject = function(str, type) {
    type = type || "object";  // can pass "function"
    var arr = str.split(".");

    var fn = (window || this);
    for (var i = 0, len = arr.length; i < len; i++) {
        fn = fn[arr[i]];
    }
    if (typeof fn !== type) {
        throw new Error(type +" not found: " + str);
    }

    return  fn;
};
pjesi
  • 3,421
  • 3
  • 18
  • 15
2
function myClass(arg){
}

var str="myClass";
dynamic_class=eval(str);

var instance=new dynamic_class(arg); // OK
Xakiru
  • 1,631
  • 1
  • 10
  • 9
0

On Firefox, there are security rules for extensions, and so for the Javascript console. Don't make the mistake I did to test in the console because none of those solutions work. Whereas when you test from a page it works better :

  • the eval solution works well
  • Function('return new '... works (object is created) except for the constructor's arguments that are passed as "undefined" I didn't test other solutions.

In French : Sur Firefox il y a des règles de sécurité qui s'appliquent aux extensions, et donc à la console Javascript. Ne faites pas l'erreur que j'ai faite de tester depuis la console car aucune de ces solutions ne marchent. Par contre quand on teste depuis une page cela fonctionne mieux :

  • la solution eval fonctionne bien
  • Function('return new '... fonctionne (l'objet est créé) sauf que les paramètres du constructeur sont "undefined" Je n'ai pas testé les autres solutions.
Antony
  • 1
  • 1