Depura tu sitio web más rápidamente con el Modo Estricto de ECMAScript
Hablando en sentido estricto (pero sin querer ser puntilloso), JavaScript y ECMAScript no son exactamente lo mismo. JavaScript es un dialecto de ECMAScript, pero las diferencias son, en general, irrelevantes y vienen sobre todo ocasionadas por motivos históricos de compatibilidad con versiones anteriores. ECMAScript 5, la última versión disponible (y a la que nos vamos a referir como ES5), pone sobre la mesa una serie de funcionalidades interesantes. La mayoría de estas novedades están pensadas para mejorar la disciplina de programación con este lenguaje.
Me voy a centrar en el Modo Estricto, una de las características que nos van a servir para afrontar algunas de las partes más notables del lenguaje. En esencia se trata de un nuevo modo de ejecución para JavaScript que hace que el motor de ejecución funcione con ligeras variaciones semánticas.
¿Qué es el Modo Estricto?
El Modo Estricto es una forma de obligar al motor del runtime a interpretar y ejecutar JavaScript con una semántica distinta de la que podemos ver con el modo no restringido. El código ejecutado bajo el modo estricto tiene estas características:- Excluye algunas funciones sintácticas y semánticas: no podemos hacer ciertas cosas que sí son admitidas en otros casos.
- Modifica la semántica de algunas funcionalidades: el mismo código funciona de manera distinta en el modo estricto que en el no restringido.
- Devuelve más errores en algunos casos: en situaciones en las que se ignorarían de manera transparente o se ejecutarían directamente en el modo no restringido.
- Se aplica a unidades concretas del código: no podemos aplicar el Modo Estricto a todos los archivos .js a la vez (a menos que los concatenemos todos juntos, en cuyo caso se considera una sola unidad al menos desde el punto de vista del runtime).
Cuando se marca un segmento de código como "strict", muchas de las partes más abiertas de JavaScript quedan bajo los criterios impuestos por el motor en lugar de exigir disciplina al programador.
¿Podrías detectar el error en este fragmento de código? Si tenemos activado el Modo Estricto, ¡el runtime lo detectará a la primera!
function findProduct(numbers) {
var product = 0,
len = numbers.length;
for(var i = 0; i < len; ++i) {
prodct = product * numbers[i];
}
return product;
}
Compatibilidad entre navegadores
La gran mayoría de los navegadores actuales ya soportan el Modo Estricto en sus motores de Javascript, como ocurre con Internet Explorer, Chrome, Firefox, Safari y Opera. En Internet Explorer (IE), el Modo Estricto está disponible a partir de la versión 10. Puedes descargar la última Preliminar de Plataforma de IE10 desde el sitio web de IE Test Drive.Todos los ejemplos en este artículo los hemos probado con la preliminar de plataforma de IE10 utilizando la consola de evaluación de JavaScript que os presenté hace un tiempo. Los ejemplos que hemos elegido los hemos probado también en Chrome 14 y Firefox 7 Beta.
Contextos del Modo Estricto
La ejecución de una porción de código JavaScript bajo el Modo Estricto, en sí mismo, es algo bastante sencillo, valga como ejemplo:
"use strict";
alert("Look ma! Strict mode!");
Lo mejor es que esto vale perfectamente para el código ECMAScript 3 también. (ES3 es la versión anterior de ECMAScript. ¿Qué habrá pasado con ES4? Le ha ocurrido ¡lo mismo que al dodo!) Un motor de JavaScript ES3 simplemente ignora esa línea y pasa a ejecutar el resto del script. En realidad, este tipo de sintaxis para compatibilidad con ES3 ha sido uno de los objetivos de diseño más importantes en ES5. Una parte sorprendentemente abundante de la implementación de ES5 se puede poner a funcionar directamente sobre JavaScript ES3.
El Modo Estricto, no obstante, es un ejemplo de una funcionalidad de ES5 que quizá no se pueda implementar tal cual en JavaScript ES3 sin soporte adicional por parte del runtime.
Los ejemplos siguientes de código JavaScript se pueden hacer funcionar bajo el Modo Estricto:
1. Código global
Es básicamente un código ejecutable que se incluye dentro de una etiqueta <script>. Por ejemplo:
<script>
"use strict";
// Aquí va código global en modo estricto
</script>
Debemos advertir que con HTML5 no necesitamos añadir el atributo "type" en las etiquetas de script.
2. Código de evaluación ("eval")
El código de evaluación incluye el prefijo de directiva del Modo Estricto:
eval("'use strict'; // código en modo estricto");
O bien se invoca desde el código en Modo Estricto:
"use strict";
eval("// código en modo estricto");
3. Código de función
Son funciones que tienen la directiva de Modo Estricto precediendo a una porción de código (la directiva puede colocarse en cualquier sitio, eso no importa):
function foo() {
"use strict";
// código en modo estricto
}
Las funciones declaradas en el código bajo Modo Estricto heredan esa condición:
function foo() {
"use strict";
var bar = function () {
// código en modo estricto
};
bar();
}
Conviene saber que este último caso es particularmente importante a la hora de definir callbacks para distintos manejadores de eventos.
También conviene tener en cuenta que la restricción no se extiende a las pilas de llamadas. Este es un ejemplo:
function foo() {
// no se ejecuta en modo estricto a pesar de que
// la función foo ha sido llamada desde una función
// "estricta"
}
function bar() {
"use strict";
foo();
}
bar();
Restricciones del Modo Estricto
Bien, ¿qué restricciones y semánticas alternativas se aplican exactamente en modo estricto? Vamos a revisar algunas de las principales:
1. Los identificadores han de declararse obligatoriamente antes de poder asignárseles valores
Este aspecto, en mi opinión, convierte al Modo Estricto en una herramienta de gran valor por sí mismo, aunque no hiciera nada más. Las asignaciones a variables no declaradas no se añaden automáticamente como propiedades extendidas del objeto global.
"use strict";
// ReferenceError: variable no definida en modo estricto
foo = 10;
Bajo estas condiciones, el fragmento de código que os había puesto como ejemplo antes, en la sección "¿Qué es el Modo Estricto?" fallará al ejecutarse y nos devolverá un "Error de Referencia" ("ReferenceError") debido a que el nombre de la variable "product" está mal escrita dentro de la sentencia de asignación que aparece en el interior del bucle "for".
function findProduct(numbers) {
"use strict";
var product = 0,
len = numbers.length;
for(var i = 0; i < len; ++i) {
// ReferenceError: variable no definida en modo estricto
prodct = product * numbers[i];
}
return product;
}
print(findProduct([1, 2, 3, 4, 5]));
2. No se añade un contexto automático para llamadas a función sin contexto
Las llamadas a funciones sin establecer un contexto explícito no emplean de manera automática el "objeto global" en las referencias a "this". Por ejemplo, veamos este código:
function foo() {
// devuelve "true"
print(this === window);
}
foo();
En este caso, se llama a la función "foo" sin fijar un objeto de contexto explícito, esto es, no hacemos la llamada utilizando una expresión como esta:
foo.call({foo: "bar"});
En modo no restringido, la llamada original hace que el contexto se inicialice automáticamente al objeto "global", que en los navegadores es el objeto "window". Puesto que el código anterior se estaba ejecutando en modo no restringido, la expresión "this === window" se evalúa como verdadera. Si modificamos la función de la manera propuesta en segundo lugar, vemos que "this" ya no equivale a "window":
function foo() {
"use strict";
// devuelve "false"
print(this === window);
}
foo();
3. Las palabras reservadas no pueden utilizarse como identificadores
Nombrar variables y funciones como eval, arguments, implements, let, private, public, yield, interface, package, protected o static, devuelve errores.
"use strict";
var yield; // SyntaxError: se espera un identificador
4. La no conformidad con la configuración de propiedad de ES5 genera error
Cuando se contraviene la configuración de un descriptor de propiedad en ES5 tal y como se indica en la especificación, se genera un error que aparece en Modo Estricto pero no en el modo no restringido, que simplemente ignora este hecho. Veamos algunos ejemplos:
- 1. Escribir valores en una propiedad no modificable:
"use strict";
var person = Object.create({}, {
name: {
value: "foo",
writable: false,
configurable: true,
enumerable: true
}
});
// TypeError: la asignación de valor en propiedades de solo lectura
// no está permitido en modo estricto
person.name = "bar";Fijémonos en la línea marcada en negrita. Al indicar el descriptor de la propiedad "writable" como "false" hacemos que la propiedad "nombre" del objeto "person" sea de solo-lectura. Cualquier intento de asignar un valor a esta propiedad se ignora en el modo no restringido, pero en el Modo Estricto genera un error "TypeError".
- 2. Cambio de configuración de una propiedad no configurable:
"use strict";
var person = Object.create({}, {
name: {
value: "foo",
writable: false,
configurable: false,
enumerable: true
}
});
// TypeError: no se puede redefinir una propiedad no configurable ('name')
Object.defineProperty(person, "name", {
value: "bar",
writable: true,
configurable: true,
enumerable: true
});Aquí intentamos cambiar el descriptor de propiedad de un objeto no configurable. De nuevo, este error habría pasado inadvertido en modo no restringido, pero en Modo Estricto nos devuelve un "TypeError".
Tratar de escribir en las propiedades del accesor cuando no se ha definido el "setter" genera errores que, en el modo no restringido también se ignoran:
"use strict";
var person = Object.create({}, {
name: {
get: function() {
return "foo";
},
configurable: false,
enumerable: true
}
});
// TypeError
person.name = "bar";
En este caso, "name" es una propiedad de accesor que no tiene un método "setter" definido para poder modificarla. Si intentamos modificar el valor de esta propiedad tenemos en Modo Estricto un error que no aparecerá en modo no restringido.
6. No se pueden extender los objetos no extensibles
Si se intenta extender un objeto no extensible obtenemos un error en Modo Estricto, mientras que el mismo caso en modo no restrictivo se ignora:
"use strict";
var person = {
name: "foo"
};
Object.preventExtensions(person);
// TypeError: no se puede crear una propiedad en un objeto no extensible
person.age = 10;
7. Otras restricciones importantes
El código en Modo Estricto se somete a algunas restricciones más, aunque su uso es menos frecuente.
- Las constantes numéricas ya no se interpretan como de base octal si comienzan con un cero.
- Las instancias de variables/funciones en el código "eval" bajo Modo Estricto se producen en un entorno local al código "eval" y no en el entorno del código que realiza la llamada. Esto significa que el código "eval" no puede incorporar nuevos identificadores en el contexto de ejecución/alcance del llamador.
- "arguments" es inalterable. No podemos extender arbitrariamente el objeto "arguments" añadiéndole propiedades a nuestro gusto. Sinceramente, no acabo de entender por qué alguien podría tratar de hacer esto, pero seguramente ocurre más a menudo de lo que yo pienso, al menos en opinión del ECMA, porque se ve que se han tomado la molestia de indicar que ¡esto no se puede hacer en el Modo Estricto!.
- "arguments.callee" y "arguments.caller" ya no se pueden utilizar en funciones bajo Modo Estricto. Debo decir que ¡no me afecta este tema en concreto!
- No se permite crear definiciones duplicadas de propiedades de un objeto en código bajo Modo Estricto. El ejemplo siguiente nos produciría un error:
"use strict";
var o = Object.create({}, {
name: {
value: "foo"
},
name: {
value: "bar"
}
});
Este código nos genera un error de sintaxis con el mensaje "Multiple definitions of a property not allowed in strict mode" ("no se permiten múltiples definiciones de una propiedad en modo estricto"). En modo no restringido, "o.name" nos toma el valor "bar".
- La llamada a "delete" sobre una propiedad de ES5 cuya propiedad "configurable" se ha establecido como "false" genera un error en Modo Estricto. Es una variación de la restricción ya comentada en el punto 4 anterior.
- La sentencia "with" de JavaScript no se admite en Modo Estricto.
- En Modo Estricto no está permitido crear funciones con nombres duplicados de parámetros. De nuevo, parece que va contra toda lógica que alguien quiera hacer esto, pero ¡por lo visto ocurre!
Consejos y buenas prácticas
Estas son algunas cosas que debes tener en cuenta a la hora de escribir código utilizando el Modo Estricto:- Dado que el Modo Estricto es una cosa más bien reciente, es muy probable que algunos visitantes de tus aplicaciones web utilicen un motor JS que no reconoce el Modo Estricto (salvo que los usuarios actualicen voluntariamente sus navegadores a la última versión, algo que, puedo asegurarlo, no vas a conseguir). Esto significa básicamente que todo tu código se va a ejecutar en modo no restringido, tanto si has incluido la directiva de Modo Estricto como si no. Conviene siempre probar el código en modo no restringido para asegurarnos de que todo funciona como esperamos.
- Ahora bien, incluso si la gran mayoría de los usuarios finales utilizan navegadores que no soportan el Modo Estricto, sigue siendo razonable utilizar el Modo estricto en el entorno de desarrollo, ya que en estas condiciones nos obligamos a aplicar buenas prácticas de programación con JavaScript. Sin duda, al final acabas beneficiándote de haber tenido que asumir estas obligaciones durante el desarrollo y, por supuesto, no olvides nunca probar todo en modo no restringido antes de ponerlo en explotación.
- Activa el Modo Estricto poco a poco, en pequeños incrementos en vez de tratar de arreglarlo todo de una sola vez. Si tienes un archivo JS de, por ejemplo, 3.000 líneas, probablemente no será buena idea el añadir la directiva "use strict"; en la primera fila del archivo, ya que las diferencias de semántica entre los dos modos pueden dar lugar a errores sutiles e imprevistos. Debes hacerlo en porciones pequeñas, a nivel de función. En el código nuevo que desarrolles sí puedes utilizarlo de forma general.
- IE Test Drive, donde puedes descargar la Preliminar de Plataforma de IE10
- Documentación del Modo Estricto en la Guía de Desarrollo de IE10 Platform Preview
- Demo de Modo Estricto
- Artículo sobre el Modo Estricto de ES5 en Firefox 4, que trae buena información sobre el Modo Estricto en general
- La especificación de ES5-no es lo que podríamos llamar una lectura apasionante, ¡pero no hay nada mejor que hacer referencia a las especificaciones para dotar de autoridad a cualquier argumento!
Rajasekharan Vengalil
Evangelista Desarrollador en Microsoft