我們擅長商業策略與用戶體驗的完美結合。
歡迎瀏覽我們的案例。
從 1995 年開始,本文作者 Dr.Axel Rauschmayer 就專門從事 JavaScript 和 Web 開發,已經有 30 多年了。2010 年,他獲得慕尼黑大學信息學博士學位。自 2011 年以來,他一直在 2ality.com 寫博客,并寫了幾本關于 JavaScript 的書,比如《JavaScript for impatient programmers》、《Deep JavaScript: Theory and techniques》等。今天這篇文章就來自于他的博客,介紹了在 JavaScript 命名沖突時,現有代碼如何強制對提議的功能進行重命名。
不斷發展的 JavaScript:不要破壞 web!
JavaScript 的一個發展核心原則就是"不要破壞 Web":在將新特性添加到語言中后,所有現有代碼都必須能夠繼續運行。
這樣有一個壞處,就是不能從語言中刪除現有的 quirks。但這樣做益處多多,比如舊的代碼可以繼續運行,而且升級到新的 ECMAScript 版本很簡便等等。
在為新特征(如方法名稱)選擇名稱時,需要進行一個重要的測試,即在瀏覽器的 nightly 版本(早期預發布版本)中添加該特征,并檢查是否有任何網站出現錯誤。
接下來將介紹過去案例中的的四個沖突源,當產生這四種沖突時,就必須重命名特征。
沖突源1:向內置原型添加方法
在 JavaScript 中,我們可以通過改變其原型來為內置值添加方法:
神奇的是,語言可以通過這種方式改變。這種運行時的修改被稱為猴子補丁(monkey patch)。
什么是猴子補丁?
如果我們給內置原型添加方法,我們就是在運行時修改一個軟件系統。這樣的修改被稱為猴子補丁。簡單來說,對其含義有兩種可能的解釋。
這個叫法起源于 Zope 框架,人們在修正 Zope 的 Bug 的時候經常在程序后面追加更新部分,這些被稱作是“雜牌軍補丁(guerilla patch)”,后來 guerilla 就漸漸的寫成了 gorllia ((猩猩),再后來就寫了 monkey (猴子),所以猴子補丁的叫法是這么莫名其妙的得來的。
另一種說法是,它指的是搞亂(monkeying about)代碼。
反對改變內置原型的原因
對任何類型的全局命名,都會存在名稱沖突的風險。如果有解決沖突的機制,就能規避風險。例如:
全局模塊是通過裸模塊指定器或 URLs 來識別的。前者之間的名稱沖突可以通過 npm 注冊表來解決。后者之間的名稱沖突可以通過域名注冊處來解決。
可以通過將符號添加到 JavaScript 中,以避免方法之間的名稱沖突。例如,任何對象都可以通過添加一個鍵為 .NET 的方法而成為可迭代的。由于每個符號都是唯一的,所以這個鍵永遠不會與任何其他屬性鍵 .Symbol.iterator 發生沖突。
然而,帶有字符串鍵的方法會導致名稱沖突:
不同的庫可能會對他們添加到 .Array.prototype 的方法使用相同的名字。
如果一個名字已經被某個庫使用了,那么這個名稱就不能用于命名 JavaScript 標準庫的一個新特性。
具有諷刺意味的是,謹慎地添加一個方法可能會適得其反:
我們會檢查一個方法是否已經存在。如果沒有,我們就添加它。
如果我們要實現一個 polyfill(模擬原生 Web 平臺功能),將新的 JavaScript 方法添加到不支持它的引擎中,那么這個技術就能發揮作用。(順便說一下,這是修改內置原型的一個合法用例。也許是唯一的一個)。
然而,如果我們對一個普通庫的方法使用這種技術,然后 JavaScript 獲取具有相同名稱的方法,那么這兩種實現的工作方式就不一樣了,并且使用庫方法的所有代碼在使用內置方法時都會中斷。
必須更改名稱的原型方法示例
ES6 的方法最初是與 JavaScript 框架 MooTools.String.prototype.includes () .contains ()全局添加的方法相沖突。
ES2016 的方法最初是與 MooTools.Array.prototype.includes () .contains ()添加的方法相沖突。
ES2019 的方法最初是和 MooTools.Array.prototype.flat () .flatten ()相沖突。
修改內置原型并不總是糟糕的
你可能會對 MooTools 的創建者的粗心大意感到疑惑。但是,向內置原型添加方法并不總是糟糕的。在 ES3(1999 年 12 月)和 ES5(2009 年 12 月)之間,JavaScript 是一種停滯不前的語言。MooTools 和 Prototype 等框架改進了它。這些方法的缺點只有在 JavaScript 的標準庫再次增加之后才會凸顯出來。
沖突源2:檢查一個屬性的存在
ES2022 的方法最初是 .NET 的。因為以下庫檢查屬性以確定對象是否是一個 HTML 集合(而不是一個數組),所以它必須被重新命名:Magic360、YUI 2、YUI 3.Array.prototype.at () .item () .item
沖突源3:檢查全局變量是否存在
自 ES2020 以來,我們可以通過 globalThis 訪問全局對象。Node.js 一直使用該名稱來實現此目的。最初的計劃是為所有平臺標準化該名稱 .global
然而,以下模式經常被用來確定當前平臺:
如果瀏覽器也有一個名為 .global 的全局變量,這種模式(以及類似的模式)就會失效。因此,標準化的名稱被改為 .globalglobalThis。
沖突源4:通過創建局部變量 with 語句
JavaScript 的聲明 with 語句
長期以來,人們一直不鼓勵使用 JavaScript 的 with 語句,甚至在 ES5 中引入的嚴格模式中也被定為非法。在其他地方,嚴格模式在 ECMAScript 模塊中是活躍的。
該語句將一個對象的屬性變成局部變量:with
由 with 語句引起的沖突
框架 Ext.js 使用的代碼與下面的片段有些相似點:
當 ES6 方法被添加到 JavaScript 中時,如果用 Array(B行)來調用它,它就會失效。該語句將 Array 的所有屬性變成了局部變量。其中一個是繼承的屬性。因此,A行中的語句已記錄,不再是參數
Array.prototype.values () myFunc () withvalues.valuesArray.prototype.valuesvalue
Unscopables:防止 with 導致的沖突
公共符號Symbol.unscopables 允許對象隱藏語句中的某些屬性。它只在標準庫中使用一次,對于 Array.prototype:with
結論
以上提出了 JavaScript 結構與現有代碼發生名稱沖突的四種方式:
向內置原型添加方法
檢查屬性是否存在
檢查全局變量是否存在
創建局部變量 with
沖突的某些來源很難預測,但存在以下一些一般規則:
不要更改全局數據。
避免檢查是否存在全局數據。
請注意,內置值將來可能會獲得其他屬性(自己的或繼承的屬性)。
對于庫來說,為 JavaScript 值提供功能的最安全方法是通過函數。如果 JavaScript 得到一個 pipe operator,我們也可以像方法一樣使用它們。
(邯鄲微信平臺)