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