強/弱型別、動/靜型別、GC 和 VM,你真的分清楚了麼?

王子亭發表於2016-12-11

強型別 Vs. 弱型別

  • 強和弱是一個相對的概念,強是指傾向於將未定義的行為視作錯誤(Java、Python),弱是指傾向於進行隱式的轉換、忽略型別相關的錯誤(JavaScript)。
  • 很多設計得不夠嚴謹的語言,雖然大多數情況下(或者我們通常鼓勵大家這麼做)是強型別的,但也有弱型別的部分(PHP)
  • 還有的語言因為提供的抽象能力很弱,我們不得不去用弱型別的部分(C)
  • 鴨子型別(duck typing)是強弱型別的一個折中(常見於動態型別中,例如 Python),兼顧了靈活性和嚴謹性。
  • 我們認為弱型別是為了方便,而強型別是為了儘早發現錯誤。

動態型別 Vs. 靜態型別

  • 靜態型別的變數的型別是在編譯時確定的(C++、Java);動態型別的型別是在執行時確定的(JavaScript、Python),例如你可以在一個 if 的兩個分支裡給一個變數賦值不同的型別。
  • 有的動態型別語言也會新增編譯期的型別檢查(TypeScript、Python),但因為語言本身的動態性,這些檢查僅能覆蓋一部分情況。
  • 在動態型別的語言中因為型別不那麼重要,所以很多時候甚至沒有提供指定型別的語法(隱含了執行時的自動推導);而在靜態型別語言裡通常需要為變數指定型別,所以才有了編譯期自動型別推導來提供便利,而動態型別語言則做不到這一點(因為不能在編譯期確定型別,更無從推導)。
  • 我們認為靜態型別有助於在編譯時發現有關型別的錯誤,確定的型別也給了編譯期更多的優化空間;而動態型別給了開發者更高的靈活度。

垃圾回收 Vs. 無垃圾回收

  • 無 GC 是指程式碼必須自行管理申請到的記憶體並在恰當的時機釋放(Rust、C/C++);而有 GC 的語言會通過引用計數(PHP<5.3)、標記複製(V8)等方式定期查詢無用的記憶體進行釋放。
  • 標記複製的 GC 的過程通常會引起執行緒的暫停,也會花費額外的 CPU;但 GC 對於建立高層次的抽象又是必不可少的:異常、閉包等(雖然 C++ 在無 GC 的情況下也實現了這兩個特性,但也引入了非常高的複雜度)。
  • 我們認為有 GC 可以簡化對記憶體的管理,建立複雜的抽象;而無 GC 可以得到更底層的對記憶體的控制,帶來更好的效能,避免因為 GC 造成的卡頓。

虛擬機器 VS. 原生程式碼

  • 虛擬機器是指在語言和 CPU 之間還有一個用於進行翻譯的層次(JavaScript、Java);無虛擬機器是指編譯器直接生成原生程式碼給 CPU 執行(C/C++、Golang)。
  • 虛擬機器也提供了更為複雜的執行時的動態特性,但這些特性有的時候也可以在沒有虛擬機器的情況下實現(例如 C++ 的執行時型別識別、Go 的 GC)。
  • 虛擬機器可以以解釋的方式執行(Python,將程式碼視作一種資料指令來執行),也可以即時編譯(JIT)的方式來執行(V8,先將程式碼編譯到原生程式碼然後執行),有時也會混合這兩種方式(為了更快的啟動速度)。
  • 我們認為無虛擬機器的語言可以在更低的層次和其他程式互動,同時也天然地有著更好的效能;而有虛擬機器的語言則可以輕鬆地跨平臺,針對特定的架構在執行時即時編譯出更高效能的程式碼。

小結

語言 強型別 動態型別 垃圾回收 虛擬機器
C
C++ Y
Java Y Y Y
Python Y Y Y Y
JavaScript Y Y Y
PHP Y Y Y
Golang Y Y
Rust Y

參考來源:

相關文章