¿Eval () y new Function () son lo mismo?

92

¿Estas dos funciones hacen lo mismo entre bastidores? (en funciones de declaración única)

var evaluate = function(string) {
    return eval('(' + string + ')');
}

var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

console.log(evaluate('2 + 1'));
console.log(func('2 + 1'));
qwertymk
fuente
3
De hecho, esto se usa en jQuery 1.7.2 línea 573. Aunque con "algunos controles de seguridad"
PicoCreator
2
¿Por qué se newconsidera en esta discusión? Functionimplícitamente instancia un function object. La exclusión newno cambiará el código en absoluto. Aquí hay un jsfiddle que demuestra que: jsfiddle.net/PcfG8
Travis J

Respuestas:

117

No, son no lo mismo.

  • eval() evalúa una cadena como una expresión de JavaScript dentro del alcance de ejecución actual y puede acceder a variables locales.
  • new Function()analiza el código JavaScript almacenado en una cadena en un objeto de función, que luego se puede llamar. No puede acceder a las variables locales porque el código se ejecuta en un ámbito separado.

Considere este código:

function test1() {
    var a = 11;
    eval('(a = 22)');
    alert(a);            // alerts 22
}

Si new Function('return (a = 22);')()se usara, la variable local aconservaría su valor. Sin embargo, algunos programadores de JavaScript, como Douglas Crockford, creen que ninguno de los dos debe usarse a menos que sea absolutamente necesario , y evaluar / usar el Functionconstructor en datos que no son de confianza es inseguro e imprudente.

Por favor levantese
fuente
11
"nunca debe usarse" es una declaración tan amplia. Qué tal, nunca debe usarse cuando alguna de las entradas está fuera de su control. Habiendo dicho eso, nunca necesité usarlo, excepto para analizar JSON. Pero he respondido preguntas aquí que parecían usos válidos, implicaba algo así como permitir a los administradores generar funciones de plantillas que los usuarios finales podrían usar. En este caso, la entrada fue generada por un administrador, por lo que fue aceptable
Juan Mendes
3
@Juan: El punto de Crockford es que eval a menudo se usa incorrectamente (depende de su perspectiva), por lo que debe excluirse de un JavaScript de "mejores prácticas". Sin embargo, estoy de acuerdo en que es útil para usos como JSON o el análisis de expresiones aritméticas cuando la entrada se valida correctamente por primera vez. Incluso Crockford lo usa en su biblioteca JSON json2.js.
favor
Entonces, ¿esto significa que new Function(code)es lo mismo eval('(function(){'+code+'})()')o es un contexto de ejecución completamente nuevo?
George Mauer
5
@GeorgeMauer: Creo que quieres decir eval('(function(){'+code+'})'), que devolvería un objeto de función. Según MDN , "Las funciones creadas con el constructor de funciones no crean cierres para sus contextos de creación; siempre se ejecutan en el contexto de la ventana (a menos que el cuerpo de la función comience con una declaración" use estricto ", en cuyo caso el contexto no está definido) . "
PleaseStand
6

No.

En su actualización, las llamadas a evaluatey funcproducen el mismo resultado. Pero, definitivamente, no están "haciendo lo mismo entre bastidores". La funcfunción crea una nueva función, pero luego la ejecuta inmediatamente, mientras que la evaluatefunción simplemente ejecuta el código en el acto.

De la pregunta original:

var evaluate = function(string) {
    return eval(string);
}
var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

Estos te darán resultados muy diferentes:

evaluate('0) + (4');
func('0) + (4');
palswim
fuente
En cualquier caso, ambos son una mala práctica en todos los casos menos en los más excepcionales.
palswim
8
Bueno, modificó su ejemplo de acuerdo con su implementación. Si hubiera implementado la evaluación como return eval('(' + string + ')');entonces, arrojarían los mismos resultados.
Juan Mendes
@Juan No pensé, pero sí.
Editó
6

new Functioncrea una función que se puede reutilizar. evalsimplemente ejecuta la cadena dada y devuelve el resultado de la última declaración. Su pregunta está equivocada cuando intentó crear una función contenedora que usa Function para emular una evaluación.

¿Es cierto que comparten algún código detrás de las cortinas? Sí, muy probablemente. ¿Exactamente el mismo código? Claro que no.

Por diversión, aquí está mi propia implementación imperfecta usando eval para crear una función. ¡Espero que arroje algo de luz sobre la diferencia!

function makeFunction() {
  var params = [];
  for (var i = 0; i < arguments.length -  1; i++) {
    params.push(arguments[i]);
  }
  var code = arguments[arguments.length -  1];


 // Creates the anonymous function to be returned
 // The following line doesn't work in IE
 // return eval('(function (' + params.join(',')+ '){' + code + '})');
 // This does though
 return eval('[function (' + params.join(',')+ '){' + code + '}][0]');
}

La mayor diferencia entre esta y la nueva función es que la función no tiene un ámbito léxico. Entonces no tendría acceso a las variables de cierre y la mía sí.

Juan Mendes
fuente
¿Por qué no usar en arguments.slice()lugar del bucle for? O si los argumentos no es un verdadero arsenal, [].slice.call(arguments, 1).
Xeoncross
3

Solo quiero señalar alguna sintaxis utilizada en los ejemplos aquí y lo que significa:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' )());
 }

observe que la función (...) () tiene el "()" al final. Esta sintaxis hará que func ejecute la nueva función y devuelva la cadena, no una función que devuelva cadena, pero si usa lo siguiente:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' ));
 }

Ahora func devolverá una función que devuelve una cadena.

Jack D Menendez
fuente
2

Si quiere decir, dará los mismos resultados, entonces sí ... pero solo evaluar (también conocido como "evaluar esta cadena de JavaScript") sería mucho más simple.

EDITAR Abajo:

Es como decir ... ¿estos dos problemas matemáticos son iguales?

1 + 1

1 + 1 + 1 - 1 + 1 - 1 * 1/1

Timothy Khouri
fuente
¿Ambos vuelven a analizar la cadena?
qwertymk
ambos analizarán la cadena (en su ejemplo de código). no sé a qué te refieres con "volver a analizar".
Timothy Khouri
1
Estoy seguro de que ambos analizan (evalúan) la cadena en algún momento, pero el Functionobjeto probablemente almacena la cadena en una variable de miembro privada evalcuando se invoca la función.
palswim
1

En ese ejemplo, los resultados son los mismos, sí. Ambos ejecutan la expresión que pasas. Eso es lo que los hace tan peligrosos.

Pero hacen cosas diferentes detrás de la esencia. El que implica new Function(), detrás de escena, crea una función anónima a partir del código que proporcionas, que se ejecuta cuando se invoca la función.

El JavaScript que le pasa no se ejecuta técnicamente hasta que invoca la función anónima. Esto contrasta con lo eval()que ejecuta el código de inmediato y no genera una función basada en él.

Chris Laplante
fuente
¿Puedes exponer un poco más? ¿No están ambos ejecutados en la declaración de devolución? ¿La función () utiliza otro nivel de llamada de pila que eval?
qwertymk
Does the Function() one use another stack call level than eval?: Asumiría que sí, porque eval()no crea una función a partir de su entrada, simplemente la ejecuta. new Function()crea una nueva función, que luego invoca.
Chris Laplante
@SimpleCoder: Function () no agrega nada a la pila de llamadas. Solo si llamas a la función resultante. eval hace exactamente lo mismo (si se aplica en el contexto de la pregunta)
Juan Mendes