热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

你这只土拔鼠呀——前端眼中的golang

最近学习了golang,并在业务上开始使用。我们来用一名只会jsts的前端视角,来快速熟悉一下go语言,10几分钟光速入门。简单的语法层面的不会多说,只从一些共同点突出点来出发。更


最近学习了golang,并在业务上开始使用。我们来用一名只会js/ts的前端视角,来快速熟悉一下go语言,10几分钟光速入门。简单的语法层面的不会多说,只从一些共同点突出点来出发。更深入的语言特性,自行根据文档去探索吧


多返回值


go一个函数可以返回多个值:


func A() (int bool) {
return 1, false
}

一般用于返回一个操作的结果与错误:


func A(a int) (error int) {
var (
err error
val int
)
val, err = getUserId(a)
if err != nil {
return err, 0
}
return nil, err
}
// 使用
err, val := A(100)

js里面的promise场景也有类似的用法,个人也有这种喜好:


export function awaitToJs(promise: Promise): Promise<[U | null, T | undefined]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[U, undefined]>((err: U) => [err, undefined]);
}
// 使用
(async () => {
const [err, data] = await getSomeInfo()
})()

方法与函数【重点】


go里面没有this,如何实现类似的效果?那就是方法了。go里面的方法,和函数的区别是,函数名字前面多了receiver。go的面向对象,其实也是如此。go里面对标js的plain object的,就是struct,而struct里面不能写函数,使用receiver来实现


// 比如我们定义一个类似js的map的功能
func (this Array0) ArrayMap(cb func(interface{}, int) interface{}) []interface{} {
var ret []interface{}
for i, v := range this {
ret = append(ret, cb(v, i))
}
return ret
}
// 使用的时候test.ArrayMap来使用
func main() {
test := Array0{{a: 10}, {a: 3}}
fmt.Println(test.ArrayMap(func(item interface{}, index int) interface{} {
return item.(struct{ a int }).a + index
}))
}
// 定义一个Math类型,是一个结构体
type Math struct {
E float64
}
// Math类型下有一个max方法
func (this *Math) max(values ...int) int {
var ret int
for _, v := range values {
if v > ret {
ret = v
}
}
return ret
}
// 使用
func main() {
var test Math
test.E = 2.718281828459045
fmt.Println(test.max(1, 2, 3, 4, 6)) // 6
}

interface


go的interface里面是一些方法的类型集合,它们是抽象的没有被实现的。接口里也不能包含变量。你如果给一个变量声明了interface类型,那么你要去实现它:


type GetInfo interface {
GetName() string
GetAge() int
}
type People struct {
name string
age int
}
// 实现GetInfo
func (people *People) GetName() string {
return people.name
}
func (people *People) GetAge() int {
return people.age
}
func main() {
var people GetInfo
instance := new(People) // 实例化
instance.name = "lhyt"
instance.age = 100
people = instance // 实现了GetInfo的instance
fmt.Println(people.GetAge(), people.GetName())
}

但是一般业务代码中,用空interface较多。空interface类似ts的any的效果。


any => 空interface【重点】


基本介绍


空interface不包含任何方法,任何其他类型都实现了空接口,因此具有ts的 any 的效果。前面代码也看见了,有空interface。使用的时候如果想取值(你知道那是一个结构体/一个int),那么需要断言。如果断言失败,将会导致程序错误


回头看看这段代码:


fmt.Println(test.ArrayMap(func(item interface{}, index int) interface{} {
return item.(struct{ a int }).a + index
}))

item是空interface类型(any),我们事先知道是一个struct,因此断言它就是 struct{ a int } ,语法为 anyValue.(someType)表示空interface类型的anyValue此处运行时类型为someType 。当我们断言错误的时候:


fmt.Println(test.ArrayMap(func(item interface{}, index int) interface{} {
return item.(int) + index
}))
// panic: interface conversion: interface {} is struct { a int }, not int

动态类型


那么问题来了,如果有真的多个类型存在的可能性呢?比如某个比较坑的第三方库,有时候返回个int有时候返回个float的。面对这种情况,go还是有办法的:


// 第三方库给的id有时候是string有时候是float64
func getIntIdFromStringOrFloat64(id interface{}) int {
if _, ok := id.(string); ok {
val, err := strconv.Atoi(id.(string)) // strconv很常用,做string和其他互转
if err == nil {
return val
}
panic(err)
}
return int(id.(float64))
}

这段表示,如果id断言为string成功,那么string转为int;如果断言到的是float64,那么float64转为int。经过我们的兼容,就不怕他们乱改类型了,我们的服务还是不会出事


补充一点,逗号ok模式是go里面很常用的,一般会接if一起用。表示做一些事情,返回成功或者错误,并根据有没有成功来做一些事情:


if _, ok := id.(string); ok {}

type-switch


如果有很多种类型,当然不会是像上面的写法那样子一层层if,go有专门的特殊的switch支持这种需求。type也可以是其他复杂的类型,如struct、map、slice、channel等


func getIntIdFromStringOrFloat64(id interface{}) int {
switch id.(type) {
case string:
val, err := strconv.Atoi(id.(string))
if err == nil {
return val
}
panic(err)
case float64:
return int(id.(float64))
}
return id.(int)
}

业务代码中实现动态调用


比如有一个rpc客户端映射表,通过key去获取然后进行调用,那么大概会这样做:


type Rpc interface {
Request(c *gin.Context, v ...interface{}) interface{}
}
func SomeService(c *gin.Context, key string) {
// ClientMap是map[string]Rpc类型
RpcClients := ClientMap[key]
if RpcClients == nil {
return
}
Params := map[string]interface{}{
"id": 666,
}
RpcClients.Request(c, Params)
}

对象 => 结构体/映射


go中的结构体/映射对标js的 plain object 了。但结构体和映射有一些不一样,结构体是需要提前知道且确定好每一个字段,做不到动态;而map就可以做到动态增减key-value对。取值的时候,结构体可以通过 . ,而map需要 ["someKey"]


type Hello struct {
a int
b string
}
type World map[string]string
type World1 map[bool]string
// 结构体
test1 := Hello{1, "hey"}
// map,key为string
test2 := World{"a": "1", "b": "2"}
// key为bool
testc := World1{false: "1", true: "2"}
// 注意取值方式区别
fmt.Println(test1.a, test2["a"], testc[false])

结构体做不到后续新增key了,map却可以,map取不到的话返回nil 。类似的,js的数组对标go的切片/数组,go数组也是需要提前知道有什么元素,而slice类似map一样,可以动态维护元素


try-catch => panic/recover


js中使用try-catch捕获错误,go的话,类型上的错误在编译阶段即可抛出,剩下的就是那些动态的、运行时报错了。运行时报错在go里面叫 panic ——程序恐慌


func exec(g func()) {
defer func() {
if err := recover(); err != nil {
fmt.Println("错误: %v", err)
}
}()
g()
}

当运行时出错,将会panic。recover是指从 panicError 中恢复,让程序可以重新获得控制权,停止终止过程进而恢复正常执行。类似的js代码:


function exec(f) {
try {
f()
} catch (e) {
console.log('错误:', e)
}
}

toString类型转换 => stringer


go也有类似js的类型转换toString。js中默认的把对象转字符串是 [object Object] ,数组转字符串是隐式调用join,或者可以手动修改 Symbol.toPrimitive 方法。go里面类似手动重写 toString 的方式就是stringer了。fmt包自带


type Stringer interface {
String() string
}

在fmt打印的时候打印字符串,如果打印的是struct,则会走系统默认打印出 {value1, value2} 集合。我们想自定义这个过程,需要自己去实现String方法:


type Person struct {
Name string `json:"name"`
Age int `json:"年龄"`
}
func (p Person) String() string {
data, err := json.Marshal(p)
if err == nil {
return string(data)
}
panic(err)
}
func main(){
test := Person{"lhyt", 100}
fmt.Println(test)
// {"name":"lhyt","年龄":100}
// 不修改的情况是{lhyt, 100}
}

此外,一个结构体里面后面接 json:"name" 表示在json序列化的时候,key是这个。这是go的结构体标签,你可以理解为一些字段描述信息,运行时可以通过反射读取到这些信息,做一些对应的逻辑


reflect


go的运行时动态相关的逻辑很多就靠反射来实现了。比如js的 object.keys 的go的实现:


type World map[string]string
test2 := World{"a": "1", "b": "2"}
v := reflect.ValueOf(test2)
fmt.Println(v.MapKeys(), "<<<")
// [a b]

知道了keys,那么object.values/entries都可以实现了


还可以做很多很有趣的动态的事情,看看 v 它的提示有啥:


最后


go的特色和深入这里不多说了,比如协程,有兴趣的移步 这里 。当然还有一些前端开发的共同点,可以从前端的视角去快速熟悉,比如go的hmr——air,go的包管理——go mod等。单机玩go的话,可以装个air热更新跑起来即可,包括我现在单机测试也是这样


一大批文档: learnku.com/go/docs




推荐阅读
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
author-avatar
书友64457430
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有