2010-11-15 23 views
26

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

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

myFunc = new Function(args, body); 

¿Es posible hacerlo sin eval()?


Muchas gracias, muchachos! En realidad, a + b no era mi principal preocupación. Estoy trabajando en un código que procesaría y expandiría plantillas y necesitaba pasar una cantidad desconocida (y variable) de argumentos a la función para que se introdujeran como variables locales.

Por ejemplo, si una plantilla contiene:

<span> =a </span> 

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

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

y luego llama

expand(4, 2, 1) 

Necesito sustituir =a con 4. Y sí, soy consciente de que Function es similar a eval() y funciona muy lento, pero no tengo otra opción.

+0

¿Por qué no recorrer la matriz? Probablemente haya una mejor manera de hacer lo que sea que intentes hacer. –

+0

Me temo que no se puede evitar la evaluación cuando intentas evaluar el texto puro como código. –

+0

Hay mejores formas de hacerlo. Si absolutamente debe usar 'new Function()', eso suena como una tarea de tarea. En ese caso, el instructor te está enseñando cómo hacer las cosas de la manera incorrecta. – darioo

Respuesta

43

Usted puede hacer esto utilizando apply():

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

sin que el operador new, Function da exactamente el mismo resultado. Puede usar funciones de matriz como push(), unshift() o splice() para modificar la matriz antes de pasarla para aplicarla.

Usted también puede simplemente pasar una cadena separada por comas de los argumentos de la función :

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

myFunc = new Function(args, body); 

En una nota lateral, ¿es consciente del objeto arguments? Se le permite obtener todos los argumentos pasados ​​a una función utilizando el soporte de estilo notación de matriz:

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 eficaz que utilizar el Función constructor, y probablemente es un método mucho más adecuado para lograr lo necesitas.

+0

¡Tu sugerencia es simplemente perfecta! Lo siguiente funciona como un amuleto: args = 'a, b'; cuerpo = 'return (a + b);'; myFunc = new Function (args, body); – mtelis

+0

@mtelis: gran cosa, me alegra que te haya ayudado :-) Puedes marcar una respuesta como aceptada haciendo clic en la marca de verificación justo debajo de las flechas de votación. ¡Y bienvenidos a Stack Overflow! –

+0

¿La variable 'this' no será diferente en' myFunc' si no usa el nuevo operador? –

0

Si acaba de querer una función sum(...):

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 proporcionar más detalles? Es bastante difícil decir cómo puedes hacer algo si no sabes lo que intentas hacer.

+0

Debería agregar' var nums; '; de lo contrario, crea nums globales que ciertamente no son deseados. – ThiefMaster

+0

cierto. Eso fue descuidado. Gracias por elegir eso. –

-1
new Function(...) 

función de declarar de esta manera hace que la función de no ser compilado y es potencialmente más lento que los otros formas de declarar funciones.

Let es 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 anterior es crear una misma operación function expression y function constructor realizar. El resultado es el siguiente:

FireFox Rendimiento Resultado

FireFox Performance Result

IE Rendimiento Resultado

IE Performance Result

Sobre la base de los hechos que recomiendo utilizar 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)); 
+1

¿Podría vincular el recurso del que tomó esa oración sobre el rendimiento? –

+13

la cita es * incorrecta *, hay muchos mitos sobre esto. La función * se compila * justo después del momento en que se llama al [constructor '' Function'] (http://sideshowbarker.github.com/es5-spec/#x15.3.2.1). Después de eso es como * cualquier otra función definida por el usuario *, sin penalizaciones de rendimiento, la * parte lenta * puede ser la creación de la función, pero no la función en sí después de su creación ... – CMS

+0

@CMS He actualizado lo anterior publicar y mostrar algunas estadísticas. –

0

Hay algunas maneras diferentes de que pueda 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); 
+0

Pero eso es solo equivalente a 'var ab = (función (c) {...}) (['a', 'b']) '! –

+0

@Chris Morgan, cierto. Estaba pensando que quería asignar las funciones a las variables en lugar de ponerlas en una expresión de función y evaluarla. – WolfRevoKcats

+0

Muchas gracias, muchachos! En realidad, a + b no era mi principal preocupación. Estoy trabajando en un código que procesaría y expandiría plantillas y necesitaba pasar una cantidad desconocida (y variable) de argumentos a la función para que se introdujeran como variables locales. Por ejemplo, si una plantilla contiene: – mtelis

7

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

Si usted se encuentra en un escenario en el que necesidad utilizar la palabra clave new y necesita enviar un número variable de argumentos a la función, se 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á!

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

Esta respuesta se refiere a constructores arbitrarios. Sin embargo, la pregunta era específicamente sobre el constructor 'Function'. El constructor de función no tiene que ser usado con 'new' en absoluto y por lo tanto es suficiente usar' Function.apply (null, args) '. –

-1
function construct(){ 
     this.subFunction=function(a,b){ 
     ... 
     } 
} 
var globalVar=new construct(); 

vs

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

Yo prefiero la segunda versión si hay sub-funciones.

-1

b.apply (null, arguments) no funciona correctamente cuando b hereda un prototipo, porque se omite 'new', no se invoca el constructor base.

+0

¿Se supone que esto es un comentario sobre otra respuesta? No parece tener nada que ver con la pregunta. – Bergi

+0

Esta pregunta pregunta específicamente por el caso 'b === Función', donde' apply' de hecho funciona (no necesita ser llamado como un constructor). Para el caso general, consulte [aquí] (http://stackoverflow.com/q/1606797/1048572). – Bergi

+0

es de hecho un comentario para @Andy E, pero SO no me deja comentar directamente – user2969819

-1

En esta muestra 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}); 
Cuestiones relacionadas