> For the complete documentation index, see [llms.txt](https://gaoge011022.gitbook.io/missing-necessary-knowledge-for-cs-students/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gaoge011022.gitbook.io/missing-necessary-knowledge-for-cs-students/ji-chu-zhi-shi/bian-cheng-yu-yan/statically-typed-and-dynamically-typed.md).

# 静态类型与动态类型

”静态类型“与”动态类型“，则是对一门编程语言是否在编译期就能确定变量类型的区分。例如，考虑下面这段伪代码：

```
a = 1
a = true
```

在 Python 这样的动态类型语言中，不规定变量类型必须不变，因此类似这样的代码是正确的。而在 Java、C#、TypeScript 等编程语言中，这样的代码是不能通过编译的。这就是对静态类型语言和动态类型语言的简单区分。

可以注意到，即使在上面区分动态类型与静态类型的例子中，我也没有写下面这种代码：

```c
int a = 1;
a = TRUE;
```

这是一段 C 语言代码，并且它是正确且能够通过编译的。很多人认为一门语言在变量定义时需要声明类型（比如这里的`int`）就认为它是”强类型“的，这实际上属于完全的误解——甚至 C 语言还是静态弱类型的。判断一个变量是什么类型的，实际上根本不需要依赖于程序员显式声明类型：

```
a = 1
b = true
c = []
```

在上面这段代码中，程序员并没有为这三个变量声明类型，但编译期仍旧能够很容易地得知 a 是数字、b 是布尔量、而 c 是一个列表/数组。这种无需显式声明类型也能推导出变量类型的能力被称为”类型推断“。因此，在很多现代的静态类型编程语言中，都是可以在许多情况中使用编程语言的类型推断能力，而无需操心为变量给出一个具体的声明的：

```c++
auto h{new Holder{42}}; // Holder<int> *
auto num{42} // int
auto nums{new int[10]}; // int *
auto nums2{std::array<int, 5>{1, 2, 3, 4, 5}}; // std::array<int, 5>
std::array<int, 5> nums3{1, 2, 3, 4, 5}; // std::array<int, 5>
```

静态类型语言一个很大的优势在于，能够通过类型系统在编译阶段检查出程序中一些潜在的 BUG，例如考虑下面这段 Python 代码：

```python
def get_text_length(s):
    return len(s)

get_text_length(42)
```

这段代码只有在实际运行到了`get_text_length(42)`这一句时才会报错。而在一些较大的项目中，有些代码可能本就很难被运行到，若类似这样的代码潜藏在程序中，就会产生许多难以发现的 BUG。而在一些静态类型语言，比如 TypeScript 中，这样的问题将在编译时被检查出来：

```typescript
function getTextLength(s: string) {
  return len(s);
}

getTextLength(42);
```

在编译时，`getTextLength(42)`会直接报错，因为 TypeScript 知道`getTextLength`函数只能用于字符串。类似这样的被称作”静态类型检查“的工作提升了代码的可靠性。

再考虑下面这段 Python 代码，思考静态类型带来的另一个优势：

```python
def process_string(s):
    s.split(...)
    ...

process_string(...)
```

在你编写`process_string`函数时，你会发现当你输入了`s.`之后，编辑器并没有像你期望地给出提示，告诉你变量`s`上可以调用哪些方法，比如`split`、`join`、`isnumeric`等——因为 Python 不知道变量`s`是什么类型的，不知道它是字符串、数字、布尔量或是其他什么，因此自然不会给你这些提示。

而在 TypeScript 中，你标注了`s`的类型之后，编辑器就能根据类型给出相应的提示：

```typescript
function processString(s: string) {
  s.split(...)
  ...
}

processString(...)
```

在这里，当你输入了`s.`之后，编辑器知道变量`s`是一个字符串，因此就会给出相应的提示。

这就是静态类型的另一大优势：可以在编写代码时给出更多的提示。并且，结合之前提到的”类型推断“，实际上在很多现代的静态类型语言中，你都不怎么需要手动标注变量的类型——最常见的只是你需要标注函数中参数的类型而已。因此，对于很多程序员来说，静态类型语言反而意味着只要付出极少的代价（添上一点点类型标注）就可以获得极高的编程效率提升（得益于编辑器提供的智能提示和静态类型检查）。

而在过去，在编辑器和编译器还没有今天这么智能的时代，静态类型语言既没有类型推断，而编辑器也没有智能提示，人们普遍认为静态类型语言的开发效率要比动态类型语言更低。然而在今天，事情已经完全不一样了。

不过，动态类型语言也并非完全无法享受类型推断与智能提示带来的好处。例如 Python 自 3.5 后提供了被称作”类型提示（type hints）“的语法，你也可以通过在变量后面加个冒号，标上可选的类型，提示编辑器该变量的类型，以获得智能提示，就像 TypeScript 一样。很多动态类型编程语言都采用了这种方式，这被称为”渐进式类型“，即并不强制标上类型，而是在你认为需要的地方标上类型。只不过，相比大多数静态类型语言，大多数动态类型语言中的”渐进式类型“不够完备，缺少足够强大的类型推断能力。
