1. 程式人生 > 其它 >JavaScript 命名發生衝突時,如何對現有程式碼進行強制重新命名

JavaScript 命名發生衝突時,如何對現有程式碼進行強制重新命名

JavaScript 命名衝突:現有程式碼如何強制對有問題的名稱進行重新命名

有時,提議的特性(方法、全域性變數等)的名稱與現有程式碼衝突,必須更改。本次分享將解釋了這種情況是如何發生的,並列出了重新命名的功能。

 

目   錄:

 

  • 不斷髮展的 JavaScript:不要破壞網路!  
  1. 衝突來源:向內建原型新增方法  
  2. 術語:猴子補丁(monkey patch  )  
  3. 反對更改內建原型的原因  
  4. 必須更改名稱的提議原型方法示例  
  5. 修改內建原型並不總是被認為是不好的風格  
  • 衝突來源:檢查屬性的存在  
  • 衝突來源:檢查是否存在全域性變數  
  • 衝突來源:通過 with 建立區域性變數  
  1. JavaScript 的 with 語句  
  2. 由於與的衝突  
  3. Unscopables:防止由 with 引起的衝突  
  • 結論  

 

 

1、不斷髮展的 JavaScript:不要破壞網路!

發展 JavaScript 的一個核心原則是不要“破壞網路”:在語言中新增新功能後,所有現有程式碼必須繼續工作。

 缺點是無法從語言中刪除現有的怪癖。但好處是相當大的:舊程式碼繼續工作,升級到新的 ECMAScript 版本很簡單,等等。

 有關此主題的更多資訊,請參閱“針對不耐煩的程式設計師的 JavaScript”中的“不斷髮展的 JavaScript:不要破壞網路”部分。

 當為新功能(例如方法名稱)選擇名稱時,一項重要的測試是將該功能新增到瀏覽器的夜間版本(早期預釋出)中,並檢查是否有任何網站出現錯誤。接下來的部分介紹了四種衝突來源,過去就是這種情況,並且必須重新命名功能。

 

2、衝突來源:向內建原型新增方法 

 

在 JavaScript 中,我們可以通過改變它們的原型來為內建值新增方法:

// Creating a new Array methodArray.prototype.myArrayMethod = function () {  return this.join('-');};assert.equal(  ['a', 'b', 'c'].myArrayMethod(), 'a-b-c');
// Creating a new string methodString.prototype.myStringMethod = function () {  return '¡' + this + '!';};assert.equal(  'Hola'.myStringMethod(), '¡Hola!');

可以以這種方式更改語言是令人著迷的。這種執行時修改稱為猴子補丁。下一小節將解釋該術語。然後我們將看看這種修改的缺點。

 

2.1、術語:猴子補丁

 

如果我們向內建原型新增方法,我們就是在執行時修改軟體系統。這種修改稱為猴子補丁。我儘量避免使用行話,包括這個術語,但瞭解它是件好事。它的含義有兩種可能的解釋(引用百科):

 

  • 它來自“較早的術語guerrilla patch,它指的是在執行時偷偷改變程式碼 - 並且可能與其他此類補丁不相容。游擊隊這個詞,與大猩猩(或幾乎如此)諧音,變成了猴子,可能是為了讓補丁聽起來不那麼嚇人。”

  • 它“指的是用程式碼‘胡鬧’(弄亂它)。”

 

2.2、反對改變內建原型的原因

 

對於任何型別的全域性名稱空間,總是存在名稱衝突的風險。當有解決衝突的機制時,這種風險就會消失——例如:

 

  • 全域性模組通過裸模組說明符或 URL標識。通過 npm 登錄檔防止前者之間的名稱衝突。後者之間的名稱衝突可以通過域名註冊來防止。

     

  • 符號被新增到 JavaScript 以避免方法之間的名稱衝突。例如,任何物件都可以通過新增鍵為 的方法變得可迭代Symbol.iterator。由於每個符號都是唯一的,因此該鍵永遠不會與任何其他屬性鍵發生衝突。

    但是,帶有字串鍵的方法可能會導致名稱衝突:

 

  • 不同的庫可能對它們新增到的方法使用相同的名稱Array.prototype。

  • 如果某個名稱已被任何地方的庫使用,則它不能再用於 JavaScript 標準庫的新功能。在幾種情況下,這是一個問題。它們將在下一節中描述。

    具有諷刺意味的是,小心新增方法會使事情變得更糟——例如:

if (!Array.prototype.libraryMethod) {  Array.prototype.libraryMethod = function () { /*...*/ };}

 

在這裡,我們檢查一個方法是否已經存在。如果沒有,我們新增它。如果我們正在實現一個向不支援它的引擎新增新的 JavaScript 方法的polyfill ,這種技術就可以工作。(順便說一句,這是修改內建原型的合法用例。也許是唯一的用例。)

但是,如果我們將這種技術用於普通的庫方法,而 JavaScript 稍後獲得了一個同名的方法,那麼這兩種實現的工作方式不同,並且所有使用庫方法的程式碼在使用內建方法時都會中斷。

 

2.3、必須更改名稱的提議原型方法的示例 

 

  • ES6 方法String.prototype.includes()最初是,它與JavaScript 框架 MooTools.contains()全域性新增的方法發生衝突(錯誤報告)。

  • ES2016 方法Array.prototype.includes()最初.contains()與 MooTools 新增的方法發生衝突(錯誤報告)。

  • ES2019 方法Array.prototype.flat()最初.flatten()與 MooTools 發生衝突(錯誤報告,部落格文章)。

 

2.4、修改內建原型並不總是被認為是不好的風格  

 

您可能想知道:MooTools 的建立者怎麼會如此粗心?然而,向內建原型新增方法並不總是被認為是不好的風格。在 ES3(1999 年 12 月)和 ES5(2009 年 12 月)之間,JavaScript 是一種停滯不前的語言。

MooTools 和 Prototype 等框架對其進行了改進。在 JavaScript 的標準庫再次增長之後,他們方法的缺點才變得明顯。

 

3、衝突來源:檢查屬性是否存在 

ES2022 方法Array.prototype.at()最初是.item(). 它必須重新命名,因為以下庫檢查屬性.item以確定物件是否是 HTML 集合(而不是陣列):Magic360、YUI 2、YUI 3(提案中的相關部分)。

 

4、衝突來源:檢查是否存在全域性變數  

 

從 ES2020 開始,我們可以通過globalThis. Node.js 一直global為此使用這個名稱。最初的計劃是為所有平臺標準化該名稱。

但是,經常使用以下模式來確定當前平臺:

if (typeof global !== 'undefined') {  // We are not running on Node.js}

如果瀏覽器也有一個名為global. 因此,標準化名稱改為globalThis。

 

5、衝突來源:通過with  #建立區域性變數

5.1、JavaScript的with宣告  

 

長期以來一直不鼓勵使用JavaScript 的with語句,甚至在 ECMAScript 5 中引入的嚴格模式中也被認定為非法。在其他地方,嚴格模式在 ECMAScript 模組中處於活動狀態。

 

該with語句將物件的屬性轉換為區域性變數:

const myObject = {  ownProperty: 'yes',};
with (myObject) {  // Own properties become local variables  assert.equal(    ownProperty, 'yes'  );
  // Inherited properties become local variables, too  assert.equal(    typeof toString, 'function'  );}

 

5.2、由於with  #引起的衝突

 

Ext.js 框架使用的程式碼與以下片段大致相似:

function myFunc(values) {  with (values) {    console.log(values); // (A)  }}myFunc([]); // (B)

當 ES6 方法Array.prototype.values()被新增到 JavaScript 中時,myFunc()如果使用 Array 呼叫它就會中斷(B 行):該with語句將 Array 的所有屬性都values轉換為區域性變數。其中之一是繼承財產.values。因此,記錄 A 行中的語句Array.prototype.values,不再是引數values(錯誤報告 1,錯誤報告 2)。

 

5.3、Unscopables:防止由with

 

公共符號Symbol.unscopables允許物件從with語句中隱藏一些屬性。它在標準庫中只使用一次,用於Array.prototype:

assert.deepEqual(  Array.prototype[Symbol.unscopables],  {    __proto__: null,    at: true,    copyWithin: true,    entries: true,    fill: true,    find: true,    findIndex: true,    flat: true,    flatMap: true,    includes: true,    keys: true,    values: true,  });

 

unscopables 列表包括values在其旁邊或之後引入的方法。

 

結論 :

我們已經看到提議的 JavaScript 構造與現有程式碼發生名稱衝突的四種方式:

 

  • 向內建原型新增方法

  • 檢查屬性是否存在

  • 檢查全域性變數是否存在

  • 通過建立區域性變數with

  • 一些衝突來源難以預測,但存在一些一般規則:

  • 不要更改全域性資料。

  • 避免檢查全域性資料是否存在。

  • 請注意,內建值將來可能會獲得其他屬性(自己的或繼承的)。

     

庫為 JavaScript 值提供功能的最安全方法是通過函式。如果 JavaScript 有一個管道操作符,我們甚至可以像方法一樣使用它們。

 

如有相關前端方面的技術問題 ,歡迎加微信群,我會定期在群裡給大家分享最新技術和解答問題 。