
在釐清 Javascript 的資料型別的過程中,一不小心踏入了這個名詞坑。
一開始只是想釐清 Javascript 的資料型別,讀到 MDN 一篇關於 JavaScript 的資料型別與資料結構的文章,文中出現「動態型別」一詞來說明 Javascript 的特性,為了弄清楚該詞的意思,結果一腳踏入無底坑洞…
既然時間都花下去了,就來整理一下筆記吧。
靜態型別、編譯式語言
靜態型別的語言(statically typed language)在型別的管理上十分嚴謹,在語法撰寫時就會要求對變數型別有明確定義。
例如:
|
|
宣告變數的時候就已經規定了未來存入的值是怎樣的資料型別,如果已經用 int
宣告了 x
,就代表 x
將不能被放入字串資料,而只能放入整數。
在變數宣告與初始化階段就把變數和型別進行靜態綁定,就稱之為 「靜態型別的語言」。
編譯式語言多半會是靜態型別的語言,在編譯時期就會事先定義的型別,進行型別檢查,若出現變數誤用,資料型態不正確的話,在編譯時期就能發現,降低執行時期的風險。
C、C++、Rust、Go、Visual Basic、Swift、Obj-C、Java 和 C# 就是屬於編譯式語言。
而什麼是編譯式語言呢?
也就是程式執行前先透過編譯器(compiler)將程式碼編譯後再執行的語言,就稱為編譯式語言(Compiled language)。過程如下:
隱性型別(implicitly typed)的靜態語言
有些靜態語言在宣告時,也不需要指定型別,而是透過隱性推導的方式來確認型別。例如傳統 C# 宣告變數時需要指定型別:
|
|
但到了 C# 4.0 ,宣告變數就可以不用指定型別:
|
|
但是其型別就是初始值的型別,宣告時已經進行初始化,不能在中途任意改變型別,所以骨子裡還是靜態型別語言,稱為隱性型別(implicitly typed)的靜態語言。
不同於隱性型別是透過編譯過程的推導而得知型別,顯性型別(explicitly typed)則是將型別作為語法宣告的一部份。
動態型別、直譯式語言
動態型別的語言(dynamically typed language)相較於靜態型別的語言,在型別的處理上較鬆散靈活。
以 Javascript 為例:
|
|
宣告變數 x 時,沒有明確指定 x 的型別,代表能放入任意類型的資料;在賦值為 "Hello"
字串後,又改變賦值的資料型態放入整數 12
,即便這樣改換資料型別,但程式依然可以成功運作。
這表示動態型別的語言在程式執行過程才會進行資料型態的檢查或確認,到執行階段才能夠明確變數的型別,而且變數的型別隨時可以變化,因此直譯式語言(Interpreted language)都是動態型別語言。如 Python、PHP、Ruby、JavaScript,都屬於此類語言。
直譯式語言在執行時,才會一行一行的動態將程式碼直譯(interpret)為機器碼並執行,因此速度上會比編譯式語言要慢。其緩慢的運行速度是直譯語言最大的壞處。
動態/靜態/編譯/直譯,各型別差異
執行速度 v.s. 開發速度
雖然就執行時期的執行速度而言,編譯語言會比直譯語言來得快;然而,編譯語言的壞處是編譯時期的程式開發和除錯速度比較慢,不能像直譯語言一樣,開發一小段程序便立刻運行。
執行環境
編譯語言由編譯器進行型別和語意檢查後,編譯完成的執行檔是可以獨立運行的,程式碼能直接存取系統服務 (system service) 與 APIs,因此執行效率特別好。
但直譯語言則必須依賴一個執行環境 (execution context)才可以執行。
例如 JavaScript 只能使用瀏覽器提供的功能,它無法獨立執行 (看起來像獨立執行,實際上卻是系統自動在背後建立執行環境,如 HTML Application);或是像 Python3 程式碼需要有在有安裝 Python3 的電腦中才可以運行。
即時編譯
編譯語言開發與除錯速度慢,而直譯語言執行速度慢,若能同時改善兩者缺點、取其優點,豈不理想?!
思及此,因而發展出即時編譯的技術。這種技術混合了編譯語言與直譯語言的優點,如同編譯語言,會先把程式原始碼編譯成中介碼 (Bytecode)。到執行期時,再將中介碼直譯之後執行。
使用即時編譯技術的語言會比純編譯語言來的慢一些,但是卻又擁有直譯語言的特性。Java、C# 就是其中代表。
強型別、弱型別
靜態型別、動態型別指的是變數與型別的綁定方法。
強型別、弱型別 則是 執行階段時,型別轉換的容許程度;也可以說是語言型別系統(Type System)的編譯器或直譯器對型別檢查的寬容程度、嚴格程度、型別安全的程度。
強型別指的是「程式所定義的變數型別等於變數在執行時期的型別」。
例如,Java 為例,企圖在數字運算過程混進一個字串:
|
|
就會產生編譯錯誤,這就是屬於強型別。
但如果是 PHP 的話:
|
|
字串與數字相加的執行結果會出現 579
,是可以運行成功的。表示 PHP 的直譯器能夠容忍隱性的型別轉換,也就弱型別。
雖然常看到的靜態語言大部分是強型別。但這並不代表靜態語言一定是強型別、動態語言一定是弱型別。例如:Python 雖然是動態語言,但在型別判斷的嚴格程度上,Python 是一個強型別。
常見語言的型別特性
這個部份真的要大推 2019 iT 邦幫忙鐵人賽的你不可不知的 JavaScript 二三事系列 Day3,該文將動態靜態強弱型別整理得非常清楚!
以下也是把該文的整理結果擷取過來。
幾種常見程式語言的型別特性:
靜態語言/動態語言 | 強型別/弱型別 | 程式語言 |
---|---|---|
靜態 | 強 | Java, C# |
靜態 | 弱 | C/C++ |
動態 | 強 | Python, Ruby |
動態 | 弱 | Perl, PHP, JavaScript |
靜態語言又分顯性型別和隱性型別:
靜態顯性型別:Java, C
靜態隱性型別:Ocaml, Haskell
各語言所處的型別象限:
由以上的象限圖可以看出作為動態網頁不可缺少的角色 — Javascript,所處的位置是動態型別,而且是比 PHP 還要弱的弱型別。(哭哭)
原因在於 Javascript 在處理數學運算和字串串接的時候,算數運算子和串接運算子都是「+」,這就是為什麼 JavaScript 常常容易出現一些意想不到的相加結果。雖然 PHP 是屬於弱型別,對不同型別也會做出隱式轉換,但 PHP 進行字串串接的時候會使用「.」作為串接運算子,在數學運算和字串串接上稍稍有所區隔,因此弱型別的程度沒有 Javascript 嚴重。這部分的討論可以參考這邊。
參考資料: