JavaScript 社群由一個庫引發的“smoosh門”事件到底怎麼回事?

justjavac發表於2018-03-20

原文: #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.prototypeElements 是 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 是一個嚴肅的建議,委員會成員似乎也可能會反對,而是贊成使用更常見的名稱,例如 compactchain

flatten 重新命名為 smoosh(即使它不是一個笑話)從未在 TC39 會議上討論。因此,關於這個問題的官方 TC39 立場目前是未知的。在下次會議達成共識之前,沒有任何一個人可以代表所有 TC39 發言。

TC39 會議通常由具有高度多樣化背景的人士出席:一些人擁有多年的程式語言設計經驗,另一些人使用瀏覽器或 JavaScript 引擎工作,越來越多的 JavaScript 開發人員社群參與者。

接下來發生什麼?

下一次 TC39 會議將於本週舉行。議程中有一項討論 flatten 及其網路相容性問題。希望在會議結束後我們會更多地瞭解下一步。

相關文章