¿Qué pasó?
Una propuesta para una función del lenguaje JavaScript llamada Array.prototype.flatten
resulta incompatible con la Web. El envío de la función en Firefox Nightly provocó que al menos un sitio web popular dejara de funcionar. Dado que el código problemático forma parte de la biblioteca de MooTools, que es muy utilizada, es probable que muchos más sitios web se vean afectados. (Aunque MooTools
no se usa comúnmente para sitios web nuevos en 2018, solía ser muy popular y
aún está presente en muchos sitios web de producción).
El autor de la propuesta sugirió en broma cambiar el nombre de flatten
a smoosh
para evitar el problema de compatibilidad. No todos entendieron la broma, algunas
personas comenzaron a creer de forma incorrecta que ya se había
decidido el nombre nuevo, y la situación se desbordó rápidamente.
¿Qué hace Array.prototype.flatten
?
Array.prototype.flat
, que originalmente se propuso como Array.prototype.flatten
, aplana los arrays de forma recursiva hasta el depth
especificado, que se establece de forma predeterminada en 1
.
// Flatten one level:
const array = [1, [2, [3]]];
array.flat();
// → [1, 2, [3]]
// Flatten recursively until the array contains no more nested arrays:
array.flat(Infinity);
// → [1, 2, 3]
La misma propuesta incluye Array.prototype.flatMap
, que es como Array.prototype.map
, excepto que aplana el resultado en un array nuevo.
[2, 3, 4].flatMap((x) => [x, x * 2]);
// → [2, 4, 3, 6, 4, 8]
¿Qué hace MooTools que causa este problema?
MooTools define su propia versión no estándar de Array.prototype.flatten
:
Array.prototype.flatten = /* non-standard implementation */;
La implementación de flatten
de MooTools difiere del estándar propuesto.
Sin embargo, este no es el problema. Cuando los navegadores envían Array.prototype.flatten
de forma nativa, MooTools anula la implementación nativa. Esto garantiza que el código que se basa en el comportamiento de MooTools funcione según lo previsto, independientemente de si el flatten
nativo está disponible.
Hasta ahora, todo bien.
Lamentablemente, sucede algo más. MooTools copia todos sus métodos de array personalizados en Elements.prototype
(donde Elements
es una API específica de MooTools):
for (var key in Array.prototype) {
Elements.prototype[key] = Array.prototype[key];
}
for
-in
itera sobre propiedades "enumerables", que no incluyen métodos nativos como Array.prototype.sort
, pero sí incluyen propiedades asignadas con regularidad, como Array.prototype.foo = whatever
. Sin embargo, y este es el punto clave, si reemplazas una propiedad no enumerable, p.ej., Array.prototype.sort = whatever
, esta seguirá sin ser enumerable.
Actualmente, Array.prototype.flatten = mooToolsFlattenImplementation
crea una propiedad flatten
enumerable, por lo que más tarde se copia en Elements
. Sin embargo, si los navegadores envían una versión nativa de flatten
, esta se vuelve no enumerable y no se copia en Elements
. Cualquier código que dependa de Elements.prototype.flatten
de MooTools ahora está dañado.
Aunque parece que cambiar el Array.prototype.flatten
nativo para que sea enumerable solucionaría el problema, es probable que cause aún más problemas de compatibilidad. Cada sitio web que dependa de for
-in
para iterar en un array (que es una práctica incorrecta, pero sucede) de repente obtendría una iteración de bucle adicional para la propiedad flatten
.
El problema subyacente más grande es modificar objetos integrados. En la actualidad, se acepta que extender prototipos nativos es una práctica incorrecta, ya que no se compone bien con otras bibliotecas y código de terceros. No modifiques objetos que no te pertenezcan.
¿Por qué no mantenemos el nombre existente y rompemos la Web?
En 1996, antes de que el CSS se generalizara y mucho antes de que “HTML5” se convirtiera en algo, se publicó el sitio web de Space Jam. Hoy en día, el sitio web sigue funcionando de la misma manera que hace 22 años.
¿Cómo sucedió eso? ¿Alguien mantuvo ese sitio web durante todos estos años y lo actualizó cada vez que los proveedores de navegadores lanzaron una función nueva?
Resulta que “no romper la Web” es el principio de diseño número uno para HTML, CSS, JavaScript y cualquier otro estándar que se use ampliamente en la Web. Si el envío de una nueva función del navegador hace que los sitios web existentes dejen de funcionar, eso es malo para todos:
- los visitantes de los sitios web afectados tienen una experiencia del usuario deficiente de forma repentina.
- los propietarios del sitio web pasaron de tener un sitio web que funcionaba perfectamente a uno que no funcionaba sin cambiar nada.
- los proveedores de navegadores que envían la nueva función pierden participación de mercado, ya que los usuarios cambian de navegador después de notar que “funciona en el navegador X”.
- Una vez que se conoce el problema de compatibilidad, otros proveedores de navegadores se niegan a enviarlo. La especificación de atributos no coincide con la realidad (“solo una obra de ficción”), lo que es perjudicial para el proceso de estandarización.
Por supuesto, en retrospectiva, MooTools hizo lo incorrecto, pero romper la Web no los castiga a ellos, sino a los usuarios. Estos usuarios no saben qué es una herramienta de moo. Como alternativa, podemos encontrar otra solución y los usuarios pueden seguir usando la Web. La elección es fácil.
¿Eso significa que las APIs de mala calidad nunca se pueden quitar de la plataforma web?
Depende. En casos excepcionales, se pueden quitar elementos no deseados de la Web. Incluso solo averiguar si es posible quitar una función es un esfuerzo muy complicado, ya que requiere una telemetría extensa para cuantificar cuántas páginas web cambiarían su comportamiento. Sin embargo, esto se puede hacer cuando la función no es lo suficientemente segura, es dañina para los usuarios o se usa muy pocas veces.
<applet>
, <keygen>
y showModalDialog()
son ejemplos de APIs de mala calidad que se quitaron correctamente de la plataforma web.
¿Por qué no arreglamos MooTools?
Es una buena idea aplicar parches a MooTools para que ya no extienda objetos integrados. Sin embargo, no resuelve el problema en cuestión. Incluso si MooTools lanzara una versión con parches, todos los sitios web existentes que la usen tendrían que actualizarse para que desaparezca el problema de compatibilidad.
¿No pueden simplemente actualizar su copia de MooTools?
En un mundo ideal, MooTools lanzaría un parche y todos los sitios web que usan MooTools se actualizarían mágicamente al día siguiente. Se resolvió el problema, ¿no?
Lamentablemente, esto no es realista. Incluso si alguien pudiera identificar de alguna manera el conjunto completo de sitios web afectados, encontrar la información de contacto de cada uno de ellos, comunicarse con todos los propietarios de sitios web y convencerlos de que realicen la actualización (lo que podría implicar la refactorización de toda su base de código), el proceso completo tardaría años, en el mejor de los casos.
Ten en cuenta que muchos de estos sitios web son antiguos y es probable que no se les realice mantenimiento. Incluso si el encargado del mantenimiento sigue en el cargo, es posible que no sea un desarrollador web con las mismas habilidades que tú. No podemos esperar que todos cambien su sitio web de 8 años debido a un problema de compatibilidad web.
¿Cómo funciona el proceso de TC39?
TC39 es el comité a cargo de desarrollar el lenguaje JavaScript a través del estándar ECMAScript.
#SmooshGate hizo que algunos creyeran que “TC39 quiere cambiar el nombre de flatten
a smoosh
”, pero era una broma interna que no se comunicó bien de forma externa.
Las decisiones importantes, como cambiar el nombre de una propuesta, no se toman a la ligera, no las toma una sola persona y, definitivamente, no se toman de la noche a la mañana en función de un solo comentario de GitHub.
TC39 opera en un proceso de etapa claro para las propuestas de funciones.
Las propuestas de ECMAScript y cualquier cambio importante en ellas (incluido el cambio de nombre de los métodos) se analizan durante las reuniones de TC39 y deben ser aprobadas por todo el comité antes de que se vuelvan oficiales. En el caso de Array.prototype.flatten
, la propuesta ya pasó por varias etapas de acuerdo, hasta la etapa 3, lo que indica que la función está lista para implementarse en los navegadores web. Es común que surjan problemas de especificación adicionales durante la implementación. En este caso, los comentarios más importantes llegaron después de intentar enviarlo: la función, en su estado actual, falla en la Web. Los problemas difíciles de predecir como estos son parte de la razón por la que el proceso de TC39 no termina una vez que los navegadores envían una función.
TC39 funciona con consenso, lo que significa que el comité debe aceptar cualquier cambio nuevo. Incluso si smoosh
hubiera sido una sugerencia seria, es probable que un miembro del comité se opusiera a ella en favor de un nombre más común, como compact
o chain
.
El cambio de nombre de flatten
a smoosh
(incluso si no hubiera sido una broma) nunca se analizó en una reunión de TC39. Por lo tanto, actualmente se desconoce la postura oficial de TC39 sobre este tema. Ninguna persona puede hablar en nombre de todo TC39 hasta que se llegue a un consenso en la próxima reunión.
Por lo general, a las reuniones de TC39 asisten personas con orígenes muy diversos: algunas tienen años de experiencia en el diseño de lenguajes de programación, otras trabajan en un navegador o en un motor de JavaScript, y una cantidad cada vez mayor de asistentes está allí para representar a la comunidad de desarrolladores de JavaScript.
¿Cómo se resolvió SmooshGate?
Durante la reunión de TC39 de mayo de 2018, se resolvió oficialmente #SmooshGate cambiando el nombre de flatten
a flat
.
Array.prototype.flat
y Array.prototype.flatMap
se enviaron en V8 v6.9 y Chrome 69.