前言
本菜鸡是一名去年刚毕业的node开发,现在也加入到了学习go的学习队伍中来了,那么,既然我是一个node开发转go,那我就先从最基本的开始说起,先来对比一下在node里面的数据类型(也就是javascript)和go里面的数据类型上,有什么相同点和不同点。
两种主要的数据类型
相信大家在编程的过程中,不管是java还是javascript都会接触到一个概念,那就是在传递数值的过程中,可以把数据类型总的来说可以分为两种,一种是值类型,一种是引用类型。也就是在传递的时候,可以分为值传递和引用传递。
这两种数据类型主要的区别是在引用的过程中,值类型的话,它在引用的过程中,它是 复制 一份当前的变量给对方,而引用类型则不同,它是通过指向这个数据结构所存储的位置,也就是两个变量引用来自同一个地方的数据。
那么在使用这两种数据结构的时候,会有什么不用点呢。
javascript中的值类型和引用类型
值类型
在javascript中,所有的简单的数据类型都是值类型,例如number,string,bool等等,这些数据类型在被引用时,改变现有数据,都不会改变原数据(这里说的有点绕口)
用一个简单的例子来说明
var a = 12
var b = "hello"
var c = true
function text(a,b,c) {
var a1 = a
var b1 = b
var c1 = c
a1 = 13
b1 = "world"
c1 = false
console.log(a, a1) //12, 13
console.log(b, b1) // hello, world
console.log(c, c1) // true, false
}
text(a,b,c)
这里我们可以看到,初始的a,b,c并不会因为后续的在赋值的改变而有变化。
引用类型
在javascript中,所有的复杂的数据结构,在传递值得过程中,都是引用类型,例如数组,对象等。
复杂的数据类型,在对它的引用变量进行二次赋值的时候,是会对初始的引用对象造成影响的,同样的也可以用一个简单的例子来说明这种情况
var a = [1,2,3,4]
function test(a) {
var b = a
b[1]=10
console.log(a) // [1,10,3,4]
console.log(b) // [1,10,3,4]
}
test(a)
这里我们可以看到,当我们对b做出改变时,a也会跟着改变。
go中是否存在两种数据传递类型??
这里我可以先直接给出一个结论:在go语言里面,所有数据传递都是值传递,也就是都会走一个拷贝的路线,而这也包括了上面所提到的数组
注意:因为go是一门强类型的语言,而javascript是一门弱类型的语言,所以在声明一个变量的时候,两者会有一些区别。
下面通过go来举个小例子说明一下
func main() {
a := [3]int {1,2,3} //这里是声明一个数组,等同于 var a = [3]int {1,2,3}
b := a
b[1] = 10
fmt.Print(a,b) // 这一步相当于console.log()
// [1 2 3] [1 10 3]
}
通过这个很简单的小例子我们可以看到,对于数组来说,它同样也是属于值传递的类型。
那么就真的没有什么方法可以做到引用传递了吗,这样的话,在写代码的时候,不是会非常的麻烦?答案当然是有的。
指针和切片
可能对于很多前端的或者是像我一样只是接触过javascript的开发人员来说,可能对于指针这个概念有点陌生,因为在javascript里面是没有指针这么一个东西的(只有引用),那么在go里面,却可以通过指针的方式来完成。
// 这是一个将两个数字交换位置的例子
func sort(a,b int) {
a , b = b , a
}
func main() {
var a, b int = 2, 3
sort(a,b)
fmt.Println(a,b) // 2,3
}
这里我们可以看到,它依然还是原来的值,因为在go里面,所有的传递类型,都是值传递。
那么,当我们使用指针的话,就可以解决这么一个问题了
func sort(a,b *int) {
*a , *b = *b , *a
}
func main() {
var a, b int = 2, 3
sort(&a,&b)
fmt.Println(a,b) // 3, 2
}
这里还有一点需要注意的:在go语言里面,指针是不能拿来运算的(好像c是可以的)
同样的,数组我们也可以通过指针的方式,使得它变成引用传递。
func chunk(a *[3]int) {
a[2] = 10
}
func main() {
var a = [3]int {1,2,3}
chunk(&a)
fmt.Println(a) // [1 2 10]
}
虽然效果是可以的,但是在使用的过程中,却非常的麻烦,首先在Go这样的强类型语言里面,数组的长度是需要提前预定好的,而不能跟javascript一样,可以随便写,同时指针的引用也非常的麻烦,那么有没有一种方式,可以达成在数组中使用指针一样,相同的效果呢?
这里就要提到在Go语言里相对于其他语言来说,比较特殊的一种类型——切片
切片的一些概念
在官方里面对于切片的说法是:Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
那么切片在使用上也会有所不同
// 声明一个切片
func main() {
var a = [...]int {1,2,3}
b := [...]int {4,5,6}
fmt.Println(a, b) // [1,2,3],[4,5,6]
}
这里我们可以看到在声明一个切片的时候,并不需要定死一个数组的长度,而是可以灵活的去配置。
例如,我想将两个切片进行拼接,或者是对一个切片后面继续追加数据。
func concat(a, b []int) {
c := append(a, b...) // 类似于ES6中的解构,将切片打散
fmt.Print(c) // [1 2 3 4 5 6]
}
func push(a []int, b int) {
d := append(a,b)
fmt.Print(d) // [1 2 3 9]
}
func main() {
a := []int {1,2,3}
b := []int {4,5,6}
concat(a,b)
push(a,9)
}
具体的切片的一些概念,以及它详细的使用方法,我会在之后的文章中,继续说明,这里我们回到刚才的问题—— 如何将类似于数组的类型在传递过程中,变成引用传递。
当我们使用切片的时候,在函数中传递值得时候,它是怎么样的呢?我们再来看下面的这一个例子。
func update(a []int) {
a[2] = 10
}
func main() {
//切片的拼接
a := []int{1,2,3}
update(a)
fmt.Print(a) //[1 2 10]
}
我们可以看到,通过切片的方式,就成功的把一个数组结构(虽然它不是数组),成功的可以进行引用传递了,同时也避免了很多的麻烦。
最后
往后,随着我继续深入的学习go语言,会继续加更更多的go相关的文章,同时也会将它与熟悉的node来做一个对比,希望可以加深我对于这门语言的一个理解。
如果觉得我的文章有哪里不对的地方,欢迎评论指正,如果觉得文章内容还过的去,那就来个一键三联!!!