You groundhog -- golang in the front end's eyes

Posted by GameMusic on Tue, 08 Mar 2022 05:19:39 +0100

Recently, I learned golang and began to use it in business. Let's use a front-end perspective that only knows js/ts to quickly familiarize ourselves with the go language and get started at the speed of light in 10 minutes. The simple grammar level will not say much, but only start from some common and prominent points. For more in-depth language features, explore them according to the documents

Multiple return values

go a function can return multiple values:

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

Generally used to return the result and error of an operation:

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

// use
err, val := A(100)

The promise scene in js has similar usage, and individuals also have this preference:

export function awaitToJs<T, U = Error>(promise: Promise<T>): Promise<[U | null, T | undefined]> {
  return promise
    .then<[null, T]>((data: T) => [null, data])
    .catch<[U, undefined]>((err: U) => [err, undefined]);
}

// use
(async () => {
    const [err, data] = await getSomeInfo()
})()

Methods and functions [key points]

There is no this in go. How to achieve similar effects? That's the way. The difference between the method in go and the function is that there is more receiver in front of the function name. Go's object-oriented, in fact, is the same. Go refers to the plain object of js, which is struct. In struct, functions cannot be written, which is implemented by receiver

// For example, we define the function of a map similar to js
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
}

// When using test Using 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
    }))
}

// Define a Math type, which is a structure
type Math struct {
    E float64
}

// There is a max method under the Math type
func (this *Math) max(values ...int) int {
    var ret int
    for _, v := range values {
        if v > ret {
            ret = v
        }
    }
    return ret
}
// use
func main() {
    var test Math
    test.E = 2.718281828459045
    fmt.Println(test.max(1, 2, 3, 4, 6)) // 6
}

interface

The interface of go contains the type collection of some methods, which are abstract and not implemented. The interface cannot contain variables. If you declare an interface type for a variable, you need to implement it:

type GetInfo interface {
    GetName() string
    GetAge() int
}

type People struct {
    name string
    age  int
}

// Implement 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) // instantiation 
    instance.name = "lhyt"
    instance.age = 100
    people = instance // Implements the instance of GetInfo
    fmt.Println(people.GetAge(), people.GetName())
}

However, in general business code, there are many empty interfaces. The empty interface is similar to the any effect of ts.

Any = > empty interface [key]

Basic introduction

The empty interface does not contain any method, and any other type implements the empty interface, so it has the effect of any of ts. The previous code also saw that there is an empty interface. When using, if you want to get a value (you know it's a structure / an int), you need to assert. If the assertion fails, it will cause a program error

Look back at this Code:

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

item is an empty interface type (any). We know in advance that it is a struct, so we assert that it is struct {a int}, and the syntax is anyValue (someType), representing anyValue of empty interface type. Here, the runtime type is someType. When we assert error:

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

Dynamic type

So the question is, what if there is the possibility of multiple types? For example, the third-party library of a comparison pit sometimes returns an int and sometimes a float. In the face of this situation, go still has a way:

// The id given by the third-party library is sometimes string and sometimes float64
func getIntIdFromStringOrFloat64(id interface{}) int {
    if _, ok := id.(string); ok {
        val, err := strconv.Atoi(id.(string)) // strconv is very commonly used to do string and other conversions
        if err == nil {
            return val
        }
        panic(err)
    }
    return int(id.(float64))
}

This paragraph indicates that if the id assertion is successful, the string is converted to int; If float64 is asserted, float64 is converted to int. After our compatibility, we are not afraid of their random change of types, and our service will not cause accidents

In addition, the comma ok mode is commonly used in go, which is usually used together with if. It means to do something, return success or error, and do something according to whether it is successful or not:

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

type-switch

If there are many types, of course, they will not be layers of if as described above. go has a special switch to support this demand. type can also be other complex types, such as struct, map, slice, channel, etc

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)
}

Dynamic call in business code

For example, if there is an rpc client mapping table that can be obtained through the key and then called, it will probably do the following:

type Rpc interface {
    Request(c *gin.Context, v ...interface{}) interface{}
}

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

Object = > struct / Map

The structure / mapping in go refers to the plain object of js. However, there are some differences between structure and mapping. Structure needs to know and determine each field in advance, which can not be dynamic; Map can dynamically increase or decrease key value pairs. When taking value, the structure can pass, The map needs to be retrieved by ["someKey"]

type Hello struct {
    a int
    b string
}

type World map[string]string
type World1 map[bool]string

// structural morphology
test1 := Hello{1, "hey"}
// map, key is string
test2 := World{"a": "1", "b": "2"}
// key is bool
testc := World1{false: "1", true: "2"}
// Pay attention to the difference of value selection methods
fmt.Println(test1.a, test2["a"], testc[false])

The structure can't be used to add key s, but the map can. If the map can't be used, nil will be returned. Similarly, the array of js is used to benchmark the slice / array of go. The go array also needs to know what elements are in advance. Like map, slice can maintain elements dynamically

try-catch => panic/recover

js uses try catch to catch errors. If you go, type errors can be thrown in the compilation stage, and the rest are dynamic and run-time errors. The error in the running times is called panic in go -- program panic

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

When an Error occurs at runtime, panic will be. recover refers to recovering from panic or Error, so that the program can regain control, stop the termination process, and then resume normal execution. Similar js code:

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

toString type conversion = > stringer

Go also has a type conversion toString similar to js. The default conversion from object to string in js is [object Object], and the conversion from array to string is an implicit call to join, or you can manually modify symbol Toprimitive method. In go, the way similar to manually rewriting toString is stringer. fmt bag comes with

type Stringer interface {
    String() string
}

When fmt prints strings, if struct is printed, the system will print {value1, value2} sets by default. To customize this process, we need to implement the String method ourselves:

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"Age"`
}

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", "age": 100}
    // The case without modification is {lhyt, 100}
}

In addition, a structure followed by json:"name" indicates that the key is this when json is serialized. This is the structure label of go. You can understand it as some field description information. The runtime can read this information through reflection and make some corresponding logic

reflect

The runtime dynamic logic of go is implemented by reflection. For example, js object Implementation of keys go:

type World map[string]string

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

If you know the keys, then object Values / entries can be implemented

You can also do many interesting and dynamic things. See v its tips:

last

The characteristics and depth of go will not be discussed here, such as collaborative process and interested steps here . Of course, there are some common points in front-end development, which can be quickly familiar from the front-end perspective, such as go's hmr - air, go's package management - go mod, etc. If you play go on a stand-alone basis, you can install an air hot update and run it, including my stand-alone test