¿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 es parte de la amplia biblioteca de MooTools, es probable que se vean afectados muchos más sitios web. (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 compacta 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 depende del comportamiento de MooTools funcione según lo previsto, independientemente de si flatten
nativo está disponible.
Hasta ahora, todo bien.
Lamentablemente, sucede algo más. MooTools copia todos los métodos de array personalizados en Elements.prototype
(en el que 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 gancho informativo, si reemplazas una propiedad no enumerable, p.ej., Array.prototype.sort = whatever
, sigue siendo no enumerable.
Actualmente, Array.prototype.flatten = mooToolsFlattenImplementation
crea una propiedad flatten
enumerable, por lo que luego 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 la función no coincide con la realidad (“solo una obra de ficción”), lo cual es malo 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 el solo hecho de averiguar si es posible quitar una función es un esfuerzo muy complicado, ya que se requiere una amplia telemetría para cuantificar en cuántas páginas web se modificaría 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?
Una buena idea es aplicar parches a MooTools para que ya no extienda los 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.
¿Las personas 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 identifica de alguna manera el conjunto completo de sitios web afectados, logra encontrar información de contacto de todos y cada uno de ellos, se comunica con todos los propietarios de sitios web y los convence para que realicen la actualización (lo que podría significar refactorizar toda su base de código), el proceso completo tardaría años, en el mejor de los casos.
Recuerda que muchos de estos sitios web son antiguos y probablemente no tengan mantenimiento. Incluso si el encargado del mantenimiento sigue existiendo, es posible que no sea un desarrollador web altamente calificado como 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 en consenso, lo que significa que el comité debe acordar 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 fue una broma) nunca se debatió en una reunión de TC39. Por lo tanto, por el momento, se desconoce la postura oficial de TC39 sobre este tema. Ninguna persona puede hablar en nombre de todo el 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 finalmente?
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.