這篇主要是紀錄我是怎麼理解這些名詞的。

因為關於程式語言的部分,我目前只有寫過 JavaScript,所以對於其他程式語言比較沒辦法體悟,這篇會從我使用 JavaScript 的角度來去理解!

在都還沒研究時,我其實就已經知道 JavaScript 分別是哪幾種,但還不明白差異:

JavaScript =>

  1. 動態語言
  2. 弱型別
  3. 靜態作用域

動態語言、靜態語言

用一句話來解釋就是在執行的時候可以改變結構的是動態語言,如果不能的話那就是靜態語言了。

這塊需要釐清以及容易卡住的地方我覺得是「執行」和「編譯」的名詞。

假如今天是在 vscode 寫 code,使用的程式語言是動態語言,像是 JavaScript,執行的話我會理解成,我印出了一個值,比如說 console.log(x),那要看到這個 x 是多少的話,就得對它做「執行」的動作,我才能知道我到底印出了多少,不管是在瀏覽器上,還是在 node.js 上,而我在寫 console.log(x)的時候,就是同時在做「編譯」這件事情,我在 vscode 寫程式碼的過程就是編譯時的過程。

理解了這塊之後,再來回頭看動態語言跟靜態語言的差異。

執行的時候可以改變結構,也就是代表在編譯時都不會去管,用類別檢查來做個舉例:

寫 JavaScript 時,其實執行時才會進行類別檢查,編譯時怎麼寫都不會管。

test

x 值的類別是什麼呢?

在第一個賦值於 5 的時候,印出來的值會是 number 的型別,如果後來再去賦予一段字串,那它印出來的型別就會變成是 string。

也就是說,假如要印出一個變數,得在執行的時候才會決定,編譯時什麼都不知道,這就是動態語言的奇妙之處,所以在寫 JavaScript 的時候,可以隨心所欲的去改變自己的變數,因為動態語言的特性下其實可以看成變數是任何的類型,不會產生編譯錯誤,在 vscode 執行程式前,不會發生報錯的狀況。

而靜態語言在這塊就會嚴謹很多,一樣用型別的檢查這件事情來做舉例。

靜態的特性就是會在編譯時進行對變數或是函式進行類別的檢查,必須要在執行前,就知道每一個變數是什麼類別了,而不是在執行的時候才知道,所以使用前就得要先宣告,要是像上面一樣,在執行前沒有給變數一個明確是什麼型別的話,就會在編譯時報錯。

聽起來很麻煩,但這樣其實是好處多多的,因為我在寫 JavaScript 時,動態的特性讓我想寫什麼都不會編譯時出錯,但也因為如此,很多的錯誤都是要等到執行後才會發現,有時候在想如果今天 JavaScript 是一個靜態語言,那其實很多常見的錯誤就可以提前發現,所以這點上有好有壞,靜態特性可以提升可靠性,而動態特性可以獲得靈活性跟高彈性。

回到最開頭講的話,執行的時候可以改變結構的是動態語言,如果不能的話那就是靜態語言,其實也就是檢查的嚴格程度,動態的話,就是動來動去,結構定下來是在執行的時候,也就代表執行前都沒還確定下來,靜態則是不可以亂改變的人,所以執行前也就是編譯時,就會先做檢查,檢查不過就先送你報錯,看似殘忍的提早送你報錯,也比你真正在執行後報錯好,這是我想靜態語言這樣設定的心態吧。

強型別、弱型別

顧名思義這個跟「型別」有很大的關係,用一句話來形容的話,就是程式語言容忍隱性型別轉換的程度。

理解的話要先從什麼是隱性以及顯性開始說起。

隱性就是看出不來,顯性就是非常明顯的,轉型別的說的話,一個就是你偷偷的從數字變成了字串,怎麼轉換不是寫程式的人決定的,是程式本身自己覺得,自己變換過去,但顯性的話就不會發生這種事情,發生了什麼變化一定是寫程式的人自己做得決定,要從數字變成字串就得自己去轉換。

可以想像成隱性的話會有一個小幫手,為了幫助你,在他覺得應該要轉換的時候,助你一臂之力,幫你做事情,而顯性的話就沒有小幫手,都得靠自己。

而強型別來看的話,就是不允許隱性的行為,沒有小幫手,也就是得在編譯得時候就強制要求變數跟型別類型要匹配,你得一切都自己決定好,要是決定的不夠清楚的地方,就直接送你編譯報錯,而弱型別就有點像是上面提到的動態語言一樣很寬鬆,就像是有個小幫手會默默幫你,當你遇到沒有很清楚什麼型別是什麼型別的時候,小幫手會幫你自動轉換,都幫你做好決定了,編譯時這塊的匹配機制就相對十分寬鬆。

另外這塊我還有用一個整理雜物的例子來理解:

用兩種的箱子來分類雜物,一種是裡面已經分類好的箱子,箱子不能亂放,假如是分類為「衣服」的箱子,那裡面只能放衣服而已,分類為「書籍」的箱子裡面只能放書而已,被分類的明明白白,而另一種是通用的箱子,這箱子彈性很大的,想放衣服也可以,想放書本也可以,當它放入的衣服的時候,就變成放衣服的箱子,但也可以把衣服拿出來,放入書本,那它這時候就變成了放書的箱子。

而這種裡面已經分類好的箱子呢其實就很像是前面所提到的強型別,當我看到這種箱子的時候,我就知道裡面只有這個類型的物品,這樣代表我得花比較多的時間跟精力去確保每一個雜物都放在適當得箱子中。

弱型別就像是通用的箱子,它可以把任何雜物放入通用的箱子中,而不用去進行分類,這種方式在整理雜物的時候容易很多,但是若我需要使用特定的某個雜務時,就必須非常輕楚它們被放在哪個箱子中,不然很容易就會找不到它們。

JavaScript 就是弱型別語言的一個代表,也是我現在主要在寫的,上圖 JavaScript 的部分可以看到說,今天 x 是字串,而 y 是數字,那麼把兩個相加會怎麼樣呢? 最後結果是字串還是數字呢?

答案是 string,弱型別的話,在這種狀況下會有小幫手來幫你偷偷轉,不會編譯報錯,會自動變成 string 或是 number 的其中一個,會自動幫忙合理化,所以說其實弱型別的語言我覺得學起來的門檻會比較低,而且寫起來會比較輕鬆,但同時也比較難去建立一個良好的程式概念,這個部分有好有壞,對我來說 JavaScript 弱型別的特性幫我簡化了很多事情,但我得去知道它幫我偷偷簡單化了什麼步驟,而不是都讓它幫我去做不去理解,相互比較之下我相信也可以擁有好的程式觀念。

靜態作用域、動態作用域

這邊用兩句話來解釋:

靜態作用域指的是變數跟函式在宣告的時候就已經確定下來自己的作用域,
而動態作用域的作用域會隨著程式碼的呼叫而變化。

首先,維基百科上告訴我大多數現在程式設計語言都是採用靜態作用域規則,連結:
https://zh.wikipedia.org/zh-tw/%E4%BD%9C%E7%94%A8%E5%9F%9F

那麼看起來靜態作用域肯定有它的好處存在,不過其實我還沒有參透什麼情境下要使用動態作用域什麼時候要使用靜態作用域,目前已經變成靜態作用域腦了。

話題拉回來,很多人覺得很難理解,我個人覺得其實還蠻好懂的,靜態作用域所謂在宣告的時候就已經確定自己的作用域這件事情,其實也可以濃縮成兩句話,就是我自己在學 JavaScript 的 scope 時理解的兩句話:

JavaScript 的作用域跟你如何呼叫完全無關,
只跟「程式碼的位置在哪裡這件事情」有關。

用程式碼來舉個例子:

最外面宣告一個 x 的變數叫做外面的變數,然後宣告兩個函式 foo 跟 foo2,裡面分別一個把 x 印出來,一個再宣告一個 x 叫做裡面的變數,順便再呼叫 foo,然後最下面去呼叫 foo2。

那首先要是還不確定是什麼作用域的話,x 的答案有可能是外面的變數也有可能是裡面的變數,這點要先知道,但是在這邊來說的話,答案會是外面的變數。

因為靜態作用域的定義就是在宣告時就已經確定下來自己的作用域了,所以在這個例子來看的時候,宣告 foo 時,就已經存在 foo 裡面 x 的作用域的,所以會變成是會先去找 foo 裡面有沒有 x,沒有的話就會往外面找,這邊應該 foo 裡面本身沒有 x,所以是會找到外層的外面的變數,所以說不管是怎麼呼叫的,都會依造這樣的機制來看,不會變來變去,應該說不會動來動去。

而動態作用域就不ㄧ樣了,先說我沒有寫過動態作用域的程式語言,我對它的認識是看許多文章跟影片來理解的。

如果是動態作用域的話,在上面那個例子可能最後的答案就變成印出裡面的變數了,這是為什麼呢?

因為動態作用域有兩個大特點:

  • 函式被呼叫得時候才會去定義作用域的變數值
  • 變數值每一次在呼叫的時候都會重新定義

我覺得最大的差別是動態作用域在找它的作用域時,不是依靠位置去尋找,而是依照呼叫他的地方來決定,所以不同的呼叫,可能最後找到作用域就會不一樣,變化多端,我個人覺得這樣會讓我很難理解我現在變數到底是什麼,變來變去的。

結語

不同的程式語言擁有不同的個性特點,而這些特點也會影響到寫程式的思維方式或是編寫的風格。所以了結這些特點是很重要的,我也因此花了不少時間在研究這些的差異。

總之,我研究完的心得就是,每個哪一個是特別好或是特別壞的,一切都看使用需求跟情境,不用去區分優劣,但是要去了解自己使用語言的特性跟優劣處,知己知彼才會寫 code 事半功倍。

參考來源

文章類

靜態語言 / 動態語言、強型別 / 弱型別、静態作用域 / 動態作用域 | Jenifer.Code.World
程式語言的特性本質(一)靜態語言與動態語言的信任抉擇
編譯 vs. 直譯/ 靜態 vs. 動態/ 強型別 vs. 弱型別 | GrowingDNA 成長基因

影片類

十分钟理解动态编程语言、静态编程语言、编译型编程语言、解释型编程语言、强类型编程语言及弱类型编程语言 - YouTube
13-1 什麼是變數領域 Scope ? 靜態 vs 動態作用域 全域區域變數 JavaScript 網頁程式設計入門教學課程 | #安迪 TV | Andy PRO TV - YouTube
JS 靜態作用域和動態作用域 - YouTube