1 简介
主要基于 YouTube 视频 Learn GO Fast: Full Tutorial,有根据我自己的理解稍作修改。
为什么放在笔记的前端目录下呢,因为这份笔记主要是以一个前端程序员的视角出发,会大量地将 Go 的特性与 JS / TS 比较。才不是因为没想好放哪呢
也就是说,本笔记适合:
- 已有 JS/TS 基础的开发者,有点 C/C++ 基础就更好了
- 希望快速理解 Go 设计哲学的人
不适合完全零编程基础。
学了能干嘛
这里引用一段 菜鸟教程 的介绍:
Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。
对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。
相比以事件循环为核心的 Node.js,Go 在多核并发、CPU 密集型任务和服务端长期运行场景下通常更具优势。简单来说就是前端程序员学了 Go 可以写更高性能的后端了。
而且 Go 不难学,技多不压身嘛。
6 个重要特性
首先需要知道的是 Go 语言有 6 个重要特性,这些特性是 Go 的精髓,并且将会贯穿始终:
静态类型
变量的类型必须显式定义或者赋初值以自动推断;类型与变量绑定,对于已定义的变量,其类型不能变动。
类型安全
类型错误在编译期或运行期显式暴露,语言层面极少出现隐式类型转换。但也允许程序员显式进入不安全领域。
自动垃圾回收(GC)
无需手动管理内存(尽管 Go 也提供了指针
GC 行为对开发者基本透明,性能特征相对可预测。) 。 编译型语言
需要先编译为二进制机器码,然后执行。
原生并发能力
并发作为语言级能力提供(goroutine / channel
无需引入第三方库。) , 简洁
简洁性贯穿整个语言设计的始终,而且在有意识地限制语言特性数量,避免复杂语法、隐式行为和过度抽象。
Keep this in mind. 后续所有语法和设计,几乎都可以追溯到这 6 点。
强 / 弱类型?
我并不喜欢使用强 / 弱类型来描述一门语言,因为这一对概念可以说是 ill-defined,其中混杂了两个正交的维度:
- 静态类型 vs 动态类型:类型信息主要在编译期还是运行期决定和检查?
- 静态类型语言中,类型和变量绑定,类型信息主要在编译期检查
- 动态类型语言中,类型不与变量绑定,类型在运行期绑定到值上
- 类型安全 vs 类型不安全:在遇到类型错误或模糊的操作时,倾向于报错还是隐式处理?
- 类型安全语言的特性:编译期不允许跨类型模糊操作、运行期类型检查严格
- 类型不安全语言的特性:跨类型操作时隐式类型转换或隐式失败、运行期类型检查宽松
这里我们可以举几个例子:
JS:动态类型 + 类型不安全。变量无类型,运行时大量的隐式类型转换,不多说了
Python:动态类型 + 类型安全。变量无类型,但运行时类型检查严格,例如
1 + "2"就会报TypeErrorC:静态类型 + 类型不太安全。虽然有静态的类型系统,但是其设计初衷就一直是绝对信任程序员 + 0 成本抽象,因此没有运行时检查(MSVC 调试模式那种不算
会出一点奇奇怪怪的问题例如:) , cpp// a.c int x = 0; // b.c float x; void set() { x = 1.0f; } // main.c #include <stdio.h> void set(); int main() { set(); extern int x; printf("%d\n", x); }gcc a.c b.c main.c -o test可以正常过编译,但运行结果是未定义的(也就是 UB) 。 Rust:静态类型 + 类型安全。很教科书级的,Rust 用户们非常自豪的一点。
所以我在这里没有用简单的强 / 弱类型来形容 Go,而是说它静态类型 + 类型安全。但它也不是绝对的类型安全。Go 中也有类似 C++ reinterpret_cast 的不安全指针,这里我们之后再议。
环境配置
官方链接:https://go.dev/doc/install
找到你对应的平台,跟着文档安装即可。看文档应该是前端程序员的基本能力,这里不展开了。如果安装好了,在命令行执行:
go version应该是能打印出版本号的。
以及,VSCode 有官方 Go 插件。
第一个 Go 项目
Go 语言代码的扩展名是 .go。Go 的项目管理中有 module 和 package 两个概念。
- Package 是一个或多个
.go文件,可以理解为一个命名空间,是 Go 的基本编译与可见性单位,不是发布单位,在 JS 里也没有对应的概念。 - Module 是 package 组合起来形成的。一个 module 就是我们一般所说的「项目
地位类似于 JS package。Go 依赖也是以 module 为单位,正如 JS 依赖以 npm package 为单位。」 ,
| Go | JS |
|---|---|
| package | (不可比) |
| module | package |
类似于 npm init,Go module 初始化的命令是
go mod init <name>这里的 <name> 就是你项目的名称,理论上可以随便填,但是从惯例上来说这里的 <name> 直接用 GitHub 上项目的地址,例如 github.com/Linho1219/learn-go。
执行之后生成了一个 go.mod 文件,它就是 Go 的 package.json。打开看看:
module github.com/Linho1219/learn-go
go 1.24.1其中记录了 module 的名称和当前 Go 的版本。未来如果安装依赖,在 go.mod 中就会记录。
接下来我们开始写第一份 Go 代码。新建一个 main.go 文件:
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}此处的 package main 指定该文件是 main 这个 package。Go 将会从 main package 中的 main 函数开始执行。函数使用关键字 func 定义。要打印内容,使用 import 关键字引入内置包 fmt。
控制台执行 go build 就会把当前 module 编译为二进制文件。前面提到 Go 是一门编译型语言而不是 JS 那样的解释型语言,就体现在这里。Go 在最终分发时通常是一个二进制文件,不需要什么特别的运行时或者一箩筐 node_modules,这也是它对比 Node 或 Python 更适合部署的原因之一。
如果需要编译后直接执行,还可以使用 go run .,一步到位。
go run .
Hello world!这些命令的更多用法、参数不是这里的重点,网上有很多文档,可以自行查阅。
Congratulations! 这便迈出了前往 Go 世界的第一步。
应该说这套语法应该不会让人感到陌生。Go 并没有在语法形式上搞什么特例独行,形式上与 JS 一样还是一脉相承的 C 风格。接下来真正重要的也不是语法,而是 Go 的设计哲学,也就是它对值、逻辑、错误的看法。只有理解了这些才能理解 Go,否则写出来的就是 Chinglish 一般的四不像。