Skip to content

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)

  • 数值:int int8,int16,int32,int64, uint8 还有无符号类型 uint uint8~uint64
  • 浮点数float32,float64,complex64,complex128(JS 只有 Number,Go 区分整数和浮点)。
  • 字符串:string (双引号,不可变)。
  • 布尔:bool
  • 其他数字类型 byteruneuintptr(无符号整型,用于存放一个指针)
  • 零值机制: 变量声明未赋值时,默认为: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]。注意,sublist共享底层数组。修改 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 的 ObjectMap
  • ⚠️ 避坑指南:
    • 随机序:range 遍历 map 时,顺序是随机的(不像 JS Object 现在的实现有一定顺序)。
    • 初始化: 只声明 var m map[string]intnil map,直接赋值会 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 的 interfacetype。方法绑定 ≈ 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 的 Promiseasync 函数执行,但 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 := 1let x = 1未使用变量会编译报错。
可见性首字母大写(Public),小写(Private)export,private跨包调用或 JSON 序列化时,字段必须大写!
数组Slice ([]int)ArraySlice 切片共享底层数组,修改会相互影响。
对象Struct + MethodsClass / Object没有class,this,extends。用结构体和接收者。
异步Goroutine (go func())Promise / Async主程结束协程直接死;注意并发读写 Map 会 Panic。
空值nilnull/undefined只有引用类型 (Pointer, Slice, Map) 才能为 nil。
JSONjson.Marshal/UnmarshalJSON.stringify/parse结构体字段必须打 Tag 且首字母大写。
异常return val, errtry...catch必须显式判断if err != nil