原文: #SmooshGate FAQ 作者:Mathias Bynens
smoosh?!發生了什麼?!
一項名為 JavaScript 功能的提案 Array.prototype.flatten
證明與 Web 不相容。在 Firefox Nightly 中釋出該功能會導致至少一個受歡迎的網站中斷。鑑於有問題的程式碼是廣泛使用的 MooTools 庫的一部分,很可能會有更多網站受到影響。(儘管 MooTools 在 2018 年並不常用於新網站,但它曾經非常流行,並且仍然存在於許多已經正在執行的網站上。)
該提案筆者開玩笑地建議把 flatten
重新命名為 smoosh
,以避免相容性問題。
但是,並非所有人都知道這是一個笑話,有些人開始錯誤地認為這個新名字已經被確定,並且事情迅速升級。
Array.prototype.flatten
是什麼?
Array.prototype.flatten
遞迴地將陣列展按照指定的 depth
進行展平,depth
的預設值為 1
。
// Flatten one level:
const array = [1, [2, [3]]];
array.flatten();
// → [1, 2, [3]]
// Flatten recursively until the array contains no more nested arrays:
array.flatten(Infinity);
// → [1, 2, 3]
複製程式碼
同樣的提議還包括 Array.prototype.flatMap
,如同 Array.prototype.map
一樣,可以在引數裡面傳遞一個回撥函式。
[2, 3, 4].flatMap((x) => [x, x * 2]);
// → [2, 4, 3, 6, 4, 8]
複製程式碼
MooTools 導致了什麼問題?
MooTools 定義了他們自己的非標準版本 Array.prototype.flatten
:
Array.prototype.flatten = /* non-standard implementation */;
複製程式碼
MooTools 的 flatten
實現與建議的標準不同。但是,這並不是問題!當瀏覽器提供了原生的 Array.prototype.flatten
時,MooTools 會覆蓋原生實現。這可確保依賴 MooTools 的程式碼按預期執行,無論原生 flatten
是否可用。到現在為止還挺好!
不幸的是,發生了其他事情。MooTools 將其所有自定義陣列方法複製到 Elements.prototype
(Elements
是 MooTools 特定的 API):
for (var key in Array.prototype) {
Elements.prototype[key] = Array.prototype[key];
}
複製程式碼
for
-in
遍歷“可列舉”屬性,其中不包括像原生方法 Array.prototype.sort
,而是包括自定義的屬性Array.prototype.foo = whatever
。但是 - 背鍋開始了 - 如果你覆蓋了一個非列舉屬性,例如 Array.prototype.sort = whatever
,那麼這個屬性仍然是不可列舉的。
目前,Array.prototype.flatten = mooToolsFlattenImplementation
建立一個列舉 flatten
屬性,所以它以後會被複制到 Elements
。但是,如果我們釋出原生版本的 flatten
,它將變得不可列舉,並且不會被複制到 Elements
。現在,任何使用 MooTools 並依賴於 Elements.prototype.flatten
的程式碼都被破壞了。
儘管將原生 Array.prototype.flatten
變為可列舉可能會解決問題,但它可能會導致更多的相容性問題。每個依賴於 for
-in
遍歷陣列(這是一個糟糕的做法,但它經常被使用)的網站會突然得到該 flatten
屬性的迴圈迭代。
這裡更大的底層問題是修改內建物件。現在擴充套件本地原型通常被認為是一種不好的做法,因為它不能很好地與其他庫和第三方程式碼結合。不要修改不屬於你的物件!
我們為什麼不保留現有名稱並打破網路?
1996 年,在 CSS 廣泛傳播之前,早在“HTML5”之前,Space Jam 網站就已經開始執行了。今天,該網站已經順利執行 22年了。
這是怎麼做到的呢?這些年有沒有人維護該網站,每次瀏覽器供應商釋出新功能時都會更新它?
事實證明,“不要打破網路”是 HTML,CSS,JavaScript 和 Web 任何標準上都廣泛使用的頭號設計原則。如果釋出新的瀏覽器功能導致現有網站停止工作,那對每個人都不利:
-
受影響網站的訪問者突然得到一個破壞的使用者體驗;
-
網站所有者從一個完美的網站變成了一個沒有功能的網站,而網站所有者卻並沒有改變任何東西;
-
使用者看到“只支援 XXX 瀏覽器”之後切換瀏覽器,因此推出新功能的瀏覽器供應商失去了市場份額。
-
一旦知道相容性問題,其他瀏覽器供應商拒絕實現此特性。導致某特性的規範與實際實現情況不符(“只是虛構的作品”),這對標準化過程不利。
當然,回想起來 MooTools 做錯了一件事 - 但是打破網路並不懲罰它們(MooTools),而是會懲罰使用者。這些使用者不知道 MooTools 是什麼。
或者,我們可以找到另一種解決方案,使用者可以繼續使用網路。
這是否意味著無法從 Web 平臺中刪除不好的 API?
在極少數情況下,可以從網路中刪除不良的功能。即使僅僅弄清楚是否可以刪除一個功能也是非常棘手的工作,需要大量的遙測來量化有多少網頁會改變他們的行為。但是,如果功能足夠不安全,對使用者有害,或者很少使用,則可以完成此操作。
<applet>
,<keygen>
和 showModalDialog()
都是從 Web 平臺成功刪除的錯誤 API 的示例。
為什麼不修復 MooTools?
修補 MooTools 以便它不再擴充套件內建物件是個不錯的主意。但是,它並沒有解決手頭的問題。即使 MooTools 釋出補丁版本,所有使用它的現有網站也必須更新,這樣相容性問題才能消失。
能不能只更新網站中使用的 MooTools 副本?
在理想情況下 MooTools 會釋出一個補丁,每個使用 MooTools 的網站都會在第二天神奇地更新。問題解決了,對吧?!
不幸的是,這是不現實的。即使有人以某種方式識別了整套受影響的網站,也可以設法找到每一個網站的聯絡資訊,成功地與所有網站所有者聯絡並說服他們全部執行更新(這可能意味著重構他們的網站完整的程式碼庫),整個過程最多需要幾年的時間。
請記住,這些網站很多都是舊的,可能無法維護。即使維護人員仍然在身邊,也可能他們不是像您一樣的高技能 Web 開發人員。由於網路相容性問題,我們不能指望每個人都去改變他們已經執行了七八年的網站。
TC39 的工作流程是什麼樣的?
JavaScript 語言基於 ECMAScript 標準,TC39 是負責 JavaScript 語言更新發展的委員會
“Smoosh門”事件使得一些人誤認為“TC39 想要把 flatten
重新命名為 smoosh
”,但這是一個沒有很好溝通的笑話。重新命名提案等重大決策不會被輕視,不會被單個人採納,並且絕對不會在 GitHub 的評論上完成。
TC39 對於功能提案有著清晰得分級過程。ECMAScript 提案及其任何重大變更(包括方法更新)在 TC39 會議期間進行討論,並且需要整個委員會批准後方可正式提交。在這種情況下 Array.prototype.flatten
提案已經經歷了好幾個階段的討論,一直到 Stage 3,表明該功能已準備好在 Web 瀏覽器中實現。實施過程中出現其他規範問題很常見。在這種情況下,最重要的反饋意見是在試圖釋出它之後才有的:該特性在當前狀態下打破了 Web。即使瀏覽器釋出新功能後 TC39 的流程並沒有結束,就是因為這些難以預測的問題。
TC39 以協商一致的方式運作,這意味著委員會必須就任何新的變化達成一致。即使 smoosh
是一個嚴肅的建議,委員會成員似乎也可能會反對,而是贊成使用更常見的名稱,例如 compact
或 chain
。
把 flatten
重新命名為 smoosh
(即使它不是一個笑話)從未在 TC39 會議上討論。因此,關於這個問題的官方 TC39 立場目前是未知的。在下次會議達成共識之前,沒有任何一個人可以代表所有 TC39 發言。
TC39 會議通常由具有高度多樣化背景的人士出席:一些人擁有多年的程式語言設計經驗,另一些人使用瀏覽器或 JavaScript 引擎工作,越來越多的 JavaScript 開發人員社群參與者。
接下來發生什麼?
下一次 TC39 會議將於本週舉行。議程中有一項討論 flatten
及其網路相容性問題。希望在會議結束後我們會更多地瞭解下一步。