一个前端的 Golang 语法层面入门感悟(持续补充)
写本文主要想对比下 Golang 和通常前端所用的日常开发语言,如 JS 和 TS 之间的异同,以便更好地切换开发时的心智模式。
关于变量作用域
Golang 的作用域好像与 ES6 的 let 变量作用域相同。
The Way To Go 中的例子
- 输出 GOG
package main
var a = "G"
func main() {
n()
m()
n()
}
func n() { print(a) }
func m() {
a := "O"
print(a)
}
- 输出 GOO
package main
var a = "G"
func main() {
n()
m()
n()
}
func n() {
print(a)
}
func m() {
a = "O"
print(a)
}
- 输出GOG
package main
var a string
func main() {
a = "G"
print(a)
f1()
}
func f1() {
a := "O"
print(a)
f2()
}
func f2() {
print(a)
}
注意 f2()
中的 print(a)
,读的是全局变量a
,而不是函数执行域中f1()
的a
。
关于包导出
Golang 通常一个目录为一个包(package)。不像 JS 中常见的包管理机制(如 ESM等),Golang 包内成员不需要用export
等关键字显式声明导出。
导出的成员函数应以大写字母开头,否则不可以导出(Goland IDE 如果引用了包内小写的函数,会有报错提示,点击导出后 IDE 会将函数开头改成大写字母)。
三、变参函数
Golang 的变参函数类似于解构赋值,但有略微不同。如 The Way To Go 中的例子。
- 当变参函数想要拿到剩余变长参数所组成的切片,在表示切片类型时,类似解构赋值的三点运算写在前面:
func Greeting(prefix string, who ...string) // ...string 表示 []string 这一切片类型
Greeting("hello:", "Joe", "Anna", "Eileen")
- 当将切片传递给变参函数时,类似解构赋值的三点运算写在切片变量后面:
package main
import "fmt"
func main() {
x := min(1, 3, 2, 0)
fmt.Printf("The minimum is: %d\n", x)
slice := []int{7,9,3,5,1}
x = min(slice...) // 写在后面
fmt.Printf("The minimum in the slice is: %d", x)
}
func min(s ...int) int {
if len(s) == 0 {
return 0
}
min := s[0]
for _, v := range s {
if v < min {
min = v
}
}
return min
}
关于结构体的类型
我们知道在 TypeScript 中,对象的类型只要形状一致,即认为该对象属于某一类型:
type Options = {
par1: string;
par2: number;
par3: any;
}
const instance1: Options = {
par1: "aaa",
par2: 123,
par3: "bbb",
}
const instance2 = {
par1: "aaa",
par2: 123,
par3: "bbb",
}
const instance3: Options = instance2; // 无报错
但在 Golang 中并非如此。例子在下面:
varargs.go
package varargs
import (
"fmt"
)
type Options struct {
Par1 string
Par2 int64
Par3 interface{}
}
func PrintVarArgsUsingStruct(para *Options) {
fmt.Print("\n\nPrintVarArgsUsingStruct: ", *para)
}
sturcttest.go
package structtest
import (
"fmt"
"goSimplePractice/varargs"
)
func Test() {
fmt.Print("\n\nstructTest: ")
varMadeFromType := &varargs.Options{Par1: "123", Par2: 19980416, Par3: "testTestTest"}
varMadeFromStruct := &struct {
Par1 string
Par2 int64
Par3 interface{}
}{Par1: "123", Par2: 19980416, Par3: "testTestTest"}
varargs.PrintVarArgsUsingStruct(varMadeFromType)
// varargs.PrintVarArgsUsingStruct(varMadeFromStruct)
// Output: Cannot use 'varMadeFromStruct' (type *struct {...}) as the type *Options
varargs.PrintVarArgsUsingStruct((*varargs.Options)(varMadeFromStruct)) // ✅
}
事实上,在 golang 等强类型语言中无形状这一说法,因为 golang 不是动态类型语言,无需为兼容弱类型的对象做妥协。但有完全相同「形状」,即完全相同的字段(名字、个数和类型)的结构体可以互相转换。