通俗的方式理解動態類型,靜態類型;強類型,弱類型

0 x 01 引言

今天和一個朋友討論 C++ 是強類型仍是弱類型的時候,他告訴我 C++ 是強類型的,他和我說由於 C++ 在寫的時候須要 int,float 等等關鍵字去定義變量,所以 C++ 是強類型的,我告訴他 C++ 是弱類型的他居然還嘲笑我不懂基礎。javascript

我又嘗試去問了另一個同窗 Python 是強類型仍是弱類型的時候,獲得的居然是弱類型,就由於定義變量沒有 int,float!java

而後我想找一些網上的資料試圖告訴他們他們是錯的(我是對的),結果發現網上的資料大多爲了嚴謹結果把簡單的問題(其實並不簡單)說的很複雜。好比:知乎上的一些 回答。因此用通俗的方式,以大多數程序猿(媛)所須要瞭解的知識去介紹類型系統,可是又不喪失嚴謹性就是這篇文章寫的意義。segmentfault

0 x 02 什麼是動態(靜態)類型,強(弱)類型

基礎版本數組

編譯時就知道變量類型的是靜態類型;運行時才知道一個變量類型的叫作動態類型。好比:安全

編譯器在將 int age = 18; 這段代碼編譯的時候就會把 age 的類型肯定,換言之,你不能對他進行除以 0 的操做等等,由於類型自己就定義了可操做的集合;可是像 C++ 裏常見的 auto ite = vec.iterator(); 這種也屬於靜態類型,這種叫作類型推導,經過已知的類型在編譯時期推導出不知道的變量的類型。在靜態類型語言中對一個變量作該變量類型所不容許的操做會報出語法錯誤app

可是像 var name = student.getName(); 這行 JavaScript 代碼就是動態類型的,由於這行代碼只有在被執行的時候才知道 name 是字符串類型的,甚至是 null 或 undefined 類型。你也沒辦法進行類型推導,由於 student.getName 函數簽名根本不包含返回值類型信息。後面會介紹經過一些其餘手段來給函數簽名加上類型。在動態類型中對一個變量作該變量類型所不容許的操做會報出運行時錯誤函數

不容許隱式轉換的是強類型,容許隱式轉換的是弱類型。好比:性能

在 Python 中進行 '666' / 2 你會獲得一個類型錯誤,這是由於強類型語言中是不容許隱式轉換的,而在 JavaScript 中進行 '666' / 2 你會獲得整數 333,這是由於在執行運算的時候字符串 '666' 先被轉換成整數 666,而後再進行除法運算。 優化

高級版本this

須要先介紹一些基本概念:

Program Errors(程序錯誤)

  • trapped errors:致使程序終止執行(程序意識到出錯,使用對應的錯誤處理機制),如除 0,Java 中數組越界訪問
  • untrapped errors:程序出錯後繼續執行(其實並不必定保證繼續執行,程序自己並不知道出錯,也沒有對應的錯誤處理機制),如 C 語言裏的緩衝區溢出,Jmp 到錯誤地址

Forbidden Behaviors(禁止行爲)

程序在設計的時候會定義一組 forbidden behaviors,包括了全部的 untrapped errors,可能包括 trapped errors。

Well behaved、ill behaved

  • well behaved: 若是程序的執行不可能出現 forbidden behaviors,則稱爲 well behaved
  • ill behaved: 只要有可能出現 forbidden behaviors,則稱爲 ill behaved

他們之間的關係能夠用下圖來表達:

從圖中能夠看出,綠色的 program 表示全部程序(全部程序,你能想到和不能想到的),error 表示出錯的程序,error 不只僅包括 trapped error 和 untrapped error。

根據圖咱們能夠嚴格的定義動態類型,靜態類型;強類型,弱類型

  • 強類型:若是一門語言寫出來的程序在紅色矩形外部,則這門語言是強類型的,也就是上面說的 well behaved
  • 弱類型:若是一門語言寫出來的程序可能在紅色矩形內部,則這門語言是弱類型的,也就是上面說的 ill behaved
  • 靜態類型:一門語言在編譯時排除可能出如今紅色矩形內的狀況(經過語法報錯),則這門語言是靜態類型
  • 動態類型:一門語言在運行時排除可能出如今紅色矩形內的狀況(經過運行時報錯,但若是是弱類型可能會觸發 untrapped error,好比隱式轉換,使得程序看起來彷佛是正常運行的),則這門語言是動態類型

舉個栗子:

在 Python 中執行 test = '666' / 3 你會在運行時獲得一個 TypeError 錯誤,至關於運行時排除了 untrapped error,所以 Python 是動態類型,強類型語言。

在 JavaScript 中執行 var test = '666' / 3' 你會發現 test 的值變成了 222,由於這裏發生了隱式轉換,所以 JavaScript 是動態類型,弱類型的。更爲誇張的是 [] == ![] 這樣的代碼在 JavaScript 中返回的是 true,這裏是具體的 緣由

在 Java 中執行 int[] arr = new int[10]; arr[0] = '666' / 3; 你會在編譯時期獲得一個語法錯誤,這說明 Java 是靜態類型的,執行 int[] arr = new int[10]; arr[11] = 3; 你會在運行時獲得數組越界的錯誤(trapped error),這說明 Java 經過自身的類型系統排除了 untrapped error,所以 Java 是強類型的。

而 C 與 Java 相似,也是靜態類型的,可是對於 int test[] = { 1, 2, 3 }; test[4] = 5; 這樣的代碼 C 語言是沒辦法發現你的問題的,所以這是 untrapped error,所以咱們說 C 是弱類型的。

下圖是常見的語言類型的劃分:

另外,因爲強類型語言通常須要在運行時運行一套類型檢查系統,所以強類型語言的速度通常比弱類型要慢,動態類型也比靜態類型慢,所以在上述所說的四種語言中執行的速度應該是 C > Java > JavaScript > Python。可是強類型,靜態類型的語言寫起來每每是最安全的。

0 x 03 動態類型與靜態類型的區別,如何利用好動態類型

靜態類型因爲在編譯期會進行優化,因此通常來講性能是比較高的。而動態語言在進行類型操做的時候(好比字符串拼接,整數運算)還須要解釋器去猜想其類型,所以性能很低;可是現代的解釋器通常會有一些優化措施來提高速度,拿 JavaScript 的 V8 解釋器舉個栗子:

V8 的優化過程(粗略版本)

咱們知道,像 Java / C++ 這樣的靜態類型語言對於對象通常都會有個類模板(通常調用函數的時候都是去類模板找的)。而像 V8 這種則是會在運行時建立類模板,從而在訪問屬性或調用方法的時候僅須要計算該屬性在類模板中的偏移就能夠了;傳統的 JavaScript 對象通常是經過 Hash 或 Trie 樹實現的,可是查找的效率很低。拿一段代碼舉例:

function Point(x, y) { 
this.x = x; 
this.y = y; 
} 
var p1 = new Point(1, 2);

在使用 new 調用 Point 函數的時候會先生成一個 class0 類模板(運行時生成),執行 this.x = x 的時候會生成 class1 類模板,執行 this.y = y 的時候會生成 class2 類模板。具體的轉換過程以下圖:

爲一個對象肯定一個類模板能夠極大的提高屬性的訪問速度,類模板的肯定就是經過走圖裏的路徑(轉換路徑)。每當你增長或刪除對象的屬性的時候都會致使對象的類模板發生改變,甚至你增長的順序不一樣也會生成不一樣的類模板!

V8 若是發現一個方法被調用(傳入相同類型的參數)屢次時,會使用 JIT 將函數編譯成二進制代碼,從而提高速度。

結合 V8 總結的優化方案:

  • 不要輕易的增長刪除一個對象的屬性,對於已有的屬性儘可能作到保證類型的不變,保證隱藏類儘量被複用
  • 實例化屬性的時候儘量保證屬性添加的順序一致性,保證隱藏類和優化代碼能夠被複用
  • 儘量重複調用方法,傳的參數的個數和類型要在屢次調用時要保持一致
  • 對於數組,最好使用 push,unshift 等方法去改變數組大小,緊密的數組在 V8 中是以連續的地址存的,不要隨意去刪除數組中的元素,由於稀疏數組在 V8 中是一個 hash 表
  • V8 存儲整數用的是 4 個字節,出現大整數時將會涉及到隱式類型轉換,性能下降,所以儘可能不要讓整數超過 32 bit

0 x 04 如何避免弱類型語言所帶來的問題

弱類型語言因爲在運行時缺少類型系統,所以很容易出現類型操做上的 untrapped error;C 語言中咱們前面介紹了數組訪問越界的狀況,這裏咱們以弱類型語言 JavaScript 爲例:

  • 儘可能使用嚴格比較符號,如:===
  • 儘可能不要讓字符串與其餘類型的變量進行運算操做
  • 複雜對象不要在運算符上進行操做

0 x 05 語言類型靜態化的方案

像 JavaScript 這種動態類型的語言靜態化後對運行時的安全性,效率確定會有很大的提高的,目前有 TypeScript 這種預編譯的方案;還有就是像 flow 這樣的經過註釋來標識類型的方案。

0 x 06 總結

寫到最後,我才發現文章的標題沒取好,就這樣吧。

相關文章
相關標籤/搜索