1. 示例
package main
//定义一个数据类型
type SomeReq struct {
Data []struct {
Name string `json:"name"`
Age int `json:"age"`
} `json:"data"`
}
//
type jsonUnmarshal struct {
reqData SomeReq
}
//方法1 a 类型为jsonUnmarshal
func (j jsonUnmarshal) JsonUnmarshal_1(reqData string) {
err := json.Unmarshal([]byte(reqData), &j.reqData)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(j.reqData.Data)
}
}
//方法1 a 类型为*jsonUnmarshal
func (j *jsonUnmarshal) JsonUnmarshal_2(reqData string) {
err := json.Unmarshal([]byte(reqData), &j.reqData)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(j.reqData)
}
}
func main() {
var data = `{"data":[{"name":"adada","age":1}]}`
//声明变量a 为jsonUnmarshal类型
var a jsonUnmarshal
a.JsonUnmarshal_1(data)
fmt.Printf("[方法1执行后的j.reqData]:%v\n", a.reqData)
a.JsonUnmarshal_2(data)
fmt.Printf("[方法2执行后的j.reqData]:%v\n", a.reqData)
}
输出
[{adada 1}]
[方法1执行后的j.reqData]:{[]}
{[{adada 1}]}
[方法2执行后的j.reqData]:{[{adada 1}]}
2. 问题描述
明明是一样的操作,你会发现,最终打印出来的结果不一样。
3. 问题分析
问题在于声明函数时func (j 结构体类型)
这部分造成的影响。
3.1 结构体类型为jsonUnmarshal
时
局部变量j
是把变量a
的值复制了一份,也就是传说中的值拷贝。这意味着你对j
的操作只在函数内部生效,不会影响到a
。如果你打印a
的地址,会发现和a
的地址和变量j
的不一样。
3.2 结构体类型为*jsonUnmarshal
时
局部变量j
是拿到了变量a
的内存地址,操作j
的也就是操作a
。
4. 开发时的技巧
常在河边走哪有不湿鞋,虽然道理懂了但总会有失手的时候。如果发现拿到的字段值不对时,就应该想到是这个问题。
此外,debug时看到这样的现象就需要引起注意。
你会发现j
这个变量出现了两个。多出了一个j(shadowed)
,就是影子变量的意思。这个时候就应该意识到对j
的操作只会在方法内部生效。
影子变量指的是代码执行过程中,出现了变量名称相同,但是内存地址不同的情况。
也就是同一个变量名被声明了多次,像这样