# Day 2：資料型態的夢魘——動態型別加弱型別(1)

「JavaScript 是動態型別」，很多人會說知道。

「JavaScript 是弱型別」，也有很多人會說知道。

「JavaScript 是動態型別加弱型別」，可能很多人就不那麼肯定了。

許多人把這兩件事混淆，其實這是兩個不同的觀念。那麼，究竟**動態型別**和**弱型別**有什麼差別？

**動態型別加上過於寬鬆的弱型別，是我認為 JavaScript 最叫人頭痛的萬惡根源**，因此選擇以這部分做切入。

本篇文章先來看何謂靜態型別與動態型別。

## 靜態型別 vs. 動態型別

### 靜態型別的例子

我們先從比較容易理解的靜態型別來看。以 Java 為例：

```java
int x;
```

這是一個很簡單的變數宣告，沒有給初始值。請問 `x` 這個變數裡未來會放什麼類型的值？

答案是整數。

為什麼我能這麼篤定？因為在宣告 `x` 時，使用了 `int` 這個關鍵字，明確宣告了變數 `x` 是個整數變數。

如果我在使用過程，企圖在 `x` 內放入非整數的值，例如：

```java
int x;
x = "Hello";
```

編譯時就會得到以下錯誤：

```
HelloWorld.java:5: error: incompatible types: String cannot be converted to int
```

這就是典型的靜態型別。這類語言在型別的管理上十分嚴謹，在語法撰寫時就會要求對變數型別有明確定義，有時讓人覺得囉嗦；但相對的，一旦有變數誤用或資料型態上的 Bug，在編譯時期就能發現，降低執行時期的風險。

編譯式語言多半是靜態語言，Java 和 C# 是其中的代表。

### 動態型別的例子

用另外一個語言 Lua 來舉例：

```
local x
x = 123
print(x)
x = "Hello"
print(x)
```

可以發現在用 `local` 宣告變數 `x` 時，沒有事先明確指定 `x` 的型別是什麼，能放入任意類型的資料；甚至中間還一度改變指派值的資料型態，一開始放的是整數 `123`，後來改放字串 `"Hello"`，程式依然可以運作。

這就是一個動態語言的例子，這類語言在程式執行過程才會進行資料型態的檢查或確認，因此直譯式語言都是動態語言。如 Python、PHP、Ruby，還有我們的主角 JavaScript，都屬於此類語言。

這類語言在程式編寫時，不用花太多心思在宣告型別的語法上，簡潔而靈活，可以在過程依需求任意改變型別，做到十分靈活的變數處理；但相對缺乏對當前變數的型別限制，當執行到某一行程式時，無法絕對肯定 `x` 變數現在放了什麼類型的值，容易造成非預期的執行可能性，導致非預期的執行結果。也就是容易埋下 Bug！

### 宣告時沒有指定型別，就是動態型別？

從上面兩個例子可以明顯看出動態語言和靜態語言的差別。可能會覺得：「很好辨認嘛，看宣告變數時有沒有指定型別，就知道是不是動態語言」。

這個理解是不精確的。

因為有的靜態語言在宣告時，也不需要指定型別，是透過**隱性推導**的方式來確認型別，例如 Ocaml、Haskell。

像傳統 C# 是典型的靜態語言，但在 C# 4.0 添加了新特性，允許宣告時不指定型別，但不改變 C# 骨子裡是靜態語言的事實。

例如傳統 C# 的撰寫方式如下：

```
int n = 123;
Console.WriteLine(n);
```

C# 4.0 後允許用以下方式撰寫：

```
var n = 456;
Console.WriteLine(n);
```

看起來很像動態語言的寫法。但如果企圖在過程放入不同型別的值，在編譯時一樣會被檢查出來，也就是說 C# 骨子還是靜態語言，不允許動態語言的使用方式，比如使用過程任意改變型別。

例如以下例子，將發生 `Cannot implicity convert type 'string' to 'int'` 的編譯錯誤(Compilation Error)：

```
var n = 456;
Console.WriteLine(n);
n = "Hello";
```

可能會有疑問：既然沒有指定型別，編譯器怎麼知道 `n` 的型別應該是什麼？

以 C# 4.0 來說，`n` 的型別就是**初始值的型別**，所以宣告時必須被初始化，否則會有 `Implicity-typed variables must be initialized` 的編譯錯誤，如以下例子：

```
var n;
n = 123;
Console.WriteLine(n);
```

這種不在語法上指定，而靠隱性推導的方式，稱為**隱性型別(implicity typed)的靜態語言**。

C# 4.0 添加了這個特性，算是吸收了動態語言在宣告語法上簡便的優點，同時仍保有靜態語言的穩定性。

### 總結靜態語言和動態語言的定義和差別

**靜態語言(Statically Typed Languages)：**

* 型別檢查(Type Checking)發生在**編譯時期(Compile Time)**。
* 程式撰寫時必須使用明確的型別宣告。
* 型別一旦宣告後，在執行時期時無法任意更換型別，否則會發生錯誤。

**動態語言(Dynamically Typed Languages)：**

* 型別檢查(Type Checking)發生在**執行時期(Runtime)**。
* 程式撰寫時不用明確的型別宣告。
* 執行時，變數能任意更換型別。

**而靜態語言又分為：**

* 顯性型別(explicitly typed)：型別是語法宣告的一部份，從語法就能得知。
* 隱性型別(implicity typed)：型別是透過編譯過程的推導得知。

## Reference

* [靜態語言 vs. 動態語言的比較](https://blog.sina.com.tw/dotnet/article.php?entryid=614009)
* [動態語言與靜態？直譯與編譯？強型別與弱型別？](https://millenniummeetonce.blogspot.com/2018/04/blog-post_5.html)
* [弱类型、强类型、动态类型、静态类型语言的区别是什么？](https://www.zhihu.com/question/19918532)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://something-about-js-book.onejar99.com/day02.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
