Golang函数重点小结
1. 定义
- 函数属于第一类对象,具备相同签名(参数及返回值)的视作同一类型
- 第一类对象(first-class object)指可在运行期创建,可用作函数参数或返回值,可存入变量的实体。最常见的用法就是匿名函数
- 函数只能判断其是否为nil,不支持其他比较操作
- 从函数返回局部变量指针是安全的,编译器会通过逃逸分析(escape analysis)来决定是否在堆上分配内存
- 函数内联(inline)对内存分配有一定的影响
- Go函数不支持重载;函数重载需要进行额外的类型匹配,影响性能。
2. 参数
- 参数都是值拷贝,区别在于拷贝目标对象,还是拷贝指针。在函数调用前,会为形参和返回值分配内存空间,并将实参拷贝到形参空间
- 指针参数的性能不一定就好,被复制的指针会延长目标对象声明周期,还可能导致它被分配到堆上,那么其性能消耗就得加上堆内存分配和垃圾回收成本
- 其实在栈上复制小对象只需很少的指令就可以完成,远比运行时进行堆内存分配要快。另外,并发编程也提倡尽可能使用不可变对象(只读或复制),这可消除数据同步等麻烦。当然,如果复制成本很高,或需要修改原对象状态,自然用指针更好。
- 变参实际上就是一个切片
- 参数复制的是切片本身,并不包括底层数组
3. 匿名函数
- 匿名函数指没有定义名字符号的函数,除没有名字外,其他与普通函数相同
- 可赋值给变量,作为参数或返回值(被赋值的变量是一个地址)。
- 将匿名函数赋值给变量,与为普通变量提供名字标识符有着根本的区别。当然,编译器会为匿名函数生成一个“随机”符号名
- 普通函数和匿名函数都可作为结构体字段,或经通道传递
- 匿名函数也叫闭包函数。它们被允许调用定义在其他环境中的变量(这里具体可看《TheWayToGo》的第6章)
- 闭包通过指针引用环境变量,可能导致其生命周期延长,甚至被分配到堆上。还有所谓“延迟求值”的特性
- 闭包让我们不用传递参数就可读取或修改环境状态,当让也要为此付出代价。对于性能要求较高的场合,须慎重使用。
4. 延迟调用/错误处理
- 相比直接用CALL汇编指令调用函数,延迟调用则须花费更大代价。这其中包括注册,调用等操作,还有额外的缓存开销。
- panic会立刻中断当前函数流程,执行延迟调用。而在延迟函数中,recover可捕获并返回panic提交的错误对象
- 中断性错误会沿调用堆栈向外传递,要么被外层捕获,要么导致进程奔溃
- 在延迟函数中panic,不影响后续延迟调用执行。而recover后panic,可被再次捕获。另外,recover必须在延迟调用函数中执行才能正常工作
- 除非是不可恢复性,导致系统无法正常工作的错误,否则不建议使用panic
5. 概念补充
- 工厂函数:一个返回值为另一个函数的函数可以被作为工厂函数,这在需要创建一系列相似的函数时非常有用(《TheWayToGo》6.9)
- 高阶函数:可以返回其他函数的函数和接受其他函数作为参数的函数称之为高阶函数。这是函数性语言的特性。