Search code examples
javascriptecmascript-5

How to new a Class by it's string type name and apply a array parameter inside which has null value?


  1. I have 2 Class defined somewhere else, all constructors need 3 parameters (a, b, c), but a, b(object), c(object) is in an Array

    var paramArr = [a, b, c];
    
  2. I would like to call these 2 Class via a parameter according to a dictionary object, like:

    var dict = {'a': 'A', 'b': 'B'};
    

    then,

    callClass(x){
      var className = dict[x];//string
      new className();
    }
    
  3. So, first I tried to do is something like eval so that I could call the Class with it string name.

    callClass(x){
      var className = dict[x];//string
      var classFn = new Function(this, 'new ' + className + '()');
      classFn();
    }
    
  4. still works fine. at last I should add the args by Fn.prototype.apply(), then I had to say all messed up:

    callClass(x){
      var className = dict[x];//string
      paramArr.unshift(null);
      var functionBodyStr = 'return new (Function.prototype.bind.apply(' + className + ', '+ paramArr +'))';
      var classFn = new Function(this, functionBodyStr);
      classFn();
    }
    

the classFn in the console will be:

    function(this){
      return new (Function.prototype.bind.apply(Classname, , a ,[object Object],[object Object]))
    }

it is something I expected but not all:

first, the 'null' value in the array is converted to disappeared, which leads to 'unexpected token ,'

second, all the other objects are converted to '[object Object]', which leads to 'unexpected identifier'

So, how could I apply a array param with null value? or this is a wrong way to do this?


Solution

  • If I understand correct, you are trying to create object where class/constructor name is dynamically decided. For this, you can try using eval.

    Using Eval

    Idea

    • Create class/constructor name per your requirement.
    • Use eval to look for this function and get its scope.
    • If found, you can directly use new fn(...)
    • If not found, it will throw error so you will have to handle this case.

    function MyClass1(a, b, c) {
      this.a = a;
      this.b = b;
      this.c = c
    }
    
    function MyClass2(a, b, c) {
      this.a = a;
      this.b = b;
      this.c = c
    }
    
    var classPrefix = 'MyClass';
    var result = [];
    for (var i = 0; i < 5; i++) {
      var functionName = classPrefix + (i % 3 + 1);
      var fn;
      try {
        fn = eval(functionName);
        result.push(new fn(i, i+1, i+2));
      } catch (ex) {
        console.log(functionName + ' was not found. Please check again.')
      }
    }
    
    result.forEach(function(obj) {
      console.log(obj.constructor.name, obj.a)
    })

    References:


    Using HashMap

    Idea of this approach is to have a list of possible classes with a map and use it to get reference dynamically. This is better/preferred than eval approach but it comes with an overhead of maintaining the map.

    function MyClass1(a, b, c) {
      this.a = a;
      this.b = b;
      this.c = c
    }
    
    function MyClass2(a, b, c) {
      this.a = a;
      this.b = b;
      this.c = c
    }
    
    var functionMapper = {
      'MyClass1': MyClass1,
      'MyClass2': MyClass2,
    }
    
    var classPrefix = 'MyClass';
    var result = [];
    for (var i = 0; i < 5; i++) {
      var functionName = classPrefix + (i % 3 + 1);
      var fn = functionMapper[functionName];
      fn && result.push(new fn(i, i+1, i+2));
    }
    
    result.forEach(function(obj) {
      console.log(obj.constructor.name, obj.a)
    })