常见编程语言入门:go

556 阅读6分钟

概述

go的出现主要是在保证c++或java等性能的前提下提升了开发效率,这里可以参考 What is the purpose of the project?

go的语法主要来自于c family,在此基础上做了大量简化,比如没有class,简单的并发api(goroutine,channel),也因此go的用法和常见语言有比较大的差别,下面以nodejs端的js为例,通过对比对go做一下基本了解。

与js的对比

解释和编译

js是一种解释型语言,为了加快运行效率,node.js中的v8引擎会进行 just-in-time (JIT) 编译,go是一种编译型语言。

类型

由于类型系统的相关概念有争议(弱类型、强类型、动态类型、静态类型语言的区别是什么?),因此这里不会按此分类。
js本身不会有静态类型检查,因此才有了带类型的ts;类型之间可以各种隐式转换,因此也有了大部分语言没有的三个等号的判等===
go虽然可以声明时不带类型,但自身包含静态类型检查,不能隐式类型转换,可以使用type_name(expression)进行强制转换

面向对象

js支持以原型继承为基础的面向对象,并由class语法糖,go中为了简化使用,不包含class等继承方式,不算是一个真正意义上的面向对象语言(Is Go an object-oriented language?

多线程

js的官方规范ecmascript没涉及线程相关的概念,但无论是node.js还是浏览器传统上是以单线程为基础的异步事件驱动,但是后来html规范推出了web worker,node.js推出了worker_threads,因此也为多线程提供了原生的支持,而go更是以轻量级线程goroutine和消息传递方式channel而擅长高并发。

语法

js的语法和c语言很相似,go的语法自带lint功能,比如不能存在引入但不用的包。

包管理

js有生态繁荣的npm,它是世界上最大的software registry,且yarn和npm包管理工具十分方便,go使用go modules,使用可参考这里,没有深度使用,这里不多展开。

结构

一个基础的go代码,比如

package main

import "fmt"

func main() {
   /* 这是我的第一个简单的程序 */
   fmt.Println("Hello, World!")
}

包含以下部分

  • 包声明 声明当前的包名
  • 包引入
  • 启动函数,main函数,类似于c语言的main函数。

数据类型

包括基本数据类型和派生类型,其中前者包括

  • 布尔型
  • 数字
  • 字符串 后者包括
  • 指针
  • 数组
  • 切片
  • 结构
  • 函数
  • 接口
  • map
  • channel

布尔型

布尔类型(bool)只有true和false两个值

数字类型

数字类型在go中分的很细,处理传统的int,还有长度的整数和浮点数以及复数。

字符串

字符串(string)类型就是字符组成的数组,即[]rune[]byte用双引号,其中rune和byte表示单个字符,用单引号。

指针

go中的指针是阉割版的,可以使用&取址,声明指针,指针赋值,除了不能进行运算其他和c语言类似

数组

go中的数组和c语言类似,用来存放定长数量的某种类型,可以用...表示数组长度,编译器根据元素个数推论,但数组名是值类型,不是指针

var variable_name [SIZE] variable_type

切片

就是动态数组,可以用未指定长度的数组定义

var identifier []type

或者用make

var slice1 []type = make([]type, len)

结构

还是类似于c语言,用struct关键字

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

结构体指针同样使用点来访问成员

结构可以直接初始化,或者使用new,比如

type user struct {
    id int `1123`
}
  b := user{}
  b.id = 222
  c := new(user)
  c.id = 333

函数

函数使用func关键字定义,参数列表包括参数类型、顺序和个数,函数名和参数列表构成了函数签名。

func function_name( [parameter list] ) [return_types] {
   函数体
}

比如

func swap(x, y string) (string, string) {
   return y, x
}

接口

c++等面向对象的语言使用class封装数据和方法,go中可以使用结构、接口和函数和创建一个自定义的数据结构。
接口类型可以定义一组方法,用interface关键字,其他类型只要有这些方法就相当于实现了该接口。
结构可以定义一组数据,然后使用函数实现接口的对应方法,完成数据的封装。比如

package main

import (
    "fmt"
)

type Phone interface {
    call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
    fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
    fmt.Println("I am iPhone, I can call you!")
}
func main() {
    var phone Phone
    phone = new(NokiaPhone)
    phone.call()

    phone = new(IPhone)
    phone.call()

}

map

map就是字典,存储无序键值对

channel

使用chan关键字,用于两个goroutine之间传递消息。

变量和常量

变量

变量声明方法有好多

  • 使用var,其中类型在变量名后面,如果没有类型,编译器会根据后面的初始值推断
var v_name v_type
v_name = value

如果有类型没有初始值,会初始为零值,零值对于不同类型有不同含义,对于数值初始为0,对于布尔值初始化为false,对于字符串初始化为"",其他一般初始化为nil

  • 省略var,使用:=根据后面的初始值判断类型,用在函数内
v_name := value
  • 多变量声明
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3

var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断

vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误


// 这种因式分解关键字的写法一般用于声明全局变量
var (
    vname1 v_type1
    vname2 v_type2
)

常量

常量使用const,只能是基本类型,iota是一个特殊常量

作用域

主要包含函数作用域和全局作用域

语句

条件语句

if语句,除了小括号可以省略基本和js一样

if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
}
if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
} else {
  /* 在布尔表达式为 false 时执行 */
}

switch,每个case后默认带break,如果不需要可以使用fallthrough

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

select语句是通信中使用的switch

循环语句

for循环可以使用range迭代数组(array)、切片(slice)、通道(channel)或集合(map),有三种形式
第一种和js中的for一样

for init; condition; post { }

第二种和js中的while一样

for condition { }

第三种就是无限循环类似于for { }

for { }

使用range时,以字符串为例

strings := []string{"google", "runoob"}
for i, s := range strings {
        fmt.Println(i, s)
}

其中i代表index,s代表value


完结