文章目錄
- 零、概述
- 一、函數基礎
- 1、函數基礎概念
- 2、參數傳遞機制
- 3、返回值特性
- 3.1. 多返回值
- 3.2. 命名返回值
- 3.3. 錯誤處理
- 二、函數類型與高階函數
- 1. 函數類型定義
- 2. 高階函數(函數作為參數、返回值)
- 三、匿名函數與閉包
- 1. 匿名函數(Lambda函數)
- 2. 閉包(Closure)
- 四、內置函數
- 五、方法(讓go有了面向對象的特性)
零、概述
函數核心特性速查
特性 | 說明 |
---|---|
多返回值 | 支持同時返回結果和錯誤(如(int, error) ) |
可變參數 | 通過...Type 定義,本質為切片([]Type ) |
函數類型 | 可定義函數類型(如type FuncType func(int) bool ) |
閉包 | 匿名函數捕獲外部變量,保持狀態 |
方法 | 綁定到結構體的函數,通過接收者參數(func (t Type) Method() )定義 |
內置函數 | len 、make 、append 等預定義函數,直接使用 |
最佳實踐
- 錯誤處理
- 多返回值中優先返回錯誤(如
func() (Result, error)
)。- 使用
if err != nil
判斷錯誤,避免忽略重要異常。- 減少值拷貝
- 傳遞大結構體時使用指針(
*T
),避免性能損耗。- 切片、map等引用類型默認傳遞指針,無需額外處理。
- 函數職責單一
- 每個函數專注完成一個獨立功能,符合單一職責原則。
- 避免函數過長(建議不超過50行),復雜邏輯拆分為子函數。
?
一、函數基礎
1、函數基礎概念
Go語言中的函數可賦值給變量、作為參數傳遞或作為返回值,極大提升了編程靈活性。
1. 函數定義語法
func 函數名(參數列表) (返回值列表) {函數體return 返回值
}
- 參數列表:參數類型需后置,連續相同類型可合并聲明。
func sum(x, y int) int { // x, y 均為 int 類型return x + y }
- 返回值列表:可無返回值或多返回值(用括號包裹),支持命名返回值。
func div(x, y int) (int, error) { // 多返回值if y == 0 {return 0, errors.New("除數不能為0")}return x / y, nil }
?
2、參數傳遞機制
1.1. 值傳遞
- 機制:傳遞參數的副本,函數內修改不影響原始值。
- 適用場景:小數據類型(如
int
、string
)或無需修改原始值的場景。func modifyValue(x int) {x = 100 // 僅修改副本 } func main() {a := 20modifyValue(a) // a 仍為 20 }
1.2. 引用傳遞
- 機制:傳遞參數的地址(通過指針
*T
),函數內可修改原始值。 - 適用場景:大數據類型(如結構體)或需修改原始值的場景。
func modifyPointer(x *int) {*x = 100 // 修改原始值 } func main() {a := 20modifyPointer(&a) // a 變為 100 }
1.3. 可變參數
語法:通過...Type
定義可變參數,本質為切片。
func sum(nums ...int) int { // nums 類型為 []inttotal := 0for _, num := range nums {total += num}return total
}
func main() {sum(1, 2, 3) // 等價于 sum([]int{1,2,3})
}
?
3、返回值特性
3.1. 多返回值
用途:同時返回結果和錯誤(Go語言的慣用模式)。
func readFile(path string) ([]byte, error) {data, err := os.ReadFile(path)return data, err
}
3.2. 命名返回值
語法:為返回值命名,函數體可直接使用(類似變量聲明)。
func calculate(x, y int) (sum, product int) { // 命名返回值sum = x + yproduct = x * yreturn // 隱式返回 sum, product
}
3.3. 錯誤處理
丟棄返回值:用_
忽略不關心的返回值。
data, _ := readFile("data.txt") // 忽略錯誤
?
二、函數類型與高階函數
1. 函數類型定義
在Go語言中,函數類型定義是指為一種特定的函數簽名創建一個自定義類型名稱。這樣可以讓具有相同簽名的函數共享同一個類型(有點面向接口開發的感覺)。
type Calculator func(int, int) int // 定義函數類型// 這些函數都符合Calculator類型的簽名
func add(x, y int) int { return x + y }
func subtract(x, y int) int { return x - y }
func multiply(x, y int) int { return x * y }
func divide(x, y int) int { return x / y }
?
例題:
package mainimport "fmt"type Calculator func(int, int) intfunc add(x, y int) int { return x + y }
func subtract(x, y int) int { return x - y }// 使用函數類型作為參數
func calculate(calc Calculator, a, b int) int {return calc(a, b)
}// 使用函數類型作為變量
func main() {var myCalc CalculatormyCalc = addfmt.Println(myCalc(5, 3)) // 輸出: 8myCalc = subtractfmt.Println(myCalc(5, 3)) // 輸出: 2// 作為參數傳遞result := calculate(add, 10, 5)fmt.Println(result) // 輸出: 15
}
?
2. 高階函數(函數作為參數、返回值)
函數作為參數:
func operate(x, y int, fn Calculator) int { // 接收函數作為參數
// 作為參數的函數,來處理參數return fn(x, y)
}
func main() {result := operate(10, 5, add) // 調用 add 函數
}
?
函數作為返回值:
在Go語言中,函數可以作為返回值,這使得我們可以創建閉包。閉包是一個函數,它可以捕獲并記住其所在環境中的變量。
func makeAdder(n int) Calculator { // 返回函數return func(x int) int {return x + n}
}
func main() {add5 := makeAdder(5) // add5(3) 返回 8
}
?
三、匿名函數與閉包
1. 匿名函數(Lambda函數)
- 無名稱函數,可直接定義和調用:
// 賦值給變量greet := func() {fmt.Println("Hello, Go!")}greet() // 調用匿名函數1. 定義:func() { ... }是一個匿名函數,因為它沒有名稱。
2. 賦值:我們將這個匿名函數賦值給變量greet。
3. 調用:通過greet()來調用這個匿名函數。// 立即執行函數表達式(IIFE)result := func(x, y int) int {return x * y}(3, 4) 1. 定義并執行:func(x, y int) int { return x * y }是一個匿名函數。
2. 立即執行:在定義后面緊跟(3, 4),這表示立即用參數3和4調用這個函數。
3. 結果:函數返回3 * 4的結果12,并將其賦值給變量result。
?
2. 閉包(Closure)
定義:匿名函數捕獲外部變量形成閉包,變量在閉包內保持狀態。
閉包能夠捕獲并記住其外部環境中的變量,即使在函數執行完畢后,這些變量仍然可以被訪問和修改。這使得閉包可以在不同的調用之間保持狀態,例如計數器、緩存等。
package mainimport "fmt"// adder 返回一個閉包函數,該函數捕獲了外部變量 sum
func adder() func(int) int {sum := 0 // 這是被閉包捕獲的外部變量return func(x int) int {sum += x // 每次調用時,更新并返回 sumreturn sum}
}func main() {pos := adder() // 創建一個新的閉包fmt.Println(pos(1)) // 輸出: 1fmt.Println(pos(2)) // 輸出: 3fmt.Println(pos(3)) // 輸出: 6// 創建另一個獨立的閉包neg := adder()fmt.Println(neg(-1)) // 輸出: -1fmt.Println(neg(-2)) // 輸出: -3
}
?
四、內置函數
Go語言預定義了一組內置函數,無需導入包即可使用:
函數名 | 功能描述 |
---|---|
len() | 獲取切片、字符串、通道等的長度 |
new() | 分配零值內存,返回指針(如new(int) 返回*int ) |
make() | 創建引用類型(切片、map、通道)并初始化 |
append() | 向切片追加元素,返回新切片 |
panic() | 觸發運行時恐慌,用于錯誤處理 |
recover() | 在defer 中恢復恐慌,避免程序崩潰 |
?
五、方法(讓go有了面向對象的特性)
在Go語言中,方法和函數的主要區別在于方法是綁定到特定類型的,而函數則是獨立的。
package mainimport ("fmt""math"
)type Circle struct {radius float64
}func (c Circle) area() float64 {return math.Pi * c.radius * c.radius
}func (c *Circle) updateRadius(r float64) {c.radius = r
}func calculateArea(radius float64) float64 {return math.Pi * radius * radius
}func main() {c := Circle{radius: 5}// 調用方法fmt.Println("Area using method:", c.area()) // 使用方法計算面積// 更新半徑c.updateRadius(10)fmt.Println("Updated area using method:", c.area())// 調用函數fmt.Println("Area using function:", calculateArea(5)) // 使用函數計算面積
}
通過方法,Go語言實現了面向對象編程的特性,使得類型可以擁有自己的行為。