Javascript eval. Uso y alternativas

Cuando nos iniciamos en la programación Javascript suele pasar muy poco tiempo hasta que comienza a resultarnos familiar una especie de fórmula mágica que todo el mundo parece repetir incesantemente por foros, blogs y cualquier otro medio de expresión online: eval is evil. Esta contagiosa frase es aplicable a otros lenguajes como PHP, pero en este caso, su origen se lo debemos al maestro Douglas Crockford:

“eval es el mal: la función eval es la mayor característica en el mal uso de Javascript. Evítala.”
Douglas Crockford, JavaScript: The Good Parts

Ahí queda eso. Si alguien que conoce los secretos más íntimos del lenguaje afirma una frase como la anterior, debería ser suficiente como para eliminar esa instrucción de la especificación Javascript en general y de nuestro recetario personal en particular… Sin embargo, hay autores que no están de acuerdo con la postura radical de Crockford presentando su propia versión:

“eval ha sido trivializado, mal usado y condenado irremediablemente por la mayoría de los programadores Javascript. Sin embargo, al mirar la obra de algunos de los mejores desarrolladores, puede verse que, usado apropiadamente, permite la creación de algunas piezas fantásticas de código que no serían posibles de otro modo.”
John Resig, Secrets of the JavaScript Ninja

Bueno; parece que hay debate. Eso significa que quizá no tengamos que erradicar la función eval sin más, sino que tendríamos que estudiarla un poco más a fondo y decidir después si realmente es tan maligna como la pintan…

Qué es y cómo funciona eval

Según la especificación ECMA262, eval es una propiedad del Objeto Global (Global Object) que evalúa el argumento facilitado como un programa Javascript. O lo que es lo mismo, esta función recoge una cadena que convierte en un código válido que después ejecuta:

“Al código proporcionado a la función nativa eval se le denomina eval code. Siendo más precisos, si el parámetro es una cadena (objeto String), es tratado como un programa ECMAScript aplicándosele todas las especificaciones del presente estándar. ”

Punto 10.1 del ECMA262, Tipos de código ejecutable.

Debido precisamente a su capacidad de ejecutar cadenas, eval puede comprometer el nivel de seguridad de nuestra aplicación: un error de arquitectura o validación puede facilitar la inyección de código malicioso que comprometa el sistema. Esta ha sido la principal razón por la que esta función cayó en desgracia.

Sin embargo, la aparición del formato de intercambios de datos JSON devolvió a eval todo el protagonismo perdido. Como vimos en un  artículo anterior, Javascript necesita evaluar una cadena JSON para convertirla en un objeto puro. Esto ha llevado a la aparición de funciones específicas, primero ofrecidas por librerías de terceros y después presentes en la propia especificación Javascript, que realizan un eval seguro evitando los riesgos mencionados anteriormente.

Ejemplo de uso de eval

var string1 = "foo";
var string2 = "bar";
var funcName = string1 + string2;
 
function foobar(){
  alert( 'Hello World' );
}
 
eval( funcName + '()' );  // Hello World

En el ejemplo hemos compuesto el nombre de una función de manera dinámica para después llamarla utilizando eval. El código resultante es válido y funciona como se espera, sin embargo presenta una serie de inconvenientes:

  • Resulta complicado de leer. La mezcla entre referencias a variables y código válido entrecomillado da lugar a largas cadenas difíciles de seguir:
    eval( "myValue = myObject." + myKey + ";" );

    Este método resulta pesado y lento debido a que a cada evaluación de código, se necesita ejecutar el compilador interno.

  • Eval compromete la seguridad de la aplicación ya que proporciona un protagonismo excesivo a la cadena suministrada. Validar el eval code resulta por lo general complejo.

Alternativas al uso de eval

Es por eso que, teniendo en cuenta todo lo anterior, resulte interesante encontrar alternativas válidas a su uso para que nuestras aplicaciones sean legibles, rápidas y seguras.

Método 1

En la mayoría de los casos, recurrimos a eval para ejecutar una función cuyo nombre componemos deforma dinámica o para evaluar nombres de variables:

eval( 'myVar' + num + '=' + myValue );

Una alternativa mucho más limpia es llamar a la variable a través de su contexto. En este punto cabe recordar que todas las variables Javascript pertenecen a un ámbito concreto: o bien han sido declaradas dentro de una función (finalmente un objeto) o, si son globales, pertenecen al Objeto General (Object General) definido como window.

El código anterior puede refactorizarse como sigue:

window['myvar' + num] = myValue;

NOTA: Para acceder a la propiedad de un objeto a través de un nombre dinámico, utilizamos la notación con corchetes en lugar de la notación mediante punto:

// Correcto
 window['myvar' + num] = myValue; 
 
// Incorrecto
 window.myvar + num = myValue; // SyntaxError: invalid assignment left-hand

Con este nuevo código, hemos evitamos lanzar el compilador interno del intérprete ganando rapidez; hemos ganamos legibilidad e impedimos la ejecución a ciegas de la cadena resultante.

Método 2

Cuando la cadena (eval code) es más compleja, podemos recurrir al uso del constructor del objeto Function. El código:

eval(codeToRun);

Puede escribirse también como:

var tmpFunc = new Function(codeToRun);
tmpFunc();

Este método es mucho más limpio y, cuando la el argumento está compuesto por varias líneas de código, resulta también más legible. Además, es más rapido: invocar a un constructor siempre resulta más ligero que recompilar código.

Sin embargo, esta solución no llega a ser más segura que eval. Finalmente, estaríamos ejecutando de nuevo código a ciegas por lo que su uso, debería evitarse en la misma medida. El mismo Douglas Crockford escribió al respecto: “El constructor Function es una forma de eval”.

Método 3

Finalmente, podemos añadir otra alternativa (derivada de la anterior) gracias a las características de los constructores Javascript. Elegante, limpia, pero algo compleja de entender tras un primer vistazo, tendríamos:

var myVar = "constructor";
var myString = "alert( 'Hello World' )";
myVar[myVar][myVar]( myString )();  // Hello World

¿Qué ha ocurrido en este código? Veámoslo paso a paso:

MyVar[MyVar]
// function String()  (constructor de una cadena) 
 
MyVar[MyVar][MyVar]
// function Function() (constructor de un String)

El constructor de una cadena es String y el constructor de un String es una función (Function). Ahora está algo más claro: como hemos visto en el segundo método, el constructor Function acepta una cadena que interpreta como código, por lo que el ejemplo anterior es solo una forma alternativa de obtener dicho constructor.

¿Y qué hacemos con el caso JSON?

A la hora de evaluar una cadena JSON, tenemos a nuestra disposición funciones específicas que utilizan expresiones regulares para evitar código inapropiado. Librerías como jQuery o Mootols tienen sus propios métodos seguros. La propia especificación ECMAScript5 ya proporciona estas funciones de forma nativa.

Conclusión

El uso de eval está considerado como una mala práctica. Si necesitamos utilizarlo, seguramente se deba a que nuestro código está mal estructurado y podríamos evitarlo reorganizándolo.

Sin embargo, como apuntan otros desarrolladores, usado de forma consciente es una herramienta poderosa capaz de crear estupendas aplicaciones imposibles de otro modo. Basta con recordar que hasta versiones recientes, jQuery utilizaba la función eval para trabajar con las ya mencionadas cadenas JSON.

Tenemos que ser conscientes de los riesgos que conlleva y decidir si podemos utilizar esta función sin comprometer todo el sistema.

Finalmente, responder a si debemos o no utilizar eval en nuestro código es sencillo: si conocemos con certeza la fuente de los datos, no tendríamos que tener mayor problema. Si por el contrario el texto proviene de terceros, es crítico revisar nuestro código buscando la forma de implementar alguna alternativa a su uso.

Más información:

Douglas Crockford: eval is evil
Angus Croll: How evil is eval?

Acerca de Carlos Benítez

Programador Web y arquitecto Javascript. Fundador de www.etnassoft.com y OpenLibra, la Biblioteca Libre Online
Esta entrada fue publicada en Javascript y etiquetada , . Guarda el enlace permanente. Sígueme en Twitter

Últimos libros gratuitos añadidos a OpenLibra

Aún no tenemos debug!
Ningún marciano se ha comunicado.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Licencia Creative Commons 3.0

®Copyright 2016. Cotenido web bajo licencia Creative Commons 3.0

Códigos bajo licencias MIT y GPL. Ver página de licencias para más información