go的类型及接口实现

go结构化类型系统,类似与duck typing

go语言是编译时就会绑定的,从动态绑定的角度,不是duck typing

具有c++、python的灵活性,实现对应的方法就可以运行,又想要java的类型检查

go的接口实现是隐式的,只要实现接口里的方法就可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

// /type_interface/mock/Retriever.go
package mock
type Retriever1 struct {
Contents string
}

func (r Retriever1) Get(str string) string{
return r.Contents
}


// /type_interface/real/Retriever.go
package real
import (
"net/http"
"net/http/httputil"
"time"
)

type Retriever2 struct {
UserAgent string
TimeOut time.Duration
}

func (r *Retriever2) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}

result, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}

接口变量里有什么

  1. 实现者的类型
  2. 实现者的指针(实现者的值)

接口变量的特点

  • 接口变量里面自带指针
  • 接口变量同样采用值传递,几乎不需要使用接口的指针
  • 指针接受者实现只能以指针方式使用,而值接受者两种都可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// /type_interface/main.go
package main

import (
"fmt"
"type_interface/mock"
real2 "type_interface/real"
)

type Retriever3 interface {
Get(url string) string
}

func download(r Retriever3) string {
return r.Get("http://www.baidu.com")
}


func inspect(r Retriever3) {
//
switch v := r.(type) {
case mock.Retriever1:
fmt.Println("mock.Retriever1: ", v.Contents)
case *real2.Retriever2:
fmt.Println("*real2.Retriever2: ", v.UserAgent)
}
}

func main() {
// 声明一个接口变量
var r Retriever3

// 使用mock.Retriever1来定义这个接口变量
r = mock.Retriever1{Contents: "this is faker, you know?"} // 值类型还是指针都可
inspect(r)

// 使用real2.Retriever2来定义这个接口变量
r = &real2.Retriever2{ //必须为指针类型
UserAgent: "chrome 19.0",
TimeOut: time.Minute,
}
inspect(r)

//fmt.Println(download(r))

// 类型断言
realRetriever := r.(*real2.Retriever2)
fmt.Println(realRetriever.TimeOut)

// 类型断言可能会失败,失败ok为false
if mockRetriever, ok := r.(mock.Retriever1); ok {
fmt.Println(mockRetriever.Contents)
} else {
fmt.Printf("r is not a mockRetriever")
}

}

查看接口变量

  1. 表示任何变量: interface{}
  2. Type Assertion
  3. Type Switch

接口的组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

// /type_interface/mock/Retriever.go
package mock

type Retriever1 struct {
Contents string
}

// 实现系统中的接口 ctrl + enter 可以显示针对类型的实现下拉
func (r *Retriever1) String() string {
return fmt.Sprintf("实现的string方法返回的内容是 Retriever1: {Contents=%s} ", r.Contents)
}


func (r *Retriever1) Post(url string, form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}

func (r *Retriever1) Get(str string) string {
return r.Contents
}



// /type_interface/main.go
type Retriever3 interface {
Get(url string) string
}

type Poster interface {
Post(url string, form map[string]string) string
}

type RetrieverPoster interface {
Retriever3
Poster
}

func session(s RetrieverPoster) string {
s.Post(url, map[string]string{
"name": "edit",
"contents": "faker is here right",
})
return s.Get(url)
}

func printFlie(filename string) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}

func main(){
var r Retriever3
r = &mock.Retriever1{Contents: "this is faker, you know?"}

fmt.Printf(session(r.(*mock.Retriever1))) // faker is here right

fmt.Println(r) // 实现的string方法返回的内容是 Retriever1: {Contents=this is faker, you know?}
}

go面向对象

go 仅支持封装,不支持继承和多态

Go 没有 class,只有 struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type treeNode struct {
value int
left, right *treeNode
}

// go可以将局部变量返回给外部使用,跟js很像
func createTreeNode(val int) *treeNode {
return &treeNode{value: val}
}

var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode) // new内建函数,返回treeNode的地址
root.right.left.right = createTreeNode(12) //不需要了解局部变量是分配在栈还是堆

nodes := []treeNode{
{value: 3},
{},
{value: 5, left: nil, right: nil},
{5, nil, &root},
}

fmt.Println(nodes)

为 struct 定义方法

  1. 编译器会自行判断方法需要的是值还是指针
  2. 值接受者和指针接受者
  3. 指针储存的是一个值的地址,但本身这个指针也需要地址来储存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

func (node treeNode) print() {
fmt.Print(node.value)
}

// 编译器会自行判断方法需要的是值还是指针,
// 需要实例的指针传递进来,才能改变实例的属性值
func (node *treeNode) setVal(val int) treeNode { //指针接收者里的 *treeNode 代表的是类型
if node == nil {
fmt.Printf("node is not exist")
return
}
node.value = val
return *node // 在这里*node不再代表类型,而是表示node指针所指向的值 如果单纯返回node,那它的类型就是*treeNode,而返回*node就是返回node的值(类型为treeNode)
}


var node = treeNode{value: 1}
node.setVal(25) // 调用时会将地址传递进方法内
node.print() // 调用时会将值copy

// 直接使用.调用方法会报错,因为treeNode{value: 1}返回的是值,需要取对象的引用地址来调用方法
var node2 = (&treeNode{value: 1}).setVal(5)
node2.print()

// new方法可以直接返回构建对象的指针
// func new(Type) *Type
var node3 = new(treeNode).setVal(15)
node3.print()

// 值为nil也可以调用类型的方法
var node2 *treeNode
node2.setVal(200)

&T{}相当于 new(T)

go的基础知识

go 的一些基础知识和用法

变量声明

  • 使用 var 关键字
1
2
3
4
5
6
7
8
9
10
11
// 可在函数内,也可在包内
var a int
var a,b int = 1,2
var a,b,c = 1,true,"def"

// 使用var()集中定义变量
var (
a = 1
b = true
c = "def"
)
  • 编译器自动判断类型
1
2
// 只能在函数内使用
a,b,c: = 1,true,"def"

强制类型转换

1
2
var a,b int = 3,4
var c int = int(math.Sqrt(float64(a*a + b*b)))

常量和枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const a string = "def"

// 没有char 只有rune

func enum(){
const (
java = iota
python
golang
javascript
)
const (
b = 1 << (10*iota)
kb
mb
gb
tb
pb
)
}

条件语句

  • if 条件语句
1
2
3
4
5
6
7
8

// 条件语句里面可以进行变量赋值,变量作用域就在if的block里
const filename = "a.txt"
if content, err := ioutil.ReadFile(filename); err != nil {
fmt.println(err)
} else {
fmt.printf("%s", content)
}
  • switch 条件语句
1
2
3
4
5
6
7
8
switch {
case score < 60 :
fmt.printf("不及格")
case score > 60 && score <100 :
fmt.printf("合格")
default:
panic(fmt.Sprintf("wrong score: %d",score))
}
  • for 循环
1
2
3
4
5
6
7
8
9
10
11
12
13
// 死循环
for{

}

// 单语句
for a>2 {
a--
}

for a :=1; a<10; a++{

}

函数

函数 return 方式

1
2
3
4
5
6
7
8
9
10
11
12
13
func div(a,b int) (q,r int){
q = a / b
r = a % b
return
}

func sum (numbers ...int) int{
sum := 0
for i := range numbers {
sum += numbers[i]
}
return sum
}

指针

go 语言里面,函数传递都是用的值传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func swap (a, b *int){
*a, *b = *b, *a
}

func swap2 (a,b int)( int, int ){
return b, a
}

a, b := 3, 4
swap(&a, &b)
swap2(a ,b)

// *a代表的是变量a所在地址的值,更改*a会导致函数外部的a也改变
func adjust(a *int) *int {
var b int = 2
*a = 20 // 这里导致函数外部a改为20
a = &b // 这里只是函数内部的a改为了b的指针
return a
}

var i int = 3
adjust(&i)
fmt.Printf("value: %d \n", i) // value: 20

数组

数组是值类型,函数传参会 copy 数组

1
2
3
4
5
6
7
8
9
10
11
// 数组声明
var arr1 [5]int
// [3]
var arr2 = [3]int{1, 23, 4}
arr3 := [...]int{1, 23, 4, 5, 6} // 根据定义确定类型长度
var arr4 [4][5]int

sum := 0
for _, v := range numbers{
sum += v
}
  • slice 切片
    1. slice 就是数组的视图,对切片进行赋值操作,就是数组进行的操作
    2. slice 包含三个值,指针,len,cap(容量)
1
2
3
4
5
6
7
8
9
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println(arr[2:6], arr[1:], arr[:6], arr[:])

arr1 := arr[2:]
arr1[0] = 100

// cap 和 len
fmt.Println(arr) // [0 1 100 3 4 5 6 7]
fmt.Println(arr1, len(arr1), cap(arr1)) // [100 3] 2 6
  • slice 的操作
    1. 添加元素时,如果超越 cap,系统会重新分配更大的底层数组
    2. 由于值传递的关系,必须要接收 append 的结果 s = append(s, val)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := append(arr[5:], 8)
s2 := append(s1, 110)
s3 := append(s1, 110, 112, 119)
s2[0] = 12

fmt.Println(arr, len(arr), cap(arr)) // [0 1 2 3 4 5 6 7] 8 8

// s2添加元素没有超越s1的cap
fmt.Println(s1, len(s1), cap(s1)) // [12 6 7 8] 4 6
fmt.Println(s2, len(s2), cap(s2)) // [12 6 7 8 110] 5 6

// s3添加元素超越了s1的cap,重新分配了新内存来保存s3
fmt.Println(s3, len(s3), cap(s3)) // [5 6 7 8 110 112 119] 7 12
1
2
3
4
5
6
7
8
9
10
11
12
s1 = []int{1, 23, 4, 5}
s2 = make([]int, 5, 32)

// 复制slice
copy(s2, s1) // s2 = [1 23 4 5 0]

// 删除slice中间第二个元素
s2 = append(s2[:2], s2[3:]...) // [1 23 5 0]

// 删第一个元素
s2 = s2[1:] // [23 5 0]
s2 = s2[:len(s2)-1] // [23 5]

Map

  • Map 的创建
1
2
3
4
5
m := map[string]string {
"name": "lee"
}
m1 := make(map[string]string) // m1 == empty map
var m2 map[string]string // m2 == nil
  • Map 的赋值、取值、删除
    1. key 不存在的时候,返回 value 类型的初始值
    2. map 使用哈希表,除了 slice,map,function 的内建类型都可以作为 key(struct 类型不包含上述字段,也可以作为 key)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
m := map[string]string {
"name": "lee",
"desc": "I'm lee"
}

for key, val := range m {
fmt.Println(key, val)
}

// 取值
name := m["name"]
// 赋值
m["name"] = "li"

// 可以根据获取状态来判断是否有值
if cname, ok := m["cname"]; ok{
fmt.Println(cname)
} else {
fmt.Printf("key doesn't exist !")
}

// 删除属性
delete(m, desc)

获取字符串的最长子串

  • rune 相当于 go 的 char
    1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

str := "test,为了测试"

for i, char := range str {
fmt.Println(i, char)
}

// 获取字符数量
fmt.Println(utf8.RuneCountInString(str))

bytes := []byte(str)
for len(bytes) > 0 {
ch, size := utf8.DecodeRune(bytes)
bytes = bytes[size:]
fmt.Println(ch, size, bytes)
}

var longestStrInString = func(str string) int {
var length int = 0
var start int = 0
lastOcc := make(map[rune]int)
for i, char := range []rune(str) {
fmt.Printf("(%c %d) ", char, i)
if index, ok := lastOcc[char]; ok && index >= start {
start = i
}
if i-start+1 > length {
length = i - start + 1
}
lastOcc[char] = i
}
fmt.Println()
return length
}

fmt.Println(longestStrInString("pwbds6786868767867wbertyuio")) // 11
fmt.Println(longestStrInString("这里是你最偶偶的地方")) // 6

float四舍五入转化为int

1
2
3
4
5
6
7
8
9
around := func(f float64) int {
val := math.Floor(f)
if f-val > 0.5 {
val = math.Ceil(f)
}
return int(val)
}
fmt.Println(around(5.2))
fmt.Println(around(16.9))

regexp小例子

regexp的匹配内容的替代

$选择符是在替换的时候进行的标志或选择,但是如果在正则表达式本身,就要使用\选择了

分组捕获,使用()进行数据分组,编号0代表整个匹配项,选择的分组从1号开始

1
2
//$1, $2代表的是匹配的第一个和第二个分组, 这里相当于调换了两个分组的位置
'guizhun tu'.replace(/(\w+)\s(\w+)/, '$2 $1') //tu guizhun

\用在正则表达式自己身上

1
2
3
4
5
6
7
var s = 'aaabbbcccaaabbbaaa';
var a = s.split('').sort().join(""); //"aaaaaaaaabbbbbbccc"
var ans = a.match(/(\w)\1+/g); //\1代表第一个分组匹配的内容, 这里 (\w)\1 相当于两个相同的字符
ans.sort(function(a,b) {
return a.length - b.length;
})
console.log('ans is : ' + ans[ans.length-1])

trim方法模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1. 提取中间关键字符, 使用的分组引用
const trim1 = (str) => {
  return str.replace(/^\s*(.*?)\s*$/'$1')
}
// 2. 去掉开头和结尾的空字符
const trim2 = (str) => {
    return str.replace(/^\s*|\s*$/g'')
}


#### 首字母大写
```js
const titleize = (str) => {
  return str.toLowerCase().replace(/(?:^|\s)\w/g, (c) => c.toUpperCase())
}  

console.log(titleize('my name is epeli')) // My Name Is Epeli

// 拓展,横向转驼峰,例如base-act-tab => baseActTab
'base-act-tab'.replace(/(?:-)(\w)/g, ($0, $1) => $1.toUpperCase()) // baseActTab

html转义规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const escapeHTML = (str) => {
    const escapeChars = {
    '<''lt',
    '>''gt',
    '"''quot',
    ''': '#39',
    '&''amp'
  }
  
  let regexp = new RegExp(`[${Object.keys(escapeChars).join('')}]`'g'// 为了得到字符组[<>"'&]
    
    return str.replace(regexp, (c) => `&${escapeChars[ c ]};`)
}

console.log( escapeHTML('<div>Blah blah blah</div>')) // &lt;div&gt;Blah blah blah&lt;/div&gt;

反转义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const unescapseHTML = (str) => {
    const htmlEntities = {
    nbsp: ' ',
    lt: '<',
    gt: '>',
    quot: '"',
    amp: '&',
    apos: '''
  }
  
  return str.replace(/&([^;]+);/g, ($0, $1) => {
        return htmlEntities[ $1 ] || ''
    })
}

console.log(unescapseHTML('&lt;div&gt;Blah blah blah&lt;/div&gt;')) // <div>Blah blah blah</div>

匹配成对标签

1
2
3
4
let reg = /<([^>]+)>.*?</\1>/g

console.log(reg.test('<title>regular expression</title>')) // true
console.log(reg.test('<p>laoyao bye bye</div>')) // false

Promise标准及实现

Promise标准解读

  1. 只有一个then方法,没有catch,race,all等方法,甚至没有构造函数
    Promise标准中仅指定了Promise对象的then方法的行为,其它一切我们常见的方法/函数都并没有指定,包括catch,race,all等常用方法,甚至也没有指定该如何构造出一个Promise对象,另外then也没有一般实现中(Q, $q等)所支持的第三个参数,一般称onProgress

  2. then方法返回一个新的Promise

  3. 不同Promise的实现需要可以相互调用(interoperable)

  4. Promise的初始状态为pending,它可以由此状态转换为fulfilled(本文为了一致把此状态叫做resolved)或者rejected,一旦状态确定,就不可以再次转换为其它状态,状态确定的过程称为settle

promise实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
try {
module.exports = Promise
} catch (e) { }


function Promise(exec) {
var self = this;
self.status = 'pending';
self.onResolvedCallback = [];
self.onRejectedCallback = [];

function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
setTimeout(function () {
if (self.status === 'pending') {
self.status = 'resolved';
self.data = value;
for (var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value);
}
}
})
}
function reject(reason) {
setTimeout(function () {
if (self.status === 'pending') {
self.status = 'rejected';
self.data = reason;
for (var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason);
}
}
})
}

try {
exec(resolve, reject);
} catch (error) {
reject(err)
}
}

function resolvePromise(promise2, x, resolve, reject) {
var then
var thenCanceldOrThrow = false

if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise!'))
}
if (x instanceof Promise) {
if (x.status === 'pending') {
x.then(function (v) {
resolvePromise(promise2, v, resolve, reject);
}, reject);
} else {
x.then(resolve, reject);
}
return
}

if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
try {
then = x.then
if (typeof then === 'function') {
then.call(x, function rs(val) {
if (thenCanceldOrThrow) return
thenCanceldOrThrow = true;
return resolvePromise(promise2, val, resolve, reject)
}, function rj(reason) {
if (thenCanceldOrThrow) return
thenCanceldOrThrow = true;
return reject(reason);
})
} else {
resolve(x)
}
} catch (err) {
if (thenCanceldOrThrow) return
thenCanceldOrThrow = true;
return reject(err);
}
} else {
resolve(x)
}
}

Promise.prototype.then = function (onResolved, onRejected) {
var self = this;
var promise2;

onResolved = typeof onResolved === 'function' ? onResolved : function (value) { return value };
onRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason };

if (self.status === 'resolved') {
return promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
var x = onResolved(self.data);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
})
})
}

if (self.status === 'rejected') {
return promise2 = new Promise(function (resolve, reject) {
setTimeout(function (reason) {
try {
var x = onRejected(self.data);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
})
})
}

if (self.status === 'pending') {
return promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallback.push(function (value) {
try {
var x = onResolved(value);
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error);
}
});
self.onRejectedCallback.push(function (reason) {
try {
var x = onRejected(reason);
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error);
}
});
})
}
}


Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected)
}

Promise.deferred = Promise.defer = function () {
var dfd = {}
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}