TS(JS)與 Go

編譯原理

JavaScript 是一門解釋型語言或即時編譯型語言,在運行時經過編譯生成二進制機器碼,它的運行大體通過如下幾個階段(以 V8 引擎爲例):html

若是使用了 TypeScript,則在運行以前 TypeScript 將會編譯成 JavaScript 代碼。git

V8編譯過程

v8 引擎首先會解析源碼,生成抽象語法樹(AST),基於 AST,解釋器即可以開始工做生成字節碼,通過編譯器後生成能夠運行的機器碼。github

Go 是一門編譯型語言,在代碼***運行以前***須要經過編譯器生成二進制機器碼。它的編譯過程大體以下:golang

WechatIMG799.png

代碼首先會被掃描(詞法分析)生成 token,後通過 Parser(語法分析) 生成 AST,接着會有一個類型檢查的階段,一般叫作語義分析,生成中間代碼,後通過優化處理後最終生成目標機器碼。typescript

靜態類型

TypeScript 和 Go 都是靜態類型語言。函數

對於 TypeScript,筆者時常看到有關於它的各類「騷操做」,好比從 A | B 獲得 A & B優化

type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never interface A { name: string; } interface B { age: number; } type Res = UnionToIntersection<A | B>; // A & B 複製代碼

或者是:ui

type GetArrayMembers<T> = T extends {[index in keyof T]: infer V } ? V : never
const example = [1, 2, 3] as const;
type Members = GetArrayMembers<typeof example>; // 1, 2, 3
複製代碼

這些「騷操做」,幾乎不多有人第一眼就能徹底明白其中的含義。甚至多少有點「秀的我腦袋疼」的感受。this

可是考慮到 TypeScript 定義爲 JavaScript 的超級,類型系統設計的這麼複雜也就不足爲奇了,畢竟 JavaScript 是「怎麼靈活怎麼來」。spa

對於 Go 語言,在 1.x 版本中,它的靜態類型常常被調侃成「大道至簡」。其中缺乏「泛型」一直被列爲該語言須要修復的三大問題之一,探其緣由,無非就是「這個需求我不接」之類的套路話了。不過好在在 2.0 版本中,Go 語言將會實現「泛型」這個功能。

此外,對於咱們時常糾結「何時該用 interfacetype」 的問題,Go 語言對此作了很好的限制,它使用了一個新的 struct,而 interface 則被限制爲一組抽象方法的集合:

package main;

import "fmt";

type Human struct {
	name string;
	age int;
	phone string;
}

// go 裏方法(函數和方法不相同)
// Human 實現 SayHi 方法
func (h Human) SayHi() {
	fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone);
}


type Men interface {
	SayHi()
}

func main() {
	mike := Human{ "mike", 25, "137xxx"};

	var i Men;

	i = mike;
	i.SayHi();
}
複製代碼

最後,在 TypeScrip 中,你可使用 any 來規避編譯器的類型檢查,在 Go 中,可使用空 interface 實現與此類似的做用:

let a: any = 1;
a = 'hello';
複製代碼
var a interface{}
var i int = 5;
s := "Hello world";
a = i;
a = s;
複製代碼

面向對象與繼承

JavaScript 是一種基於原型繼承的語言,自 ES6 發佈之後,可使用 classextends 等語法糖來替代之前繁瑣的寫法。TypeScript 在此基礎上,擴展了類的能力,如添加 publicprivateprotected 等修飾符。

class AChild {
  protected name = 'hello world'
}

class PA extends AChild {
  say() {
    return this.name;
  }
}
複製代碼

在 Go 語言中,因爲沒有構造函數,實現面向對象時,你須要這麼寫:

package main;

import "fmt";

type Human struct {
	name string;
	age int;
	phone string;
}

type Employ struct {
	Human; // 匿名字段
	company string;
}

// Human 實現 SayHi 方法
func (h Human) SayHi() {
	fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone);
}

// Human 實現 Sing 方法
func (h Human) Sing() {
	fmt.Printf("hahaha");
}

// Employee 重載 Human 的 SayHi 方法
func (e Employ) SayHi() {
	fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone)
}

func main() {
	mike := Employ { Human { "mike", 25, "137xxx" }, "上海" };

	fmt.Printf("name: %s, age: %d. phone: %s, company: %s", mike.name, mike.age, mike.phone, mike.company); // name: mike, age: 25, phone: 137xxx. company: 上海
	mike.Sing();    // hahaha
	mike.SayHi();   // Hi, I am mike, I work at 千尋. Call me on 137xxx
}
複製代碼

其餘

  • 不用再糾結該用 export default 仍是 export { xxx } (因爲某些緣由 筆者向來都是推薦使用 export { xxx } 的形式),在 GO 中,當變量或者函數首字母大寫,會被認爲是導出(公有)。

  • 沒有了 this 帶來的問題。

  • go get xxx 裝包,可獲取一個可執行的二進制文件。

  • ...


歡迎關注公衆號

相關文章
相關標籤/搜索