本文探討如何在 JavaScript 代碼中運用強類型語言的技巧。這些技巧不僅能減少代碼錯誤,還能縮減代碼量。雖然本文以 JavaScript 為例,但這些技巧也適用於大多數弱類型語言。
關鍵要點:
JavaScript 類型系統
首先快速回顧一下 JavaScript 數據類型系統的工作原理。 JavaScript 將其值分為兩類:
<code class="language-javascript">var a = []; var b = a; a.push('Hello');</code>
當我們更改 a 時,變量 b 也會發生變化,因為它們都引用同一個數組。所有引用類型的工作方式都是如此。 JavaScript 不會強制執行任何類型,這意味著任何變量都可以在任何時候保存任何數據類型。本文其餘部分將討論此方法的缺點,以及如何應用強制執行類型的語言中的簡單技巧來編寫更好的 JavaScript 代碼。
引入一致類型規則
一致類型規則在理論上很簡單:所有值都應該只有一種類型。強類型語言在編譯器級別強制執行此規則,它們不允許您隨意混合和匹配類型。弱類型賦予我們很大的自由度。一個常見的例子是將數字連接到字符串中。您不需要執行像在 C 等語言中那樣繁瑣的類型轉換。別擔心,我不會告訴您放棄所有便利。一致類型規則只需要您注意變量和函數的行為方式,這樣您的代碼就會得到改進。
首先,讓我們看看該規則如何應用於變量。它非常簡單:您的變量應該始終只有一種類型。
<code class="language-javascript">var a = []; var b = a; a.push('Hello');</code>
上面的示例顯示了問題所在。此規則要求我們假裝此示例中的最後一行代碼會拋出錯誤,因為當我們第一次定義變量 text 時,我們賦予它字符串類型的 value,現在我們正在為其賦值一個數字。一致類型規則意味著我們不允許這樣更改變量的類型。當您的變量一致時,更容易推斷您的代碼。它尤其有助於更長的函數,在這些函數中,很容易忽略變量的來源。當在不遵守此規則的代碼庫中工作時,我意外地導致了許多錯誤,因為我看到聲明了一個變量,然後假設它會保持相同的類型——因為讓我們面對現實,這有意義,不是嗎?通常沒有理由將不同的類型分配給同一個變量。
相同的規則也適用於此。函數的參數也應該保持一致。一個錯誤的例子:
<code class="language-javascript">var text = 'Hello types'; // 错误!不要这样做! text = 1;</code>
這裡有什麼問題?通常認為,根據類型檢查來分支邏輯是不好的做法。對此有一些例外,但通常更好的選擇是使用多態性。您應該努力確保函數參數也只有一種類型。如果您忘記考慮不同的類型,它會減少出現問題的可能性,並使代碼更簡單,因為您不必編寫代碼來處理所有不同類型的案例。編寫 sum 函數的更好方法如下:
<code class="language-javascript">function sum(a, b) { if (typeof a === 'string') { a = 1; } return a + b; }</code>
然後,您在調用代碼中而不是在函數中處理類型檢查。從上面可以看出,該函數現在簡單多了。即使我們必須將類型檢查移動到其他地方,我們越早在代碼中執行它們,效果就越好。我們將在本文後面討論類型檢查和 typeof 的使用,包括如果使用不當,類型檢查如何輕鬆級聯。
這與其他兩個相關:您的函數應該始終返回相同類型的 value。我們可以在這裡舉 AngularJS 的一個例子。 AngularJS 提供了一個將文本轉換為小寫的函數,稱為 angular.lowercase。還有一個標準函數,String.prototype.toLowerCase。我們可以比較它們的行為來更好地理解規則的這一部分:
<code class="language-javascript">function sum(a, b) { return a + b; }</code>
變量 a 將包含您期望的內容:“hello types”。但是,b 將包含什麼?它將是一個空字符串嗎?函數會拋出異常嗎?或者它可能只是 null?在這種情況下,b 的 value 為 null。請注意,立即很難猜測結果是什麼——我們一開始就有三種可能的結果。對於 Angular 函數,對於非字符串值,它將始終返回輸入。現在,讓我們看看內置函數的行為:
<code class="language-javascript">var a = []; var b = a; a.push('Hello');</code>
第一次調用的結果相同,但第二次調用會拋出異常。內置函數遵循一致類型規則,並且不允許不正確的參數類型。返回值也始終是一個字符串。所以我們可以說內置函數更好,但您可能想知道究竟是如何做到這一點的?讓我們考慮一下此類函數的典型用例。我們在代碼中的某個點使用它來將字符串轉換為小寫。正如 JavaScript 代碼中經常發生的那樣,我們不能 100% 確定我們的輸入是否總是字符串。沒關係,因為我們是優秀的程序員,我們假設我們的代碼沒有任何錯誤。如果我們使用不遵守這些規則的 AngularJS 函數會發生什麼?非字符串值會毫無問題地通過它。它可能會通過幾個函數,我們甚至可能會通過 XMLHttpRequest 調用發送它。現在錯誤的值在我們的服務器中,並最終進入數據庫。您可以看到我的意思,對吧?如果我們使用遵守規則的內置函數,我們會在那時立即發現該錯誤。每當您編寫函數時,請確保其返回的類型一致。下面顯示了一個不好的例子:
<code class="language-javascript">var text = 'Hello types'; // 错误!不要这样做! text = 1;</code>
同樣,與變量和參數一樣,如果我們有這樣的函數,我們就無法對它的行為做出假設。我們將需要使用 if 來檢查返回 value 的類型。我們可能會在某個時候忘記它,然後我們手中就會出現另一個錯誤。我們可以通過多種方式重寫它,以下是一種解決此問題的方法:
<code class="language-javascript">function sum(a, b) { if (typeof a === 'string') { a = 1; } return a + b; }</code>
這次我們確保所有路徑都返回一個字符串。現在更容易推斷函數的結果了。
null 和 undefined 是特殊的
到目前為止,我們實際上只討論了原始類型。當涉及到對象和數組時,您應該遵循相同的規則,但需要注意兩個特殊情況。處理引用類型時,有時需要指示沒有 value。一個很好的例子是 document.getElementById。如果找不到匹配的元素,它將返回 null。這就是為什麼我們將 null 視為與任何對像或數組共享類型的原因,但僅限於那些對像或數組。您應該避免從可能返回 Number 等原始值的函數中返回 null。 undefined 也可以被認為是引用的“無值”。出於大多數目的,它可以被視為等於 null,但由於其在其他面向對象語言中的語義,null 更為可取。
使用數組時,您還應該考慮空數組通常比 null 更好。儘管數組是引用類型,您可以使用 null 與它們一起使用,但通常返回空數組更有意義。讓我們來看下面的例子:
<code class="language-javascript">var a = []; var b = a; a.push('Hello');</code>
這可能是數組最常見的用法之一。您從函數中獲取一個數組,然後對其進行迭代以執行其他操作。如果 getListOfItems 在沒有項目時返回 null,上面的代碼會發生什麼?它會拋出錯誤,因為 null 沒有長度(或任何其他屬性)。當您考慮像這樣使用數組,甚至是 list.forEach 或 list.map 時,您可以看到當沒有值時返回空數組通常是一個好主意。
類型檢查和類型轉換
讓我們更詳細地了解類型檢查和類型轉換。您應該何時進行類型檢查?您應該何時進行類型轉換?
類型轉換的第一個目標應該是確保您的值的類型正確。數值應該是數字而不是字符串,依此類推。第二個目標應該是您只需要轉換一次 value。進行類型轉換的最佳位置是在源處。例如,如果您從服務器獲取數據,則應該在處理接收到的數據的函數中進行任何必要的類型轉換。從 DOM 解析數據是一個非常常見的錯誤開始出現的地方。假設您有一個包含數字的文本框,並且您想讀取它。或者,它可能只是某個 HTML 元素中的屬性,它甚至不必是用戶輸入。
<code class="language-javascript">var text = 'Hello types'; // 错误!不要这样做! text = 1;</code>
由於您可以從 DOM 獲取的值通常是字符串,因此在讀取它們時進行類型轉換非常重要。在某種程度上,您可以將其視為模塊的“邊緣”。數據通過讀取它的函數進入您的 JavaScript 模塊,因此它必須將數據轉換為正確的格式。通過在模塊邊緣進行類型轉換,我們確保內部不必處理它。這在很大程度上減少了隱式類型強制轉換導致錯誤的可能性。它還允許我們編寫更少的代碼,因為我們不允許錯誤的值從邊緣進入模塊。
<code class="language-javascript">function sum(a, b) { if (typeof a === 'string') { a = 1; } return a + b; }</code>
您應該只將 typeof 用於驗證,而不是根據類型分支邏輯。對此有一些例外,但這是一個很好的經驗法則。讓我們來看兩個例子:
<code class="language-javascript">function sum(a, b) { return a + b; }</code>
這是一個使用 typeof 進行驗證的示例。我們確保傳遞給函數的參數是正確的類型。但是,下面的示例顯示了根據類型分支邏輯的含義。
<code class="language-javascript">var a = []; var b = a; a.push('Hello');</code>
不要這樣做。雖然有時可能需要這樣做,但這通常是設計不良的標誌。如果您發現自己經常執行這種邏輯,那麼您可能應該在代碼的早期將 value 轉換為正確的類型。如果您最終在代碼中使用了大量 typeof,這可能表示您可能需要轉換要比較的值。類型檢查通常會擴散,這通常是關於類型的設計不良的標誌。如前所述,您應該嘗試在模塊邊緣進行類型轉換,因為它允許您避免 typeof 級聯。如果您儘早進行轉換,則之後調用的任何函數都不必進行類型檢查或類型轉換。這也適用於對象:如果您發現自己使用 instanceof 進行大量檢查或檢查對像上是否存在屬性,則表示您可能應該以不同的方式構造數據。與 typeof 相同的規則也適用於 instanceof:您應該嘗試避免它,因為它可能是設計不良的標誌。但是,有一種情況是不可避免的:
<code class="language-javascript">var text = 'Hello types'; // 错误!不要这样做! text = 1;</code>
如果您的代碼需要對異常類型進行特定處理,instanceof 通常是一個不錯的選擇,因為 JavaScript catch 不允許像其他一些語言那樣按類型區分。在大多數其他情況下,您應該嘗試避免 instanceof。
結論
正如我們所發現的,JavaScript 的弱類型為我們帶來了極大的自由,但我們也必須謹慎行事。否則,我們將最終陷入一個類型混亂的局面,沒有任何意義。通過確保我們的代碼遵循一致類型規則,我們可以避免很多麻煩。當我們知道類型時,更容易推斷我們的代碼。我們不必在代碼中構建許多類型檢查來防止錯誤。如果您沒有使用強類型語言,這似乎很困難,但在您需要調試或維護代碼時,它會得到很大的回報。有關該主題的更多信息,我建議您查看 TypeScript。它是一種類似於 JavaScript 的語言,但它為該語言添加了更強的類型語義。它還有一個編譯器,當您嘗試執行一些愚蠢的操作(例如混合和匹配類型)時,它會吐出錯誤。
關於 JavaScript 中強類型語言的常見問題解答 (FAQ)
強類型語言是指變量綁定到特定數據類型的語言,任何與該類型不一致的操作都會導致錯誤。示例包括 Java 和 C 。另一方面,像 JavaScript 這樣的弱類型語言允許變量保存任何類型的數據,並且在必要時會自動進行類型轉換。如果處理不當,這種靈活性可能會導致意想不到的結果。
JavaScript 本身是一種弱類型語言,但您可以使用 TypeScript(JavaScript 的靜態類型超集)來強制執行強類型。 TypeScript 為 JavaScript 添加了靜態類型,允許在編譯時進行類型檢查。這有助於在開發過程的早期發現錯誤。 “嚴格模式”是 JavaScript 中的另一個方法,它通過為不安全的動作拋出錯誤來使語言的行為更像強類型語言。
強類型語言提供了一些好處。它們可以幫助在編譯時而不是運行時捕獲錯誤,這可以節省大量調試時間。它們還使代碼更具自文檔性,因為變量的數據類型清楚地表明了它們的使用方式。此外,它們可以使代碼更可預測且更容易推斷,因為它們可以防止意外的類型轉換。
雖然 JavaScript 默認情況下不是強類型語言,但您可以使用 TypeScript 或 Flow 等工具來強制執行強類型。這些工具為 JavaScript 添加了靜態類型,允許在編譯時進行類型檢查。這有助於在開發過程的早期發現錯誤。但是,需要注意的是,這些工具不會改變 JavaScript 的底層性質;它們只是在它之上提供了一層類型安全性。
TypeScript 是由 Microsoft 開發的 JavaScript 靜態類型超集。它為 JavaScript 添加了靜態類型,允許在編譯時進行類型檢查。這有助於在開發過程的早期發現錯誤。 TypeScript 代碼被轉換為 JavaScript,這意味著它可以在任何 JavaScript 運行的地方運行。它與 JavaScript 完全兼容,可以使用 JavaScript 的所有功能。
雖然強類型語言提供了許多好處,但它們也有一些缺點。它們可能更冗長,需要更多代碼才能完成與弱類型語言相同的任務。它們還需要一個編譯步驟,這可能會減慢開發過程。此外,它們可能不太靈活,並且對於某些任務(例如處理動態數據)更難使用。
“嚴格模式”是 JavaScript 中的一項功能,它使語言的行為更像強類型語言。它會為不安全的動作拋出錯誤,例如為只讀屬性賦值或在聲明之前使用變量。這有助於在開發過程的早期發現錯誤。要啟用“嚴格模式”,只需在 JavaScript 文件或函數的頂部添加“use strict;”一行即可。
類型強制是 JavaScript 中的一項功能,其中語言在必要時會自動將一種數據類型轉換為另一種數據類型。例如,如果您嘗試添加數字和字符串,JavaScript 將在執行加法之前將數字轉換為字符串。雖然這很方便,但如果處理不當,它也可能導致意想不到的結果。
避免 JavaScript 中類型強制的一種方法是使用“嚴格模式”,它會為不安全的動作拋出錯誤。另一種方法是使用“===”運算符而不是“==”運算符進行比較,因為前者不執行類型強制。此外,您可以使用 TypeScript 或 Flow 等工具為 JavaScript 添加靜態類型,這有助於在編譯時捕獲與類型相關的錯誤。
強類型語言在 JavaScript 中的使用可能會在未來增加,因為它們提供了許多好處,例如儘早捕獲錯誤並使代碼更可預測。 TypeScript 和 Flow 等工具越來越流行,並且正在開發新的工具和功能以使 JavaScript 更具類型安全性。但是,JavaScript 的靈活性和動態性將使其繼續成為許多開發人員的首選。
以上是JS中強烈打字的語言借用技術的詳細內容。更多資訊請關注PHP中文網其他相關文章!