1. 变量与基础语法 (Variables)
Go 极其讨厌“声明了但不使用”的变量,这会导致编译错误。
核心写法
Go
package main
import "fmt"
func main() {
// 1. 标准声明
var age int = 25
// 2. 类型推断 (类似 TS 的 let age = 25)
var name = "Gopher"
// 3. 简短声明 (最常用,仅限函数内部)
isDev := true
// 4. 多变量声明
x, y := 10, 20
// 声明一个变量并初始化
var a = "RUNOOB"
fmt.Println(a)
// 没有初始化就为零值
var b int
fmt.Println(b)
// bool 零值为 false
var c bool
fmt.Println(c)
fmt.Println(age, name, isDev, x, y)
//常量
显式类型定义: const b string = "abc"
隐式类型定义: const b = "abc"
//多重赋值
const a, b, c = 1, false, "str"
//多个相同类型的声明可以简写为:
const c_name1, c_name2 = value1, value2
// 常量还可以用作枚举:
const (
Unknown = 0
Female = 1
Male = 2
)
}- 前端类比:
:=约等于let(但在 Go 中不能重新声明同名变量),全局变量必须用var。 - ⚠️ 避坑指南:
- 未使用报错: JS 中定义了不用顶多是 Warning,Go 直接报错(Compile Error)。
- 作用域: 块级作用域与 JS 类似(
if,for块内有效)。
2. 基础数据类型 (Data Types)
Go 的类型比 JS 更细致,没有 undefined,只有 nil 和零值 (Zero Value)。
- 数值:
intint8,int16,int32,int64,uint8还有无符号类型uintuint8~uint64 - 浮点数
float32,float64,complex64,complex128(JS 只有Number,Go 区分整数和浮点)。 - 字符串:
string(双引号,不可变)。 - 布尔:
bool。 - 其他数字类型
byte、rune、uintptr(无符号整型,用于存放一个指针) - 零值机制: 变量声明未赋值时,默认为:
0(数值),""(字符串),false(布尔),nil(指针/引用)。 - ⚠️ 避坑指南:
string是值类型,修改字符串会产生新副本。- Go 不支持隐式类型转换(
int不能直接加float,必须强转float64(myInt) + myFloat)。
3. 流程控制 (Flow Control)
Go 没有 while,只有 for。
If / Switch
Go
// If 支持初始化语句
if result := calc(); result > 10 {
fmt.Println("Success")
} // result 在此处失效
// Switch 不需要 break,默认 break
switch os {
case "mac":
fmt.Println("macOS")
case "linux":
fmt.Println("Linux")
default:
fmt.Println("Windows")
}For 循环
Go
// 1. 类似 for(let i=0; i<10; i++)
for i := 0; i < 5; i++ { }
// 2. 类似 while(condition)
for i < 5 { }
// 3. 死循环 (类似 while(true))
for { break }- ⚠️ 避坑指南:
if条件不需要括号(),但必须有大括号{}。switch如果需要穿透(像 JS 那样继续执行下一个 case),必须显式使用fallthrough关键字。
4. 数组 (Array) vs 切片 (Slice) —— 重中之重
这是前端最容易混淆的地方。
- 数组: 固定长度,值类型(赋值会拷贝整个数组)。很少直接使用。
- 切片 (Slice): 动态长度,引用类型(类似 JS 的 Array)。企业开发 99% 用切片。
Go
// 数组 (定长)
var arr [3]int = [3]int{1, 2, 3}
// 切片 (不定长)
var list []int = []int{1, 2, 3}
list = append(list, 4) // 类似 push,但必须接收返回值
// Make 创建
buf := make([]int, 0, 5) // 长度0,容量5- 前端类比: 切片就像 JS 的 Array,支持动态扩容。
- ⚠️ 避坑指南:
- 切片截取:
sub := list[1:3]。注意,sub和list共享底层数组。修改sub的元素,list也会变!(JS 的 slice 是浅拷贝,Go 的切片不仅浅,还连着筋)。 - Append:
append可能会导致切片扩容,从而分配新的内存地址。所以必须写list = append(list, x)。
- 切片截取:
5. Map (哈希表)
Go
// 定义
users := make(map[string]int)
users["tom"] = 18
// 检查 Key 是否存在 (重点)
age, ok := users["jack"]
if !ok {
fmt.Println("Jack 不存在")
}
// 删除
delete(users, "tom")- 前端类比: 等同于 JS 的
Object或Map。 - ⚠️ 避坑指南:
- 随机序:
range遍历 map 时,顺序是随机的(不像 JS Object 现在的实现有一定顺序)。 - 初始化: 只声明
var m map[string]int是nilmap,直接赋值会 Panic。必须用make初始化。 - 并发不安全: 多协程同时读写 map 会导致程序崩溃(需用
sync.Map或加锁)。
- 随机序:
6. 指针 (Pointer)
前端一般接触不到,但在 Go 中,为了性能和修改原数据,必须懂。
&: 取地址 (Get address)。*: 取值 (Get value from address)。
Go
func changeName(name *string) {
*name = "New Name" // 修改指针指向的值
}
func main() {
name := "Old"
changeName(&name) // 传入地址
// name 变成了 "New Name"
}- 前端类比: JS 的对象默认就是引用传递。Go 的 Struct 默认是值传递(拷贝),如果你想在函数里修改外面的 Struct,或者 Struct 很大不想拷贝,就传指针。
- ⚠️ 避坑指南:
- 空指针异常: 对
nil指针解引用(*ptr)会 Panic。
- 空指针异常: 对
7. 结构体 (Struct) 与 “类”
Go 没有 class,只有 struct。
Go
type User struct {
Name string `json:"name"` // Tag,用于 JSON 序列化
Age int `json:"age"`
}
// 方法 (Method) —— 绑定到结构体
// (u *User) 叫接收者,建议用指针接收者以避免拷贝和允许修改
func (u *User) SayHi() {
fmt.Printf("Hi, I'm %s\n", u.Name)
}- 前端类比:
struct定义 ≈ TS 的interface或type。方法绑定 ≈ Class 的 method。 - JSON 处理: 后端开发必用。字段必须大写开头才能被导出(Public),否则
json.Marshal会忽略它。Tag 用来定义转成 JSON 后的 key 名。
8. 接口 (Interface)
Go 的接口是隐式实现的(Duck Typing)。
Go
type Animal interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string { // Dog 自动实现了 Animal,无需 implements 关键字
return "Woof"
}- 前端类比: TS 的 Interface。只要你的对象上有这个方法,你就符合这个接口。
- 空接口
interface{}(或 Go 1.18+ 的any): 可以代表任何类型(类似 TS 的any)。常用在接收不确定类型的参数时。
9. 错误处理 (Error Handling)
Go 没有 try-catch(除了极少用的 panic/recover),而是通过返回值返回错误。
Go
// 模拟读取文件
func readFile(name string) (string, error) {
if name == "" {
return "", fmt.Errorf("filename is empty")
}
return "content", nil
}
func main() {
content, err := readFile("")
if err != nil {
// 处理错误
fmt.Println("Error:", err)
return
}
fmt.Println(content)
}- ⚠️ 避坑指南:
- 不要忽略错误!不要写
_忽略err。这是 Go 代码健壮性的基石。 - 习惯这种
if err != nil写在每一行函数调用后的风格。
- 不要忽略错误!不要写
10. 继承 (Composition over Inheritance)
Go 没有 extends。通过嵌入 (Embedding) 实现复用。
Go
type Base struct {
ID int
}
type User struct {
Base // 嵌入,User 直接拥有了 ID 字段
Name string
}
u := User{}
u.ID = 1 // 直接访问11. 并发 (Goroutines & Channels) —— 杀手级特性
Go
func task(id int) {
fmt.Println(id)
}
func main() {
// 启动一个轻量级线程 (Goroutine)
go task(1)
// 匿名函数写法
go func() {
fmt.Println("Async")
}()
}- 前端类比:
go func()类似 JS 的Promise或async函数执行,但 Go 的调度是在用户态完成的,开销极小。 - ⚠️ 避坑指南:
- 主线程 (
main) 退出时,所有 Goroutine 会被强制杀掉。需要用sync.WaitGroup等待它们完成。 - 数据竞争:多协程操作同一变量需加锁 (
sync.Mutex)。
- 主线程 (
12. 泛型 (Generics) (Go 1.18+)
Go
// T 可以是 int 或 float64
func Add[T int | float64](a, b T) T {
return a + b
}- 前端类比: 和 TS 的泛型
<T>基本一致。
总结:前端转 Go 的企业级开发速查表
| 特性 | Go 概念 | 前端类比 (TS/JS) | 核心坑点 |
|---|---|---|---|
| 变量 | x := 1 | let x = 1 | 未使用变量会编译报错。 |
| 可见性 | 首字母大写(Public),小写(Private) | export,private | 跨包调用或 JSON 序列化时,字段必须大写! |
| 数组 | Slice ([]int) | Array | Slice 切片共享底层数组,修改会相互影响。 |
| 对象 | Struct + Methods | Class / Object | 没有class,this,extends。用结构体和接收者。 |
| 异步 | Goroutine (go func()) | Promise / Async | 主程结束协程直接死;注意并发读写 Map 会 Panic。 |
| 空值 | nil | null/undefined | 只有引用类型 (Pointer, Slice, Map) 才能为 nil。 |
| JSON | json.Marshal/Unmarshal | JSON.stringify/parse | 结构体字段必须打 Tag 且首字母大写。 |
| 异常 | return val, err | try...catch | 必须显式判断if err != nil。 |