2010-06-11 17 views
36

dijo Como en varias ocasiones, se considera mala práctica utilizar el Function constructor (véase también la ECMAScript Language Specification, 5 ª edición , § 15.3.2.1):usos legítimos del constructor Función

new Function ([arg1[, arg2[, … argN]],] functionBody) 

(donde todos los argumentos son cadenas que contienen nombres de argumento y la última (o única) cadena contiene el cuerpo de la función).

A modo de recapitulación, se dice que es lento, según lo explicado por the Opera team:

Cada vez que [...] el Function se llama al constructor en una cadena que representa el código fuente, el motor de guiones debe comenzar la maquinaria que convierte el código fuente en el código ejecutable . Esto suele ser costoso para el rendimiento - fácilmente cien veces más caro que una simple función llamada, por ejemplo. (Marcos Tarquino 'Wilton-Jones)

Aunque no es que mal, de acuerdo con this post en MDC (no he probado esto por mí mismo usando la versión actual de Firefox, sin embargo).

Crockford adds que

[e] l citando convenciones del lenguaje hacen que sea muy difícil de expresar correctamente cuerpo de una función como una cadena . En el formulario de cadena, no se puede realizar la comprobación de errores temprana . [...] Y desperdicia memoria porque cada función requiere su propia implementación independiente .

Otra diferencia es que

una función definida por una función constructor no hereda ningún margen que no sea el ámbito global (que todas las funciones heredan). (MDC)

Aparte de esto, usted tiene que estar atento para evitar la inyección de código malicioso, cuando se crea un new Function utilizando contenidos dinámicos.

Dicho esto, T.J. dice Crowder en an answer que

[n] o casi nunca es cualquier necesidad de la similares [...] nueva función (...), ya sea , de nuevo a excepción de algunos casos extremos avanzadas.

Entonces, ahora me pregunto: ¿cuáles son estos "casos avanzados"? ¿Existen usos legítimos del constructor de funciones?

+0

ES5 en modo estricto no arroja cuando se utiliza 'nueva función' ... –

+0

@ Šime: Según la especificación (Anexo C), debería:" Intentar definir dinámicamente una función de modo tan estricta utilizando la función constructor (15.3.2) lanzará una excepción 'SyntaxError', pero una prueba reveló que no. ¿Estoy malentendiendo algo? –

+0

Bien, lea la oración anterior. "Una función de modo tan estricto" se refiere a una función que o bien el nombre o uno de los parámetros es "eval" o "argumentos". –

Respuesta

8

jQuery lo usa para analizar cadenas JSON cuando un objeto del analizador JSON no está disponible. Parece de fiar a mí :)

 // Try to use the native JSON parser first 
     return window.JSON && window.JSON.parse ? 
      window.JSON.parse(data) : 
      (new Function("return " + data))(); 
+1

Usar el intérprete completo para analizar un formato de datos no es ideal; abre la puerta a la inyección de guiones. (* Muchas personas * lo hacen, sin embargo, no es solo jQuery. :)) –

+1

Los datos ya están desinfectados en este punto por jQuery, esto se hace puramente por la velocidad. –

6

que utilizar el constructor new Function() como intérprete JS en línea en una de las aplicaciones web que estoy en vías de desarrollo:

function interpret(s) { 
    //eval(s); <-- even worse practice 
    try { 
     var f = new Function(s); 
     f(); 
    } 
    catch (err) { 
     //graceful error handling in the case of malformed code 
    } 
} 

Mientras que consigo cosas de streaming a través de AJAX (no un iframe), continuamente interpret() en readyStateChange == 3. Esto funciona sorprendentemente bien.

Editar: aquí hay un estudio de caso claro que muestra que new Function() es categóricamente más rápido que eval(). Es decir. nunca debería (¿raramente?) usar eval en lugar de new Function().

http://polyfx.com/stuff/bsort.html < - la versión 1000 iteración, se puede bloquear el navegador

http://polyfx.com/stuff/bsort10.html < - la versión más corta

Eval es, en promedio, casi 8 veces más lento quenew Function().

+5

¿Qué es exactamente la "peor práctica" sobre la evaluación de cadenas de código a través de 'eval' en comparación con hacerlo a través del constructor' Function'? – kangax

+2

Se ejecuta más rápido y es más fácil de entender: http://www.go4expert.com/forums/showthread.php?t=13979 Además, es más fácil descartar 'f()' en un alcance o cierre específico si es necesario (por razones de seguridad). –

+2

Edité mi respuesta con un caso de estudio que acabo de escribir. Eval está en algunos navegadores casi 10 veces más lento. –

2

Este es un caso separado de mi otra respuesta.

Utilicé el constructor de funciones hace un tiempo para crear formateadores de cadenas personalizadas a los que se llamaba repetidamente. La sobrecarga de crear la función (que considero que es el problema de rendimiento del que está hablando) fue ampliamente superada por el rendimiento mejorado de las funciones personalizadas, que se crearon en tiempo de ejecución específicamente para procesar una cadena de formato en particular, y por lo tanto no necesitó evaluar toneladas de casos irrelevantes — o analizar una cadena de formato, para el caso. Es un poco como compilar una expresión regular, supongo.

6

John Resig utilizó el constructor de funciones para crear versiones "compiladas" de plantillas del lado del cliente escritas en una sintaxis asp. http://ejohn.org/blog/javascript-micro-templating/

+0

La implementación de John Resig inspiró el lenguaje de microtemplazos del subrayado, que usa el mismo enfoque (es decir, el constructor 'Function'): http://underscorejs.org/docs/underscore.html#section-143 – turtlemonvh

18

NWMatcher - Javascript CSS selector y de coincidencias, por Diego Perini - utiliza Function constructor (1, 2, 3, 4, etc.) para crear ("compilar") versiones de alta eficiencia de igualadores de selectores.

El benchmark (que acaba de ejecutar en Chrome 5) habla por sí mismo:

alt text

Nota la diferencia entre NWMatcher y el chisporroteo, que es un motor de selección muy similar, sólo sin compilación función :)

En una nota lateral, ECMAScript 5 no arroja ningún error en la invocación de Function. Ni en modo estricto, ni en modos "estándar".El modo estricto, sin embargo, presenta algunas restricciones a la presencia de identificadores tales como "eval" y "argumentos":

  • No puede haber declarar variables/funciones/argumentos con tales nombres:

    function eval() { } 
    var eval = { }; 
    function f(eval) { } 
    var o = { set f(eval){ } }; 
    
  • no se puede asignar a dicho identificador:

    eval = { }; 
    

también tenga en cuenta que en modo estricto, eval semántica es ligeramente diferente de la de ES3. Código de modo estricto no puede crear instancias de las variables o funciones en el entorno de la que se llamó:

eval(' "use strict"; var x = 1; '); 
typeof x; // "undefined" 
+0

Motor de selección de Ext.js (http://code.google.com/p/extjs-public/source/browse/trunk/release/source/core/DomQuery.js) también ha utilizado la compilación de funciones para las edades (ver 'compilar') –

+2

Tengo actualizado las referencias del código fuente. Pero el punto de referencia se ha convertido en un enlace muerto. – galambalazs

+0

[A menos que me falta algo] (http: // mathiasbynens.be/notes/javascript-properties), el modo estricto de ES5 permite tanto 'var o = {eval: 1}' como 'myObject.eval = 1;'. Sin embargo, tus otros ejemplos se echan un vistazo. ¿Cambió la especificación desde que escribió esta respuesta? –

2

El único uso legítimo He venido porque es cuando escribí esto:

Function.prototype.New = (function() { 
    var fs = []; 
    return function() { 
    var f = fs [arguments.length]; 
    if (f) { 
     return f.apply (this, arguments); 
    } 
    var argStrs = []; 
    for (var i = 0; i < arguments.length; ++i) { 
     argStrs.push ("a[" + i + "]"); 
    } 
    f = new Function ("var a=arguments;return new this(" + argStrs.join() + ");"); 
    if (arguments.length < 100) { 
     fs [arguments.length] = f; 
    } 
    return f.apply (this, arguments); 
    }; 
})(); 

El El código le permite usar Function.prototype.apply mientras usa la palabra clave new.

Ejemplo:

function Foo (x, y, z) { 
    this.x = x; 
    this.y = y; 
    this.z = z; 
    this.otherArgs = Array.prototype.slice.call (arguments, 3); 
} 

var foo = Function.prototype.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]); 
// /*equiv*/ var foo = Foo.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]); 
// /*equiv*/ var foo = Foo.New (1, 2, 3, 4, 5, 6, 7); 
var bool = true 
    && foo.x == 1 
    && foo.y == 2 
    && foo.z == 3 
    && foo.otherArgs.length == 4 
    && foo.otherArgs [0] == 4 
    && foo.otherArgs [1] == 5 
    && foo.otherArgs [2] == 6 
    && foo.otherArgs [3] == 7 
    ; 

alert (bool); 
+0

Este caso de uso ahora se puede lograr utilizando 'Object.create' en navegadores compatibles con ES5:' var newObj = Object.create (MyClass.prototype); var ret = MyClass.apply (newObj, constructorArgs); if (ret && typeof ret === "objeto") {newObj = ret; } ' – kpozin

1

Es posible que desee ejecutar una cadena de código más de una vez. Usar el constructor de Funciones significa que solo tiene que compilarlo una vez.

Es posible que desee pasar argumentos al código, por ejemplo, si está rellenando un evento, puede recuperar el atributo de evento y construir una función esperando un argumento de evento.

Puede combinar los dos y compilarlo en una ubicación y ejecutarlo en otra y aún así administrar pasar argumentos en las variables que la cadena de código espera.

Cuestiones relacionadas