Strict Mode en Javascript: ECMAScript 5

01 Mar 2011

Introduccíon

La nueva especificación ECMAScript5 reconoce la posibilidad de que los usuarios limiten algunas de las características del lengunajes si lo desean. Esto puede hacerse para mejorar la seguridad, para evitar algunas de los patrones que son propensos a errores, para facilitar el debug de aplicaciones o para facilitar la futura compatibilidad con las evolucines del estándar.

Para ello, ECMAScript ofrece una variante del lenguaje estricta, comunmente denominada strict mode.

Aplicando el strict mode

La directiva para determinar si un programa ECMASCript debe interpretarse como estricto o no es una expresión literal que añadimos al código:

"use strict";
// o tambien
'user strict';

NOTA: Es importante remarcar que es necesario el punto y coma ‘;’ con el que terminamos la expresión.

Ámbito del modo estricto

Este modo es exclusivo y declarado a nivel local dentro de una unidad de código (un script). Esto significa que el strict mode puede afectar tanto a un código completo como al contenido en una función individual, pero no a un bloque delimitado por llaves {}. Sus efectos solo tienen lugar dentro de dicha unidad.

El modo estricto no restringe o modifica ningún aspecto de la semántica ECMAScript de otras unidades de código que pueden interactuar con aquella donde se ha declarado. Esto supone que un programa ECMAScript puede estar compuesto de varias unidades de código que presenten un modo estricto junto a otro no-estricto o estándar. En estos casos, el modo estricto se aplica únicamente cuando su unidad correspondiente es ejecutada.

El siguiente código sirve de ejemplo:

(function () {
"use strict";
// this function is strict...
}());
 
(function () {
// but this function is no-strict...
}());

En la nueva especificación, un bloque ECMASCript puede ser procesado usando tanto el modo stricto como el modo no-estricto. Cuando utilizamos el estricto, se modifica el comportamiento de los tres tipos de código ECMAScript: el global, el generado por eval y las funciones. Los casos de aplicación son los siguientes:

  • El código global es estricto si éste comienza con la directiva correspondiente al uso estricto.
  • El código eval es estricto si comienza con la directiva de uso estricto o si la llamada al método eval se hace dentro de un código estricto.
  • El código de una función es estricto si comienza con la directiva de uso estricto o si su declaración está contenida dentro de un código ya en modo estricto.

Cambios introducidos por el strict mode

A modo de introducción, los cambios más significativos afectan tanto a la sintaxis como al comportamiento de un programa y pueden clasificarse en alguna de las siguientes categorías:

  • Nuevos eventos de error: la variante estricta especifica nuevas condiciones de error que lanzan excepciones en situaciones no especificadas en la versión no-estricta.
  • Simplificación en el uso de variables.
  • Simplificación en el uso de eval y del objeto arguments.
  • Facilita la escritura de un código Javascript más seguro.
  • Facilita la compatibilidad con futuras especificaciones ECMAScript.

Veámos ahora cada una de estas categorías más en detalle.

NOTA IMPORTANTE: Para aquellos lectores que quieran ir comprobando los siguientes ejemplos en la consola Firebug, hay que comentar que actualmente, la versión más reciente que he testeado (1.7X.0a11) no soporta el modo estricto. Esto quiere decir que, independientemente de que lo indiquemos con la directiva, siempre se comporta como si se tratase de código no-estricto. Para hacer las pruebas, tendremos que guardar los scripts en un archivo en disco y ejecutarlos en nuestro navegador favorito. Curiosamente, un console.log() desde un archivo en strict mode si es correctamente interpretado por Firebug.

Nuevos eventos de error

El modo estricto convierte algunas de las reconocidas malas prácticas (errores aceptados) en excepciones o errores reales en tiempo de ejecución.

Por ejemplo, el modo estricto impide crear accidentalmente variables globales cuando no las iniciamos utilizando var:

"use strict";
mistypedVar = 'Hello World'; // assignment to undeclared variable mistypedVar

También ahora los intentos de sobreescribir variables, métodos u objetos nativos del lenguaje lanzarán su correspondiente error:

"use strict";
NaN = 42; // NaN is read-only
 
var obj = { get x() { return 17; } };
obj.x = 5; // Setting a property that has only a getter
 
delete Object.prototype; // property Object.prototype is non-configurable and can't be deleted

Otro nuevo error se produce si tratamos de asignar el mismo nombre para dos propiedades de un mismo objeto:

"use strict";
var foo = { bar : 1, bar : 2 } // property name bar appears more than once in object literal

Otra curiosidad del modo estricto es que no permite el uso de números en base octal. Este sistema no es parte del ECMAScript pero es soportado por todos los navegadores añadiendo el prefijo cero ‘0’ a un número: 0644 === 420. Dado que es un formato raramente útil y muy propenso a errores, se ha eliminado su uso en el modo estricto:

"use strict";
var sum = 1 + 2 + 03; // octal literals and octal escape sequences are deprecated

Simplificando el uso de las variables

Este aspecto está ligado con el uso de eval. Cuando utilizamos el modo estricto, eval no puede crear instancias de variables o funciones en su mismo contexto (ámbito), sino que en su lugar, crea un contexto nuevo únicamente para estas variables.

Esto, que puede sonar complicado, significa que aquellas variables declardas con eval, no sobreescriben ni pueden ser sobreescritas por aquellas locales con el mismo nombre:

Ejemplo en modo no-estricto:

var foo = "Hello World";
var evalCtx = eval( "var foo = 'Good Bye Lennin'" );
 
console.log( foo ); // Good Bye Lennin

La función eval ha sobreescrito la variable foo como era de esperar. Sin embargo, en el modo estricto:

"use strict";
var foo = "Hello World";
var evalCtx = eval( "var foo = 'Good Bye Lennin'; foo; " );
 
console.log( foo ); // Hello World
console.log( evalCtx ); // Good Bye Lennin

Pese a que hemos redeclarado la variable con eval, ésta no sobreescribe la global (aquella del mismo ámbito en que se ha invocado a eval), sino que permanece en un contexto propio.

Simplificando el uso de eval y arguments

En un artículo anterior habíamos comentado tanto las particularidades de eval como del objeto arguments en Javascript. En el nuevo modo estricto, estos comandos se simplifican y normalizan.

El primer aspecto interesante es que, como palabras reservadas, no pueden ser sobreescritas en tiempo de ejecución:

"use strict";
eval = function(){ } // assignment to eval is deprecated
function foo( param1, param2, param3 ){ arguments = 2; } // assignment to arguments is deprecated

Del mismo modo, se ha suprimido de la especificación el arguments.callee que refería a aquella función desde la que era invocado:

function foo( param1, param2 ){
  console.log( arguments.callee );
}
foo(); // foo(param1, param2)

En strict-mode:

"use strict";
function foo( param1, param2 ){
  console.log( arguments.callee );
}
foo();  // 'caller', 'callee', and 'arguments' properties may not be accessed on
        // strict mode functions or the arguments objects for calls to them.

Escribiendo Javascript más seguro

Javascript siempre se ha considerado un lenguaje inseguro para el usuario: desde el navegador, es posible tener acceso a información privada y, debido a su flexibilidad, es prácticamente imposible ofrecer una total seguridad sin la ayuda de constantes pruebas en tiempo de ejecución. Estos análisis consumen recursos y, por lo tanto, se traducen en una disminución del rendimiento que afecta la experiencia de usuario, precisamente aquello por lo que este lenguaje fue creado.

Para atacar este problema, se ha comenzado replanteando el comportamiento de this. En una función normal (no-estricta), this siempre devuelve un objeto: el objeto proporcionado, un valor o el objeto global (window) cuando la respuesta es undefined o null. Estp puede resultar interesante en determinados patrones pero supone un riesgo: acceder al objeto global del navegador permite escalar hasta funcionalidades del entorno que deberían permanecer seguras.

function foo(){
  return this;
}
 
console.log( foo ); // window

Si ojeamos la respuesta en nuestra consola de Firebug observaremos que desde el objeto window, tenemos acceso a todo el entorno del navegador. En el modo estricto, el comportamiento ha cambiado y ahora los valores undefined o null remiten a la función original:

"use strict";
function foo(){
  return this;
}
 
console.log( foo ); // foo()

Otro aspecto mejorado de la seguridad es el relativo a los métodos caller y arguments. Ambas expresiones son problemáticas porque permiten al código acceder a funciones privilegiadas y a sus respectivos argumentos. En el modo estricto, estas propiedades lanzan un error si tratan de ser tanto asignadas como recuperadas:

"use strict";
function fun(a, b){
  "use strict";
  var v = 12;
  return arguments.caller; // throws a TypeError
}
 
fun(1, 2); // doesn't expose v (or a or b)

Facilitando el soporte para versiones futuras del ECMASCript

Brendan Eich ya ha declarado públicamente cúales son sus deseos con respecto a la evolución del lenguaje recogiendo en el nuevo borrador Harmony algunas de ellas.

El modo estricto reúne las características apropiadas para facilitar una transición hacia las nuevas evoluciones cuyo punto de partida son precisamente sus argumentos.

Por ejemplo, para las nuevas versiones, se añadirá soporte para una serie de métodos cuyas palabras clave ya han sido ‘reservadas’ dentro del strict mode con el fin de evitar futuras colisiones. Estos comandos son: implements, interface, let, package, private, protected, public, static y yield. Como se puede deducir, el futuro del ECMAScript es un soporte completo más tradicional hacia el paradigma de la Orientación a Objetos.

La recomendación para una adopción del ES6 es prohibir los estamentos no estándares en el modo estricto. Algunos ejemplos de error son:

"use strict";
{
  function foo() { }
}
"use strict";
if (true)
  function bar() { }
"use strict";
with (obj)
  function foo() { }
"use strict";
for (;;)
  function foo() { }
"use strict";
switch (v)
{
  case 10:
    function bar() { }
  default:
    function baz() { }
}

Para más información al respecto, puede visitiarse:
New ES5 strict mode requirement: function statements not at top level of a program or function are prohibited

Conclusión

El modo estricto en la espeficación 5 del ECMAScript es un modo voluntario que cada desarrollador es libre de adoptar. La idea es facilitar el paso hacia las evoluciones del estándar alterando el comportamiento actual del intérprete. Ya sea por semántica o por seguridad, debemos comenzar a asumirlo porque es el futuro del Javascript.

Más:

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 *