Necesito crear una función con un número variable de parámetros mediante new Function() constructor. Algo como esto:

args = ['a', 'b'];
body = 'return(a + b);';

myFunc = new Function(args, body);

Es posible hacerlo sin eval()?


Muchas gracias, chicos! En realidad, a+b no era mi principal preocupación. Estoy trabajando en un código que sería el proceso y expanda plantillas y necesitaba pasar desconocido (y variable) número de argumentos en la función de tal forma que puedan ser introducidas como variables locales.

Por ejemplo, si la plantilla contiene:

<span> =a </span> 

Necesito de salida el valor de parámetro a. Es decir, si el usuario declara función de la expansión como

var expand = tplCompile('template', a, b, c) 

y, a continuación, llama

expand(4, 2, 1) 

Necesito para sustituir =a con 4. Y sí, soy bien consciente de que la Función es similar a la eval() y se ejecuta muy lento, pero no tengo ninguna otra opción.

  • ¿Por qué no acaba de bucle a través de la matriz? Probablemente hay una mejor manera de ir sobre lo que estamos tratando de hacer.
  • Me temo eval no puede ser evitado, ya que intenta evaluar puro texto como código.
  • Hay mejores maneras de hacer esto. Si es absolutamente necesario utilizar new Function(), que suena como una tarea. En ese caso, el instructor es la enseñanza de cómo hacer las cosas de la manera equivocada.
InformationsquelleAutor mtelis | 2010-11-15

9 Comentarios

  1. 46

    Usted puede hacer esto utilizando apply():

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

    Sin la new operador, Function da exactamente el mismo resultado. Puede utilizar las funciones de matriz como push(), unshift() o splice() a modificar la matriz antes de pasar a aplicar.

    También puede pasar una cadena separada por comas de los argumentos a Función:

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

    En una nota de lado, son conscientes de la argumentos objeto? Te permite obtener todos los argumentos que se pasan a una función utilizando la matriz de estilo de la notación de corchetes:

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

    Esto sería más eficiente que el uso de la Función constructor, y es, probablemente, mucho más apropiado el método para lograr lo que usted necesita.

    • Tu sugerencia es simplemente perfecto! El siguiente funciona como un encanto: args = ‘a, b’; body = ‘return(a + b);’; myFunc = new Function(args, cuerpo);
    • genial, me alegro de que te ayudó 🙂 Usted puede marcar una respuesta aceptada haciendo clic en la marca de verificación situada justo debajo de la votación flechas. Y bienvenida de Desbordamiento de la Pila!
    • No la this variable a ser diferentes en myFunc si no uso el nuevo operador?
    • En este caso, no. Como la mayoría de los integrados en los constructores, omitiendo la new operador no hace ninguna diferencia de comportamiento. No estamos usando el this objeto aquí de todos modos.
    • gracias por la rápida respuesta, no me di cuenta de que new Function fue la función real y no un marcador de posición nombre… ups
  2. 7

    @AndyE la respuesta es correcta si el constructor no importa si usted utiliza el new palabra clave o no. Algunas funciones no son tan indulgentes.

    Si usted se encuentra en un escenario donde necesidad el uso de la new y de palabras clave que usted necesita para enviar un número variable de argumentos de la función, puede utilizar este

    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 y más allá!

    JS:

    //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"

    • Esta respuesta se ocupa arbitraria de constructores. Sin embargo, la pregunta era específicamente sobre el Function constructor. La función constructor no tiene que ser utilizado con new en todo y, entonces, es suficiente utilizar sólo Function.apply(null, args).
  3. 0

    Si vas a querer un sum(...) función:

    function sum(list) {
        var total = 0, nums;
        if (arguments.length === 1 && list instanceof Array) {
            nums = list;
        } else {
            nums = arguments;
        }
        for (var i=0; i < nums.length; i++) {
            total += nums[i];
        }
        return total;
    }

    Entonces,

    sum() === 0;
    sum(1) === 1;
    sum([1, 2]) === 3;
    sum(1, 2, 3) === 6;
    sum([-17, 93, 2, -841]) === -763;

    Si quieres más, podría por favor dar más detalles? Es bastante difícil decir cómo se puede hacer algo si no sabes lo que estás tratando de hacer.

    • Usted debe agregar var nums;; de lo contrario hacer numérica global que, ciertamente, no deseado.
    • Verdadero. Que fue descuidado. Gracias por recoger eso.
  4. 0

    Hay un par de maneras diferentes que usted puede escribir eso.

    //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);
    • Pero eso es sólo equivalente a var ab = (function(c) {...})(['a','b'])!
    • Morgan, la verdad verdadera. Yo estaba pensando que quería mapa de las funciones de variables en lugar de ponerlos en una expresión de función y evaluar.
    • Muchas gracias, chicos! En realidad, a+b no era mi principal preocupación. Estoy trabajando en un código que sería el proceso y expanda plantillas y necesitaba pasar desconocido (y variable) número de argumentos en la función de tal forma que puedan ser introducidas como variables locales. Por ejemplo, si la plantilla contiene:
    • <span> =a </span> necesito de salida el valor del parámetro «a». Es decir, si el usuario declara función de la expansión de var expandir = tplCompile(‘template’, a, b, c) t
  5. 0
    new Function(...)

    Declarar la función de esta forma
    la función de no ser compilado, y
    es potencialmente más lento que los otros
    maneras de declarar funciones.

    Vamos a examinar con JSLitmus y ejecutar un pequeño script de prueba:

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

    Lo que hice anteriormente es crear un function expression y function constructor realizar la misma operación. El resultado es el siguiente:

    FireFox Rendimiento Resultado

    nueva Función() con los parámetros variables

    El Rendimiento de internet explorer Resultado

    nueva Función() con los parámetros variables

    Basado en hechos recomiendo el uso de function expression en lugar de 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));
    • Podría enlazar los recursos de donde se tomó la frase sobre el rendimiento de?
    • la cita es mal, hay una gran cantidad de mitos acerca de esto. La función de es compilado sólo después de que el momento de llamar a la Function constructor. Después de que es igual a cualquier otra función definida por el usuario, sin penalizaciones de rendimiento, el lento parte puede ser la función de la creación, pero no la función de sí misma después de su creación…
    • He actualizado el post anterior y muestran algunas estadísticas.
    • por supuesto, el la función de creación de es más lento, el motor tendrá que re-analizar y evaluar el FormalParameterList y FunctionBody en tiempo de ejecución, y se produce miles de veces dentro de su prueba. Lo que estoy tratando de decir es que la función, una vez creado por el Function constructor es «compilado» y que tendrá un rendimiento equivalente a una función creada por otros medios. Modificar su prueba para verificar la invocación, no la creación de, y usted va a ver realmente resultados similares entre los dos. Consulte esta prueba y este pregunta Saludos 🙂
  6. 0
    function construct(){
    this.subFunction=function(a,b){
    ...  
    }
    }
    var globalVar=new construct();   

    vs

    var globalVar=new function (){
    this.subFunction=function(a,b){
    ...
    }
    }

    Prefiero la segunda versión si hay sub funciones.

  7. 0

    de la b.aplicar(null, argumentos) no funciona correctamente cuando b hereda un prototipo, porque la ‘nueva’ se omite, el constructor base no se invoca.

    • Es esta supuesta a ser un comentario en otra respuesta? No parece tener nada que ver con la pregunta.
    • Esta pregunta indaga específicamente para el caso b === Function, donde apply sí funciona (no necesita ser llamado como un constructor). Para el caso general, consulte aquí.
    • de hecho, es un comentario a @Andy E, pero ASÍ no me deja comentar directamente
    • Tal vez no he entendido la pregunta con precisión, lo que pasa es que yo estaba buscando una solución a un problema similar: pasar argumentos a través de una cadena de llamadas, hasta un constructor.
  8. 0

    En este ejemplo he utilizado 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});

Dejar respuesta

Please enter your comment!
Please enter your name here