Skip to content

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" 就会报 TypeError

  • C:静态类型 + 类型不太安全。虽然有静态的类型系统,但是其设计初衷就一直是绝对信任程序员 + 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

找到你对应的平台,跟着文档安装即可。看文档应该是前端程序员的基本能力,这里不展开了。如果安装好了,在命令行执行:

sh
go version

应该是能打印出版本号的。

以及,VSCode 有官方 Go 插件。

第一个 Go 项目

Go 语言代码的扩展名是 .go。Go 的项目管理中有 module 和 package 两个概念。

  • Package 是一个或多个 .go 文件,可以理解为一个命名空间,是 Go 的基本编译与可见性单位,不是发布单位,在 JS 里也没有对应的概念。
  • Module 是 package 组合起来形成的。一个 module 就是我们一般所说的「项目地位类似于 JS package。Go 依赖也是以 module 为单位,正如 JS 依赖以 npm package 为单位。
GoJS
package(不可比)
modulepackage

类似于 npm init,Go module 初始化的命令是

sh
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 文件:

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 .,一步到位。

sh
go run .
Hello world!

这些命令的更多用法、参数不是这里的重点,网上有很多文档,可以自行查阅。

Congratulations! 这便迈出了前往 Go 世界的第一步。

应该说这套语法应该不会让人感到陌生。Go 并没有在语法形式上搞什么特例独行,形式上与 JS 一样还是一脉相承的 C 风格。接下来真正重要的也不是语法,而是 Go 的设计哲学,也就是它对值、逻辑、错误的看法。只有理解了这些才能理解 Go,否则写出来的就是 Chinglish 一般的四不像。