new Function() with variable parameters

前端 未结 10 616
傲寒
傲寒 2020-12-25 11:38

I need to create a function with variable number of parameters using new Function() constructor. Something like this:

args = [\'a\', \'b\'];
bod         


        
相关标签:
10条回答
  • 2020-12-25 11:49

    the b.apply(null, arguments) does not work properly when b inherits a prototype, because 'new' being omitted, the base constructor is not invoked.

    0 讨论(0)
  • 2020-12-25 11:52

    There's a few different ways you could write that.

    // assign normally
    var ab = ['a','b'].join('');
    alert(ab);
    // assign with anonymous self-evaluating function
    var cd = (function(c) {return c.join("");})(['c','d']);
    alert(cd);
    // assign with function declaration
    function efFunc(c){return c.join("");}
    var efArray = ['e','f'];
    var ef = efFunc(efArray);
    alert(ef);
    // assign with function by name
    var doFunc = function(a,b) {return window[b](a);}
    var ghArray = ['g','h'];
    var ghFunc = function(c){return c.join("");}
    var gh = doFunc(ghArray,'ghFunc');
    alert(gh);
    // assign with Class and lookup table
    var Function_ = function(a,b) {
      this.val = '';
      this.body = b.substr(0,b.indexOf('('));
      this.args = b.substr(b.indexOf('(')+1,b.lastIndexOf(')')-b.indexOf('(')-1);
      switch (this.body) {
        case "return": 
          switch (this.args) {
            case "a + b": this.val = a.join(''); break;
          }
        break;
      }
    } 
    var args = ['i', 'j'];
    var body = 'return(a + b);';
    var ij = new Function_(args, body);
    alert(ij.val);
    
    0 讨论(0)
  • 2020-12-25 11:55

    You can do this using apply():

    args = ['a', 'b', 'return(a + b);'];
    myFunc = Function.apply(null, args);
    

    Without the new operator, Function gives exactly the same result. You can use array functions like push(), unshift() or splice() to modify the array before passing it to apply.

    You can also just pass a comma-separated string of arguments to Function:

    args = 'a, b';
    body = 'return(a + b);';
    
    myFunc = new Function(args, body);
    

    On a side note, are you aware of the arguments object? It allows you to get all the arguments passed into a function using array-style bracket notation:

    myFunc = function () {
        var total = 0;
    
        for (var i=0; i < arguments.length; i++)
            total += arguments[i];
    
        return total;
    }
    
    myFunc(a, b);
    

    This would be more efficient than using the Function constructor, and is probably a much more appropriate method of achieving what you need.

    0 讨论(0)
  • 2020-12-25 11:58
    new Function(...)
    

    Declaring function in this way causes the function not to be compiled, and is potentially slower than the other ways of declaring functions.

    Let is examine it with JSLitmus and run a small test script:

    <script src="JSLitmus.js"></script>
    <script>
    
    JSLitmus.test("new Function ... ", function() { 
        return new Function("for(var i=0; i<100; i++) {}"); 
    });
    
    JSLitmus.test("function() ...", function() { 
           return (function() { for(var i=0; i<100; i++) {}  });
    });
    
    </script>
    

    What I did above is create a function expression and function constructor performing same operation. The result is as follows:

    FireFox Performance Result

    FireFox Performance Result

    IE Performance Result

    IE Performance Result

    Based on facts I recommend to use function expression instead of function constructor

    var a = function() {
     var result = 0;
     for(var index=0; index < arguments.length; index++) {
      result += arguments[index];
     }
     return result;
     }
    alert(a(1,3));
    
    0 讨论(0)
  • 2020-12-25 12:03

    @AndyE's answer is correct if the constructor doesn't care whether you use the new keyword or not. Some functions are not as forgiving.

    If you find yourself in a scenario where you need to use the new keyword and you need to send a variable number of arguments to the function, you can use this

    function Foo() {
      this.numbers = [].slice.apply(arguments);
    };
    
    
    var args = [1,2,3,4,5]; // however many you want
    var f = Object.create(Foo.prototype);
    Foo.apply(f, args);
    
    f.numbers;          // [1,2,3,4,5]
    f instanceof Foo;   // true
    f.constructor.name; // "Foo"
    

    ES6 and beyond!

    // yup, that easy
    function Foo (...numbers) {
      this.numbers = numbers
    }
    
    // use Reflect.construct to call Foo constructor
    const f =
      Reflect.construct (Foo, [1, 2, 3, 4, 5])
    
    // everything else works
    console.log (f.numbers)          // [1,2,3,4,5]
    console.log (f instanceof Foo)   // true
    console.log (f.constructor.name) // "Foo"

    0 讨论(0)
  • 2020-12-25 12:03

    In this sample i used lodash:

    function _evalExp(exp, scope) {
      const k = [null].concat(_.keys(scope));
      k.push('return '+exp);
      const args = _.map(_.keys(scope), function(a) {return scope[a];});
      const func = new (Function.prototype.bind.apply(Function, k));
      return func.apply(func, args);
    }
    
    _evalExp('a+b+c', {a:10, b:20, c:30});
    
    0 讨论(0)
提交回复
热议问题