Introducción
La evolución constante de un lenguaje tan vivo como Javascript, que entre otras cosas pronto controlará hasta el alumbrado público de tu ciudad, no es fácil. A cada nueva edición del estándar, se incorporan nuevas funcionalidades y mejoras en respuesta a las necesidades que los desarrolladores van solicitando conforme sus escenarios evolucionan. Del mismo modo, esta constante revisión conlleva a veces problemas colaterales entre funcionalidades ya incorporadas exigiendo tomar nuevas soluciones de compromiso.
En este caso, con ECMAScript 2016, hemos comprobado que el TC-39 ha tenido que eliminar el uso de la directiva «use strict» en el cuerpo de aquellas funciones donde sus parámetros utilizan valores por defecto, desestructuración o el arrastre. Funcionalidades todas que ya hemos revisado en detalle en este mismo blog.
Veamos en qué consiste este cambio, cómo nos afecta y qué podemos hacer para sortearlo.
El cambio en ECMAScript 2016
Como hemos comentado, el cambio viene en el uso de la directiva «use strict» aplicada al cuerpo de funciones:
function foo ( x, y ) { "use strict"; // Marvelous code goes here... } |
El ejemplo anterior continúa siendo perfectamente válido. El cambio llega cuando los parámetros de nuestra función hacen uso de las fórmulas recientes:
- Uso de valores por defecto, ya sean asignaciones o expresiones
- Desestructuración (parte 1, parte 2)
- Parámetros de arrastre o acarreo
Ejemplos de todo esto serían lo siguiente:
// Default values function foo ( x = 0, y = 10 ) { "use strict"; // Marvelous code goes here... } // Default values (expressions) function foo ( x = isRequired( 'string' ) ) { "use strict"; // Marvelous code goes here... } // Destructuring function foo = function ( { id = 0 } ) { "use strict"; // Marvelous code goes here... }; // Rest parameters function foo ( ...x ) { "use strict"; // Marvelous code goes here... }; |
Todos los ejemplos anteriores, ahora lanzan un error de sintaxis indicando con ello que no es válido el uso de la directiva en estos escenarios:
Uncaught SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list |
¿Por qué es ahora un error?
Como se ha comentado, este error es una novedad en ECMAScript 2016 que no se producía en ediciones previas.
NOTA: En relación a lo anterior, un dato interesante es que cualquiera de los ejemplos anteriores es correcto en la actual versión de Firefox (durante la redacción de este artículo, la v49) ya que Mozilla no ha implementado aún este cambio.
El problema viene en el modo en cómo el modo estricto funciona con los argumentos complejos de las funciones.
Como bien explica Zakas, cuando se presentó el modo estricto en ECMAScript 5.1, tanto la desestructuración como los valores por defecto no existían. Eso permitía que el modo estricto no tuviese que preocuparse de la evaluación de esos parámetros, limitándose a comprobar el resto del flujo (evitar duplicados, sintaxis obsoleta como eval o arguments, etc.).
Desde que entran en escena los parámetros complejos, éstos requieren de una evaluación previa. El problema aquí es que el estándar obliga a que esta evaluación se realice en el mismo modo que el cuerpo de la función, lo cual significa que si hemos indicado ahí un modo estricto, éste tiene también que aplicarse a sus parámetros.
Lo anterior, si en un primer momento parece obvio, ha resultado ser problemático. El resultado inmediato es que ahora el modo estricto exige actuar en dos ámbitos: la evaluación de los parámetros y la ejecución en la función. Y eso dispara la complejidad que tiene que manejar el compilador.
En el ejemplo donde utilizamos una función auxiliar para forzar la presencia o tipo de un valor, podemos verlo fácilmente:
var isRequired = function () { throw new Error( 'Missing parameter' ); }; var myFunc = function ( param1 = isRequired() ) { "use strict"; return param1; }; |
NOTA: Este código está explicado en el artículo «Parámetros obligatorios y valores por defecto en funciones Javascript con ES6» en este mismo blog. Para este ejemplo, solo se ha añadido la directiva «use strict».
Aquí podemos ver como el modo estricto en la función myFunc obliga al intérprete a utilizarlo también durante la evaluación del valor por defecto. En este caso, esa expresión conduce a otra función externa isRequired, la cual debería también correr en modo estricto.
Sería en este tipo de ejemplos anidados donde la complejidad aumenta resultando difícil asegurar la integridad y coherencia del proceso.
Este escenario ilustra perfectamente el problema: dada la dificultad de extender el modo estricto entre la función original y la evaluación de sus parámetros, es lo que ha provocado que el estándar la elimine.
Solución de urgencia
Actualmente, la forma más directa de evitar el error es suprimiendo directamente la directiva «use strict» en el cuerpo de nuestra función.
También es interesante contemplar que podemos beneficiarnos del uso global de «use strict» ubicándolo fuera de nuestra función:
"use strict"; function foo ( x = 0 ) { // Marvelous code goes here... } |
De este modo, la directiva se aplica a todo el código incluyéndose la función con parámetros complejos. Esta solución resulta en una sintaxis válida. Para el intérprete, cuando todo el código se trata en modo estricto, se elimina la ambigüedad/complejidad de aquellas expresiones necesarias en parámetros complejos.
Forzando la sintaxis
La eliminación de «use strict» no debería suponer crítico al actualizar nuestros códigos. De hecho, su uso es innecesario cuando trabajamos con módulos o las nuevas clases que ya hacen uso este modo de forma automática. Pese a todo, si necesitamos que nuestra función se ejecute en modo estricto, podemos recurrir a una función autoejecutable (IIFE) extrayendo así el «strict mode» al cuerpo de ésta:
var foo = ( function () { "use strict"; return function ( x = 0 ) { // Marvelous code goes here... } } )(); |
La anterior sintaxis es válida y permite extender nuestro modo estricto a una función con parámetros especiales gracias a la clausura generada.
Conclusión
Los lenguajes de programación evolucionan y eso da lugar a que todas las funcionalidades introducidas con anterioridad tengan que ser revisadas a cada nueva edición. En esta ocasión, el TC-39 se ha encontrado con una ambigüedad sintáctica que lo ha llevado a suprimir por la vía rápida una directiva heredada desde la especificación ECMAScript 5.1 (ECMA-262).
Es hora de revisar nuestros códigos y actualizarlos en caso de ser necesario.
Excelente noticia sobre ecmaScript 2016, hace mucho no entraba y revisando los marcadores me encontré tu blog, gracias por tu trabajo.