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 fehlerhafte Code Teil der weit verbreiteten MooTools-Bibliothek ist, sind wahrscheinlich noch viele weitere Websites betroffen. (Obwohl MooTools 2018 nicht häufig für neue Websites verwendet wird, war es früher sehr beliebt und wird immer noch auf vielen Produktionswebsites verwendet.)
Der Angebotsautor hat scherzhaft vorgeschlagen, 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]
Dasselbe Angebot enthält Array.prototype.flatMap
, ähnlich wie Array.prototype.map
, nur das Ergebnis wird in einem neuen Array vereinfacht.
[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 weicht vom vorgeschlagenen Standard ab.
Das ist jedoch nicht das Problem. Wenn Browser Array.prototype.flatten
nativ unterstützen, überschreibt MooTools die native Implementierung. Dadurch wird sichergestellt, dass Code, der auf dem MooTools-Verhalten basiert, wie beabsichtigt funktioniert, unabhängig davon, ob die 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 „aufzählbare“ Attribute, die keine nativen Methoden wie Array.prototype.sort
, aber regelmäßig zugewiesene Attribute wie Array.prototype.foo = whatever
enthalten. 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 erstellt Array.prototype.flatten = mooToolsFlattenImplementation
ein aufzählbares flatten
-Attribut, das später nach Elements
kopiert wird. Wenn Browser jedoch eine native Version von flatten
verwenden, 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.
Obwohl es den Anschein hat, als würde das Ändern des nativen Array.prototype.flatten
in enumerierbar das Problem beheben, würde dies wahrscheinlich noch weitere Kompatibilitätsprobleme verursachen. 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 hier sind die Änderung integrierter Objekte. 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 bekannt war und lange bevor es „HTML5“ gab, ging die Website von Space Jam online. Heute funktioniert die Website 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 waren von einer perfekt funktionierenden Website zu einer nicht funktionierenden Website, ohne dass sie etwas ändern konnten.
- 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, verweigern andere Browseranbieter den Versand. Die Funktionsspezifikation entspricht nicht der Realität („nichts als 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?
Eine gute Idee ist es, MooTools so zu patchen, dass integrierte Objekte nicht mehr erweitert werden. Das Problem wird dadurch jedoch nicht gelöst. Selbst wenn MooTools eine gepatchte Version veröffentlichen würde, müssten alle bestehenden Websites, die sie verwenden, aktualisiert werden, damit das Kompatibilitätsproblem behoben wird.
Können Nutzer nicht einfach ihre Kopie von MooTools 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.
Beachten Sie, dass viele dieser Websites veraltet sind und wahrscheinlich nicht mehr verwaltet werden. Selbst wenn der Administrator noch da ist, ist er möglicherweise kein erfahrener 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 brachte einige zu der Annahme, dass „TC39 möchte flatten
in smoosh
umbenennen“. Dies war jedoch nur ein Witz, der nach außen 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 (einschließlich der Umbenennung der Methoden) werden in den TC39-Meetings besprochen und müssen vom gesamten Ausschuss 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 in einer TC39-Besprechung noch nie diskutiert, auch wenn es kein Witz gewesen wäre. Daher ist die offizielle Haltung von TC39 zu diesem Thema derzeit nicht bekannt. Keine Person kann im Namen des gesamten TC39 sprechen, bis bei der nächsten Sitzung ein Konsens erreicht wird.
Die TC39-Meetings werden in der Regel von Personen mit ganz unterschiedlichem Hintergrund besucht. Einige haben jahrelange Erfahrung im Design von Programmiersprachen, andere arbeiten an einem Browser oder einer JavaScript-Engine und immer mehr Teilnehmer repräsentieren die JavaScript-Entwickler-Community.
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.