從精通JS到golang入門 - 基礎入門

Go官網html

基礎入門

1.學習方式

第一種,本地項目學習 如何使用 go 命令來獲取、構建並安裝包、命令及運行測試java

第二種,在線學習工具使用。 第一部分只是簡單歡迎引導python

2.歡迎使用Go指南

指南的使用linux

歡迎來到Go編程語言指南。本指南涵蓋了該語言的大部分重要特性,主要包括:golang

歡迎!
學習如何使用本指南:包括如何在不一樣的課程之間切換,以及如何運行代碼。
複製代碼

基礎編程

一開始,將學習關於語言的全部基礎內容。segmentfault

定義變量,調用函數,以及在您學習下一課以前所須要瞭解的所有內容。windows

包,變量和函數。
學習Go程序的基本組件。
複製代碼
流程控制語句:forifelse和switch
學習如何用條件,循環和開關語句控制代碼的流程。
複製代碼
複雜類型:struct,slice和map。
學習如何基於現有類型定義新的類型:本課涵蓋告終構體,分佈,切片和地圖。
複製代碼

方法和接口數組

學習如何爲類型定義方法;如何定義接口;以及如何將全部內容互換起來。bash

方法和接口
本課包含了方法和接口,能夠用它們來定義對象和其行爲。
複製代碼

併發

做爲語言的核心部分,轉到提供了併發的特性。

這一部分概述了goroutein和通道,以及如何使用它們來實現不一樣的併發模式。

併發
Go將併發爲語言的核心構成。本課將進行進行介紹,並提供一些示例來展現如何使用它們。
複製代碼

一.開始學習- 包、變量、函數

/*fmt標準庫是咱們在學習Go語言過程當中接觸最先最多使用的庫, #fmt包實現了相似C語言printf和scanf的格式化I/O。主要分爲向外輸出內容和獲取輸入內容兩大部分 */
import (
	"fmt"
	"math/rand" # math/rand 額外導入的包
)
#
/*fmt向外輸出相關函數 Print系列函數會將內容輸出到系統的標準輸出 */
fmt.Print //Print函數直接輸出內容
fmt.Fprint //Printf函數支持格式化輸出字符串
fmt.Println //Println函數會在輸出內容的結尾添加一個換行符
fmt.Sprint
fmt.Errorf

fmt.Scan
fmt.Scanf 
fmt.Scanln

// 這裏比較全:https://www.cnblogs.com/wanghui-garcia/p/10326395.html
複製代碼

1.導出名

用首字母大小寫來區分公開,私有函數?

2.函數

  • 參考 這篇關於 Go 語法定義的文章瞭解類型以這種形式出現的緣由。
  • 當兩個或多個連續的函數命名參數是同一類型,則除了最後一個類型以外,其餘均可以省略。

3.多值返回

  • 函數能夠返回任意數量的返回值。 有點意思,這些不是難點
func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
	fmt.Println(a, b)
}
複製代碼

4.命名返回值

5.變量

var 語句定義了一個變量的列表;跟函數的參數列表同樣,類型在後面。

就像在這個例子中看到的同樣,var 語句能夠定義在包或函數級別。

6.初始化變量

變量定義能夠包含初始值,每一個變量對應一個。

若是初始化是使用表達式,則能夠省略類型;變量從初始值中得到類型。

7.短聲明變量

在函數中,:= 簡潔賦值語句在明確類型的地方,能夠用於替代 var 定義。

函數外的每一個語句都必須以關鍵字開始(varfunc、等等),:= 結構不能使用在函數外。

func main() {
	var i, j int = 1, 2
	k := 3
	c, python, java := true, false, "no!"

	fmt.Println(i, j, k, c, python, java)
}
複製代碼

8.基本類型

Go 的基本類型有Basic types

bool

string

int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 的別名

rune // int32 的別名 // 表明一個Unicode碼

float32 float64

complex64 complex128 這個例子演示了具備不一樣類型的變量。 同時與導入語句同樣,變量的定義「打包」在一個語法塊中。

9.零值

變量在定義時沒有明確的初始化時會賦值爲_零值_。

零值是:

數值類型爲 0, 布爾類型爲 false, 字符串爲 ""(空字符串)。

func main() {
	var i int
	var f float64
	var b bool
	var s string
	fmt.Printf("%v %v %v %q\n", i, f, b, s)
}
複製代碼

10.類型轉換

表達式 T(v) 將值 v 轉換爲類型 T

一些關於數值的轉換:

var i int = 42 var f float64 = float64(i) var u uint = uint(f) 或者,更加簡單的形式:

i := 42 f := float64(i) u := uint(f) 與 C 不一樣的是 Go 的在不一樣類型之間的項目賦值時須要顯式轉換。 試着移除例子中 float64 或 int 的轉換看看會發生什麼。

func main() {
	var x, y int = 3, 4
	var f float64 = math.Sqrt(float64(x*x + y*y))
	var z int = int(f)
	fmt.Println(x, y, z)
}
複製代碼

11.類型推導

在定義一個變量但不指定其類型時(使用沒有類型的 var 或 := 語句), 變量的類型由右值推導得出。

當右值定義了類型時,新變量的類型與其相同:

var i int j := i // j 也是一個 int 可是當右邊包含了未指名類型的數字常量時,新的變量就多是 int 、 float64 或 complex128。 這取決於常量的精度:

i := 42 // int f := 3.142 // float64 g := 0.867 + 0.5i // complex128 嘗試修改演示代碼中 v 的初始值,並觀察這是如何影響其類型的。

func main() {
	v := 42 // change me!
	fmt.Printf("v is of type %T\n", v)
}
複製代碼

12.常量

常量的定義與變量相似,只不過使用 const 關鍵字。

常量能夠是字符、字符串、布爾或數字類型的值。

常量不能使用 := 語法定義。

const Pi = 3.14

func main() {
	const World = "世界"
	fmt.Println("Hello", World)
	fmt.Println("Happy", Pi, "Day")

	const Truth = true
	fmt.Println("Go rules?", Truth)
}

複製代碼

13.數值常量

數值常量是高精度的

一個未指定類型的常量由上下文來決定其類型。

也嘗試一下輸出 needInt(Big) 吧。

package main

import "fmt"

const (
	Big   = 1 << 100
	Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
	return x * 0.1
}

func main() {
	fmt.Println(needInt(Small))
	fmt.Println(needFloat(Small))
	fmt.Println(needFloat(Big))
}

複製代碼

二.漸入佳境-流控語句

1.for

Go 只有一種循環結構——for 循環。

基本的 for 循環除了沒有了 ( ) 以外(甚至強制不能使用它們),看起來跟 C 或者 Java 中作的同樣,而 { } 是必須的。

func main() {
	sum := 0
	for i := 0; i < 10; i++ {
		sum += i
	}
	fmt.Println(sum)
}
複製代碼

跟 C 或者 Java 中同樣,可讓前置、後置語句爲空。

func main() {
	sum := 1
	for ; sum < 1000; {
		sum += sum
	}
	fmt.Println(sum)
}
複製代碼

2.for 是 Go 的 「while」

基於此能夠省略分號:C 的 while 在 Go 中叫作 for

func main() {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	fmt.Println(sum)
}
複製代碼

3.死循環

若是省略了循環條件,循環就不會結束,所以能夠用更簡潔地形式表達死循環。

func main() {
	for {
	}
}
複製代碼

4.if

if 語句除了沒有了 ( ) 以外(甚至強制不能使用它們),看起來跟 C 或者 Java 中的同樣,而 { } 是必須的。

(耳熟嗎?)

func sqrt(x float64) string {
	if x < 0 {
		return sqrt(-x) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

func main() {
	fmt.Println(sqrt(2), sqrt(-4))
}

複製代碼

5.if 的便捷語句

跟 for 同樣,if 語句能夠在條件以前執行一個簡單的語句。

由這個語句定義的變量的做用域僅在 if 範圍以內。

(在最後的 return 語句處使用 v 看看。)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	}
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}
複製代碼

6.if 和 else

在 if 的便捷語句定義的變量一樣能夠在任何對應的 else 塊中使用。

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		fmt.Printf("%g >= %g\n", v, lim)
	}
	// 這裏開始就不能使用 v 了
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}
複製代碼

練習:循環和函數

func Sqrt(x float64) float64 {
}

func main() {
	fmt.Println(Sqrt(2))
}
複製代碼

7.switch

一個結構體(struct)就是一個字段的集合。

除非以 fallthrough 語句結束,不然分支會自動終止。

複製代碼

func main() { fmt.Print("Go runs on ") switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.", os) } }

switch 的執行順序

switch 的條件從上到下的執行,當匹配成功的時候中止。

(例如,

switch i {
case 0:
case f():
}
複製代碼

當 i==0 時不會調用 f。)

注意:Go playground 中的時間老是從 2009-11-10 23:00:00 UTC 開始, 如何校驗這個值做爲一個練習留給讀者完成。

func main() {
	fmt.Println("When's Saturday?")
	today := time.Now().Weekday()
	switch time.Saturday {
	case today + 0:
		fmt.Println("Today.")
	case today + 1:
		fmt.Println("Tomorrow.")
	case today + 2:
		fmt.Println("In two days.")
	default:
		fmt.Println("Too far away.")
	}
}
複製代碼

沒有條件的 switch

沒有條件的 switch 同 switch true 同樣。

這一構造使得能夠用更清晰的形式來編寫長的 if-then-else 鏈。

func main() {
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
}
複製代碼

8.defer

defer 語句會延遲函數的執行直到上層函數返回。 延遲調用的參數會馬上生成,可是在上層函數返回前函數都不會被調用。

func main() {
	defer fmt.Println("world") // 上層函數也就是main函數了,這和js的defer執行順序很類似
	fmt.Println("hello")
}

# 執行結果
hello
world
複製代碼

js關於defer和async區別

defer 棧

延遲的函數調用被壓入一個棧中。當函數返回時, 會按照後進先出的順序調用被延遲的函數調用。

閱讀博文了解更多關於 defer 語句的信息。

func main() {
	fmt.Println("counting")

	for i := 0; i < 10; i++ {
		defer fmt.Println(i)
	}

	fmt.Println("done")
}
// 這個settimeout的作法基本相似
複製代碼

9.指針

爲何要有指針? 指針能夠提升性能,指針能夠節省複製的開銷,但同時要考慮解引用和垃圾回收帶來的影響。 www.jianshu.com/p/7e8ea1b83…

指針的好處:

1.函數的值傳遞,沒法經過調用函數,來修改函數的實參。

2.被調用函數須要提供更多的「返回值」,給調用函數。

3.指針能極大的提升效率(共享內存數據)。 blog.csdn.net/zengzisuzi/…

Go 具備指針。 指針保存了變量的內存地址。

類型 *T 是指向類型 T 的值的指針。其零值是 `nil`。

var p *int
& 符號會生成一個指向其做用對象的指針。

i := 42
p = &i
* 符號表示指針指向的底層的值。

fmt.Println(*p) // 經過指針 p 讀取 i
*p = 21         // 經過指針 p 設置 i
這也就是一般所說的「間接引用」或「非直接引用」。
複製代碼

與 C 不一樣,Go 沒有指針運算。「Go語言裏面的指針和C++指針同樣,都是指向某塊內存的地址值,能夠解引用,不一樣只是在於C++裏能夠直接對指針作算術運算而Go裏面不行。」

而 C#、java這些高級語言是沒有指針的。

10.結構體

一個結構體(struct)就是一個字段的集合。 (而 type 的含義跟其字面意思相符。)

結構體和類很是類似。

Go裏面它確實沒有類,要表示對象,它是用struct的(和C很像);Go語言,是一門沒有對象的OOP。

Go的面向對象 blog.csdn.net/u011606307/…

  1. 結構體字段使用點號來訪問。
  2. 結構體字段能夠經過結構體指針來訪問。經過指針間接的訪問是透明的。
  3. 結構體文法表示經過結構體字段的值做爲列表來新分配一個結構體。

使用 Name: 語法能夠僅列出部分字段。(字段名的順序無關。) 特殊的前綴 & 返回一個指向結構體的指針。

12.數組

類型 [n]T 是一個有 n 個類型爲 T 的值的數組。

表達式 var a [10]int 定義變量 a 是一個有十個整數的數組。

數組的長度是其類型的一部分,所以數組不能改變大小。 這看起來是一個制約,可是請不要擔憂; Go 提供了更加便利的方式來使用數組。

slice

一個 slice 會指向一個序列的值,而且包含了長度信息。

[]T 是一個元素類型爲 T 的 slice。

對 slice 切片

slice 能夠從新切片,建立一個新的 slice 值指向相同的數組。

表達式

s[lo:hi] 表示從 lo 到 hi-1 的 slice 元素,含兩端。所以

s[lo:lo] 是空的,而

s[lo:lo+1] 有一個元素。

構造 slice

slice 由函數 make 建立。這會分配一個零長度的數組而且返回一個 slice 指向這個數組:

a := make([]int, 5) // len(a)=5 爲了指定容量,可傳遞第三個參數到 make

b := make([]int, 0, 5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5 b = b[1:] // len(b)=4, cap(b)=4

nil slice

slice 的零值是 nil

一個 nil 的 slice 的長度和容量是 0。

向 slice 添加元素

向 slice 添加元素是一種常見的操做,所以 Go 提供了一個內建函數 append。 內建函數的文檔對 append 有詳細介紹。

func append(s []T, vs ...T) []T append 的第一個參數 s 是一個類型爲 T 的數組,其他類型爲 T 的值將會添加到 slice。

append 的結果是一個包含原 slice 全部元素加上新添加的元素的 slice。

若是 s 的底層數組過小,而不能容納全部值時,會分配一個更大的數組。 返回的 slice 會指向這個新分配的數組。

(瞭解更多關於 slice 的內容,參閱文章slice:使用和內幕。)

13.range

for 循環的 range 格式能夠對 slice 或者 map 進行迭代循環 range(續) 能夠經過賦值給 _ 來忽略序號和值。

若是隻須要索引值,去掉「, value」的部分便可。

練習:slice 實現 Pic。它返回一個 slice 的長度 dy,和 slice 中每一個元素的長度的 8 位無符號整數 dx。當執行這個程序,它會將整數轉換爲灰度(好吧,藍度)圖片進行展現。

圖片的實現已經完成。可能用到的函數包括 (x+y)/2 、 x*y 和 x^y(使用 math.Pow 計算最後的函數)。

(須要使用循環來分配 [][]uint8 中的每一個 []uint8。)

(使用 uint8(intValue) 在類型之間進行轉換。)

14.map

map 映射鍵到值。

map 在使用以前必須用 make 而不是 new 來建立;

map 的文法 map 的文法跟結構體文法類似,不過必須有鍵名。

map 的文法(續) 若是頂級的類型只有類型名的話,能夠在文法的元素中省略鍵名。

修改 map 在 map m 中插入或修改一個元素:

m[key] = elem 得到元素:

elem = m[key] 刪除元素:

delete(m, key) 經過雙賦值檢測某個鍵存在:

elem, ok = m[key] 若是 key 在 m 中,ok 爲 true 。不然, ok 爲 false,而且 elem 是 map 的元素類型的零值。

一樣的,當從 map 中讀取某個不存在的鍵時,結果是 map 的元素類型的零值。

練習:map 實現 WordCount。它應當返回一個含有 s 中每一個 「詞」 個數的 map。函數 wc.Test 針對這個函數執行一個測試用例,並輸出成功仍是失敗。

你會發現 strings.Fields 頗有幫助。

15.函數值

函數也是值。

16.函數的閉包

Go 函數能夠是閉包的。閉包是一個函數值,它來自函數體的外部的變量引用。 函數能夠對這個引用值進行訪問和賦值;換句話說這個函數被「綁定」在這個變量上。

例如,函數 adder 返回一個閉包。每一個閉包都被綁定到其各自的 sum 變量上。

練習:斐波納契閉包 如今來經過函數作些有趣的事情。

實現一個 fibonacci 函數,返回一個函數(一個閉包)能夠返回連續的斐波納契數。

相關文章
相關標籤/搜索