Was ist passiert?
Ein Vorschlag für eine JavaScript-Sprachfunktion namens Array.prototype.flatten
ist nicht mit dem Web kompatibel. Die Einführung der Funktion in Firefox Nightly führte dazu, dass mindestens eine beliebte Website nicht mehr funktionierte. Da der problematische Code Teil der weit verbreiteten MooTools-Bibliothek ist, sind wahrscheinlich noch viele weitere Websites betroffen. (Obwohl MooTools 2018 nicht mehr häufig für neue Websites verwendet wird, war es früher sehr beliebt und ist noch immer auf vielen Produktionswebsites zu finden.)
Der Autor des Vorschlags schlug scherzhaft vor, flatten
in smoosh
umzubenennen, um das Kompatibilitätsproblem zu vermeiden. Der Witz war nicht für alle klar, einige Leute dachten fälschlicherweise, dass der neue Name bereits festgelegt war, und die Situation eskalierte schnell.
Was tut Array.prototype.flatten
?
Array.prototype.flat
, ursprünglich als Array.prototype.flatten
vorgeschlagen, vereinfacht Arrays rekursiv bis zum angegebenen depth
, das standardmäßig 1
ist.
// 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]
Derselbe Vorschlag enthält Array.prototype.flatMap
, das Array.prototype.map
ähnelt, das Ergebnis jedoch in ein neues Array flachlegt.
[2, 3, 4].flatMap((x) => [x, x * 2]);
// → [2, 4, 3, 6, 4, 8]
Was macht MooTools, was dieses Problem verursacht?
MooTools definiert eine eigene nicht standardmäßige Version von Array.prototype.flatten
:
Array.prototype.flatten = /* non-standard implementation */;
Die flatten
-Implementierung von MooTools unterscheidet sich vom vorgeschlagenen Standard.
Das ist jedoch nicht das Problem. Wenn Browser Array.prototype.flatten
nativ unterstützen, überschreibt MooTools die native Implementierung. So funktioniert Code, der auf dem MooTools-Verhalten basiert, wie vorgesehen, unabhängig davon, ob native flatten
verfügbar ist.
So weit, so gut!
Leider passiert dann etwas anderes. MooTools kopiert alle benutzerdefinierten Arraymethoden in Elements.prototype
(Elements
ist eine MooTools-spezifische API):
for (var key in Array.prototype) {
Elements.prototype[key] = Array.prototype[key];
}
for
–in
iteriert über „zählbare“ Eigenschaften. Dazu gehören keine nativen Methoden wie Array.prototype.sort
, aber regelmäßig zugewiesene Eigenschaften wie Array.prototype.foo = whatever
. Aber – und das ist der Clou – wenn Sie eine nicht aufzählbare Property überschreiben, z.B. Array.prototype.sort = whatever
, bleibt sie nicht aufzählbar.
Derzeit wird mit Array.prototype.flatten = mooToolsFlattenImplementation
eine aufzählbare flatten
-Eigenschaft erstellt, die später in Elements
kopiert wird. Wenn Browser jedoch eine native Version von flatten
ausliefern, kann sie nicht mehr aufgezählt werden und wird nicht in Elements
kopiert. Code, der auf Elements.prototype.flatten
von MooTools basiert, funktioniert jetzt nicht mehr.
Es scheint zwar, als würde das Problem durch Ändern des nativen Array.prototype.flatten
in ein zählbares Element behoben, aber das würde wahrscheinlich zu noch mehr Kompatibilitätsproblemen führen. Auf jeder Website, auf der for
–in
zum Durchlaufen eines Arrays verwendet wird (was zwar nicht empfohlen wird, aber vorkommt), würde dann plötzlich eine zusätzliche Schleifeniteration für die Property flatten
ausgeführt.
Das größere zugrunde liegende Problem ist die Änderung von vordefinierten Objekten. Die Erweiterung nativer Prototypen gilt heute allgemein als schlechte Praxis, da sie sich nicht gut mit anderen Bibliotheken und Drittanbietercode kombinieren lässt. Ändern Sie keine Objekte, deren Inhaber Sie nicht sind.
Warum behalten wir nicht einfach den bestehenden Namen und zerstören das Web?
1996, noch bevor CSS weithin verbreitet war und lange bevor es „HTML5“ gab, ging die Website von Space Jam online. Die Website funktioniert heute noch genauso wie vor 22 Jahren.
Wie ist das passiert? Wurde diese Website all die Jahre lang von jemandem gepflegt und jedes Mal aktualisiert, wenn Browseranbieter eine neue Funktion eingeführt haben?
„Don't break the Web“ ist das wichtigste Designprinzip für HTML, CSS, JavaScript und alle anderen Standards, die im Web weit verbreitet sind. Wenn durch die Einführung einer neuen Browserfunktion bestehende Websites nicht mehr funktionieren, ist das für alle Beteiligten schlecht:
- Besucher der betroffenen Websites können die Website plötzlich nicht mehr richtig nutzen.
- die Websiteinhaber hatten eine einwandfrei funktionierende Website, die plötzlich nicht mehr funktionierte, ohne dass sie etwas geändert hatten;
- Browseranbieter, die die neue Funktion anbieten, verlieren Marktanteile, weil Nutzer den Browser wechseln, nachdem sie festgestellt haben, dass „es in Browser X funktioniert“
- Sobald das Kompatibilitätsproblem bekannt ist, weigern sich andere Browseranbieter, es zu liefern. Die Funktionsspezifikation entspricht nicht der Realität („nichts als eine Fiktion“), was sich negativ auf den Standardisierungsprozess auswirkt.
Sicher, im Nachhinein hat MooTools das Falsche getan. Aber das Web zu zerstören, bestraft nicht MooTools, sondern die Nutzer. Diese Nutzer wissen nicht, was ein Moo-Tool ist. Alternativ können wir eine andere Lösung finden und die Nutzer können weiterhin das Web nutzen. Die Entscheidung ist einfach.
Heißt das, dass schädliche APIs nie von der Webplattform entfernt werden können?
Das ist unterschiedlich. In seltenen Fällen können schädliche Funktionen aus dem Web entfernt werden. Selbst herauszufinden, ob es möglich ist, eine Funktion zu entfernen, ist sehr schwierig. Es erfordert umfangreiche Telemetrie, um zu quantifizieren, wie viele Webseiten sich ändern würden. Wenn die Funktion jedoch nicht sicher genug ist, für Nutzer schädlich ist oder nur sehr selten verwendet wird, ist dies möglich.
<applet>
, <keygen>
und showModalDialog()
sind Beispiele für fehlerhafte APIs, die erfolgreich von der Webplattform entfernt wurden.
Warum beheben wir nicht einfach MooTools?
Es ist eine gute Idee, MooTools so zu patchen, dass es keine integrierten Objekte mehr erweitert. Das Problem wird dadurch jedoch nicht gelöst. Selbst wenn MooTools eine gepatchte Version veröffentlichen würde, müssten alle bestehenden Websites, die es verwenden, aktualisiert werden, damit das Kompatibilitätsproblem behoben wird.
Können Nutzer ihre Kopie von MooTools nicht einfach aktualisieren?
In einer perfekten Welt würde MooTools einen Patch veröffentlichen und jede einzelne Website, die MooTools verwendet, würde am nächsten Tag magisch aktualisiert werden. Problem gelöst, stimmts?
Das ist leider nicht realistisch. Selbst wenn jemand irgendwie alle betroffenen Websites identifizieren, Kontaktinformationen für jede einzelne davon finden, alle Websiteinhaber erreichen und sie alle zum Aktualisieren bewegen könnte (was eine Refaktorisierung der gesamten Codebasis bedeuten könnte), würde der gesamte Prozess im besten Fall Jahre dauern.
Viele dieser Websites sind alt und werden wahrscheinlich nicht mehr gepflegt. Selbst wenn der Entwickler noch da ist, ist er möglicherweise kein hochqualifizierter Webentwickler wie Sie. Wir können nicht erwarten, dass alle ihre achtjährige Website aufgrund eines Webkompatibilitätsproblems ändern.
Wie funktioniert der TC39-Prozess?
TC39 ist das Komitee, das für die Weiterentwicklung der JavaScript-Sprache über den ECMAScript-Standard verantwortlich ist.
#SmooshGate ließ einige glauben, dass „TC39 flatten
in smoosh
umbenennen möchte“. Es war jedoch ein Insiderwitz, der extern nicht gut kommuniziert wurde.
Wichtige Entscheidungen wie die Umbenennung eines Vorschlags werden nicht leichtfertig getroffen, nicht von einer einzelnen Person und definitiv nicht über Nacht aufgrund eines einzigen GitHub-Kommentars.
TC39 verwendet für Funktionsvorschläge einen klaren Staging-Prozess.
ECMAScript-Vorschläge und alle größeren Änderungen daran (einschließlich der Umbenennung von Methoden) werden in TC39-Sitzungen besprochen und müssen vom gesamten Komitee genehmigt werden, bevor sie offiziell werden. Im Fall von Array.prototype.flatten
hat der Vorschlag bereits mehrere Phasen der Zustimmung durchlaufen, bis hin zu Phase 3. Das bedeutet, dass die Funktion in Webbrowsern implementiert werden kann. Es ist üblich, dass während der Implementierung zusätzliche Probleme mit den Spezifikationen auftreten. In diesem Fall kam das wichtigste Feedback nach dem Versuch, die Funktion zu veröffentlichen: Die Funktion funktioniert in ihrem aktuellen Zustand nicht richtig. Solche schwer vorhersehbaren Probleme sind einer der Gründe, warum der TC39-Prozess nicht einfach endet, sobald eine Funktion in Browsern eingeführt wurde.
TC39 arbeitet auf Konsensbasis, d. h. das Komitee muss sich auf alle neuen Änderungen einigen. Selbst wenn smoosh
ein ernst gemeinter Vorschlag gewesen wäre, ist es wahrscheinlich, dass ein Ausschussmitglied Einspruch dagegen einlegen und sich für einen gängigeren Namen wie compact
oder chain
aussprechen würde.
Die Umbenennung von flatten
in smoosh
wurde (auch wenn es sich nicht um einen Witz gehandelt hat) nie in einer TC39-Sitzung besprochen. Daher ist die offizielle Haltung von TC39 zu diesem Thema derzeit nicht bekannt. Niemand kann im Namen der gesamten TC39 sprechen, bis bei der nächsten Sitzung ein Konsens erreicht wird.
An den TC39-Treffen nehmen in der Regel Personen mit sehr unterschiedlichen Hintergründen teil: Einige haben jahrelange Erfahrung im Design von Programmiersprachen, andere arbeiten an einem Browser oder einer JavaScript-Engine und eine wachsende Zahl von Teilnehmern repräsentiert die JavaScript-Entwicklergemeinschaft.
Wie wurde SmooshGate schließlich gelöst?
Während der TC39-Tagung im Mai 2018 wurde #SmooshGate offiziell beigelegt, indem flatten
in flat
umbenannt wurde.
Array.prototype.flat
und Array.prototype.flatMap
wurden in V8 v6.9 und Chrome 69 eingeführt.