背景
在看 Golang 语法的时候,遇到了函数传参的一个疑惑点
type Point struct{ X, Y int }
func modify(p *Point) {
(*p).X = 42
fmt.Println(*p) // {42, 2}
(*p) = Point{3, 4}
}
func main() {
p1 := Point{1, 2}
modify(&p1)
fmt.Println (p1) // {3, 4}
}这跟 JavaScript 中的引用传递现象并不一样
function modify(p) {
p.X = 42
console.log(p) // { X: 42, Y:2 }
p = {
X: 3,
Y: 4
}
}
let p = {
X: 1,
Y: 2
};
modify(p);
console.log(p); // { X: 43, Y: 2 }然后就梳理了下与 JS 的对比
编程界一般函数传参就两种类型,值传递和引用传递
- 值传递:将
变量地址所在的具体值进行传递 - 引用传递:将
变量地址进行传递
JavaScript 中的传参机制
如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样
不少人都认为是 "JavaScript 原始值类型按值传递,引用值按引用传递 "
这句话后半句是不对的
值传递
let num1 = 6;
let num2 = num1;在内存中的表现行为:
num1、num2 拥有完全不同的内存地址,但地址内存的值是一样的
引用传递
function setName(obj) {
obj.name = "tom";
}
let person = new Object();
setName(person);
console.log(person.name); // "tom"很多开发者错误地认为,当在局部作用域中修改对象而变化反映到全局时,就意味着参数是按引用传递的
看下面这个例子
function setName(obj) {
obj.name = "tom";
obj = new Object();
obj.name = "jerry";
}
let person = new Object();
setName(person);
console.log(person.name); // "tom"如果 person 是按引用传递的,那么 person 应该自动将指针改为指向 name 为 "jerry" 的对象。可是,当我们再次访问 person. name 时,它的值是 "tom",这表明函数中参数的值改变之后,原始的引用仍然没变
引用类型传参本质上还是 复制变量的值
Golang 的传参机制
真正的值类型按值传递,引用类型按引用传递
Go 中函数传参仅有值传递一种方式
对比 JavaScript ,我们就看引用类型
type Object struct{ age int }
func setName(obj *Object) {
(*obj).age = 2
fmt.Println(*obj) // {2}
(*obj) = Object{3}
}
func main() {
person := Object{1}
setName(&person)
fmt.Println (person) // {3}
}官网解释:https://go.dev/doc/faq#pass_by_value
When are function parameters passed by value?
As in all languages in the C family, everything in Go ispassed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to. (See a later section for a discussion of how this affects method receivers.)
Map and slice values behave like pointers: they are descriptors that contain pointers to the underlying map or slice data. Copying a map or slice value doesn't copy the data it points to. Copying an interface value makes a copy of the thing stored in the interface value. If the interface value holds a struct, copying the interface value makes a copy of the struct. If the interface value holds a pointer, copying the interface value makes a copy of the pointer, but again not the data it points to.
翻译一下:
像 C 家族中的其他所有语言一样,Go 语言中的所有传递都是传值。
也就是说,函数接收到的永远都是参数的一个副本,就好像有一条将值赋值给参数的赋值语句一样。
例如,传递一个 int 值给一个函数,函数收到的是这个 int 值得副本,传递指针值,获得的是指针值的副本,而不是指针指向的数据。
(请参考 [later section] (https://golang.org/doc/faq#methods_on_values_or_pointers) 来了解这种方式对方法接收者的影响)
Map 和 Slice 的值表现和指针一样:它们是对内部映射或者切片数据的指针的描述符。
复制映射或者切片的值,不会复制它们指向的数据。复制接口的值,会产生一个接口值存储的数据的副本。
如果接口值存储的是一个结构体,复制接口值将产生一个结构体的副本。
如果接口值存储的是指针,复制接口值会产生一个指针的副本,而不是指针指向的数据的副本。参考
【Go 进阶】Go 语言到底是值传递,还是引用传递? - 掘金