Linux中國

從 Node 到 Go:一個粗略的比較

在 XO 公司,我們最初使用 Node 和 Ruby 構建相互連接的服務系統。我們享受 Node 帶來的明顯性能優勢,以及可以訪問已有的大型軟體包倉庫。我們也可以輕鬆地在公司內部發布並復用已有的插件和模塊。極大地提高了開發效率,使得我們可以快速編寫出可拓展的和可靠的應用。而且,龐大的 Node 社區使我們的工程師向開源軟體貢獻更加容易(比如 BunnyBusFelicity)。

雖然我在大學時期和剛剛工作的一些時間在使用更嚴謹的編譯語言,比如 C++ 和 C#,而後來我開始使用 JavaScript。我很喜歡它的自由和靈活,但是我最近開始懷念靜態和結構化的語言,因為當時有一個同事讓我對 Go 語言產生了興趣。

我從寫 JavaScript 到寫 Go,我發現兩種語言有很多相似之處。兩者學習起來都很快並且易於上手,都具有充滿表現力的語法,並且在開發者社區中都有很多工作機會。沒有完美的編程語言,所以你應該總是選擇一個適合手頭項目的語言。在這篇文章中,我將要說明這兩種語言深層次上的關鍵區別,希望能鼓勵沒有用過 Go 語言的用戶可以有機會使用 Go 。

大體上的差異

在深入細節之前,我們應該先了解一下兩種語言之間的重要區別。

Go,或稱 Golang,是 Google 在 2007 年創建的自由開源編程語言。它以快速和簡單為設計目標。Go 被直接編譯成機器碼,這就是它速度的來源。使用編譯語言調試是相當容易的,因為你可以在早期捕獲大量錯誤。Go 也是一種強類型的語言,它有助於數據完整,並可以在編譯時查找類型錯誤。

另一方面,JavaScript 是一種弱類型語言。除了忽略驗證數據的類型和真值判斷陷阱所帶來的額外負擔之外,使用弱類型語言也有自己的好處。比起使用 介面 interfaces 范型 generics 柯里化 currying 可變的形參個數 flexible arity 讓函數變得更加靈活。JavaScript 在運行時進行解釋,這可能導致錯誤處理和調試的問題。Node 是一款基於 Google V8 虛擬機的 JavaScript 運行庫,這使它成為一個輕量和快速的 Web 開發平台。

語法

作為原來的 JavaScript 開發者,Go 簡單和直觀的語法很吸引我。由於兩種語言的語法可以說都是從 C 語言演變而來的,所以它們的語法有很多相同之處。Go 被普遍認為是一種「容易學習的語言」。那是因為它的對開發者友好的工具、精簡的語法和固守慣例(LCTT 譯註:慣例優先)。

Go 包含大量有助於簡化開發的內置特性。你可以用標準 Go 構建工具把你的程序用 go build 命令編譯成二進位可執行文件。使用內置的測試套件進行測試只需要運行 go test 即可。 諸如原生支持的並發等特性甚至在語言層面上提供。

Google 的 Go 開發者認為,現在的編程太複雜了,太多的「記賬一樣,重複勞動和文書工作」。這就是為什麼 Go 的語法被設計得如此簡單和乾淨,以減少混亂、提高效率和增強可讀性。它還鼓勵開發人員編寫明確的、易於理解的代碼。Go 只有 25 個保留關鍵字和一種循環(for 循環),而不像 JavaScript 有 大約 84 個關鍵字(包括保留關鍵字字、對象、屬性和方法)。

為了說明語法的一些差異和相似之處,我們來看幾個例子:

  • 標點符號: Go 去除了所有多餘的符號以提高效率和可讀性。儘管 JavaScript 中需要符號的地方也不多(參見: Lisp),而且經常是可選的,但我更加喜歡 Go 的簡單。
// JavaScript 的逗號和分號
for (var i = 0; i < 10; i++) {
console.log(i);
}

JavaScript 中的標點

// Go 使用最少數量標點
for i := 0; i < 10; i++ {
fmt.Println(i)
}

Go 中的標點

  • 賦值:由於 Go 是強類型語言,所以你在初始化變數時可以使用 := 操作符來進行類型推斷,以避免重複聲明,而 JavaScript 則在運行時聲明類型。
// JavaScript 賦值
var foo = "bar";

JavaScript 中的賦值

// Go 的賦值
var foo string //不使用類型推導
foo = "bar"
foo := "bar" //使用類型推導

Go 的賦值

  • 導出:在 JavaScript 中,你必須從某個模塊中顯式地導出。 在 Go 中,任何大寫的函數將被默認導出。
const Bar = () => {};
module.exports = {
Bar
}

JavaScript 中的導出

// Go 中的導出
package foo // 定義包名
func Bar (s string) string {
// Bar 將被導出
}

Go 中的導出

  • 導入:在 JavaScript 中 required 庫是導入依賴項和模塊所必需的,而 Go 則利用原生的 import 關鍵字通過包的路徑導入模塊。另一個區別是,與 Node 的中央 NPM 存儲庫不同,Go 使用 URL 作為路徑來導入非標準庫的包,這是為了從包的源碼倉庫直接克隆依賴。
// Javascript 的導入
var foo = require(&apos;foo&apos;);
foo.bar();

JavaScript 的導入

// Go 的導入
import (
"fmt" // Go 的標準庫部分
"github.com/foo/foo" // 直接從倉庫導入
)
foo.Bar()

Go 的導入

  • 返回值:通過 Go 的多值返回特性可以優雅地傳遞和處理返回值和錯誤,並且通過傳遞引用減少了不正確的值傳遞。在 JavaScript 中需要通過一個對象或者數組來返回多個值。
// Javascript - 返回多值
function foo() {
return {a: 1, b: 2};
}
const { a, b } = foo();

JavaScript 的返回

// Go - 返回多值
func foo() (int, int) {
return 1, 2
}
a, b := foo()

Go 的返回

  • 錯誤處理:Go 推薦在錯誤出現的地方捕獲它們,而不是像 Node 一樣在回調中讓錯誤冒泡。
// Node 的錯誤處理
foo(&apos;bar&apos;, function(err, data) {
// 處理錯誤
}

JavaScript 的錯誤處理

//Go 的錯誤處理
foo, err := bar()
if err != nil {
// 用 defer、 panic、 recover 或 log.fatal 等等處理錯誤.
}

Go 的錯誤處理

  • 可變參數函數:Go 和 JavaScript 的函數都支持傳入不定數量的參數。
function foo (...args) {
console.log(args.length);
}
foo(); // 0
foo(1, 2, 3); // 3

JavaScript 中的可變參數函數

func foo (args ...int) {
fmt.Println(len(args))
}
func main() {
foo() // 0
foo(1,2,3) // 3
}

Go 中的可變參數函數

社區

當比較 Go 和 Node 提供的編程範式哪種更方便時,兩邊都有不同的擁護者。Node 在軟體包數量和社區的大小上完全勝過了 Go。Node 包管理器(NPM),是世界上最大的軟體倉庫,擁有超過 410,000 個軟體包,每天以 555 個新軟體包的驚人速度增長。這個數字可能看起來令人吃驚(確實是),但是需要注意的是,這些包許多是重複的,且質量不足以用在生產環境。 相比之下,Go 大約有 13 萬個包。

Node 和 Go 包的數量

儘管 Node 和 Go 歲數相仿,但 JavaScript 使用更加廣泛,並擁有巨大的開發者和開源社區。因為 Node 是為所有人開發的,並在開始的時候就帶有一個強壯的包管理器,而 Go 是特地為 Google 開發的。下面的Spectrum 排行榜顯示了當前流行的的頂尖 Web 開發語言。

Web 開發語言排行榜前 7 名

JavaScript 的受歡迎程度近年來似乎保持相對穩定,而 Go 一直在保持上升趨勢

編程語言趨勢

性能

如果你的主要關注點是速度呢?當今似乎人們比以前更重視性能的優化。用戶不喜歡等待信息。 事實上,如果網頁的載入時間超過 3 秒,40% 的用戶會放棄訪問您的網站

因為它的非阻塞非同步 I/O,Node 經常被認為是高性能的語言。另外,正如我之前提到的,Node 運行在針對動態語言進行了優化的 Google V8 引擎上。而 Go 的設計也考慮到速度。Google 的開發者們通過建立了一個「充滿表現力而輕量級的類型系統;並發和垃圾回收機制;強制地指定依賴版本等等」,達成了這一目標。

我運行了一些測試來比較 Node 和 Go 之間的性能。這些測試注重於語言提供的初級能力。如果我準備測試例如 HTTP 請求或者 CPU 密集型運算,我會使用 Go 語言級別的並發工具(goroutines/channels)。但是我更注重於各個語言提供的基本特性(參見 三種並發方法 了解關於 goroutines 和 channels 的更多知識)。

我在基準測試中也加入了 Python,所以無論如何我們對 Node 和 Go 的結果都很滿意。

循環/算術

迭代十億項並把它們相加:

var r = 0;
for (var c = 0; c < 1000000000; c++) {
    r += c;
}

Node

package main
func main() {
    var r int
    for c := 0; c < 1000000000; c++ {
        r += c
    }
}

Go

sum(xrange(1000000000))

Python

結果

這裡的輸家無疑是 Python,花了超過 7 秒的 CPU 時間。而 Node 和 Go 都相當高效,分別用了 900 ms 和 408 ms。

修正:由於一些評論表明 Python 的性能還可以提高。我更新了結果來反映這些變化。同時,使用 PyPy 大大地提高了性能。當使用 Python 3.6.1 和 PyPy 3.5.7 運行時,性能提升到 1.234 秒,但仍然不及 Go 和 Node 。

I/O

遍歷一百萬個數字並將其寫入一個文件。

var fs = require(&apos;fs&apos;);
var wstream = fs.createWriteStream(&apos;node&apos;);

for (var c = 0; c < 1000000; ++c) {
  wstream.write(c.toString());
}
wstream.end();

Node

package main

import (
    "bufio"
    "os"
    "strconv"
)

func main() {
    file, _ := os.Create("go")
    b := bufio.NewWriter(file)
    for c := 0; c < 1000000; c++ {
        num := strconv.Itoa(c)
        b.WriteString(num)
    }
    file.Close()
}

Go

with open("python", "a") as text_file:
    for i in range(1000000):
        text_file.write(str(i))

Python

結果

Python 以 7.82 秒再次排名第三。 這次測試中,Node 和 Go 之間的差距很大,Node 花費大約 1.172 秒,Go 花費了 213 毫秒。真正令人印象深刻的是,Go 大部分的處理時間花費在編譯上。如果我們將代碼編譯,以二進位運行,這個 I/O 測試僅花費 78 毫秒——要比 Node 快 15 倍。

修正:修改了 Go 代碼以實現緩存 I/O。

冒泡排序

將含有十個元素的數組排序一千萬次。

function bubbleSort(input) {
    var n = input.length;
    var swapped = true;
    while (swapped) {
        swapped = false;
        for (var i = 0; i < n; i++) {
            if (input[i - 1] > input [i]) {
                [input[i], input[i - 1]] = [input[i - 1], input[i]];
                swapped = true;
            }
        }
    }
}

for (var c = 0; c < 1000000; c++) {
    const toBeSorted = [1, 3, 2, 4, 8, 6, 7, 2, 3, 0];
    bubbleSort(toBeSorted);
}

Node

package main

var toBeSorted [10]int = [10]int{1, 3, 2, 4, 8, 6, 7, 2, 3, 0}

func bubbleSort(input [10]int) {
    n := len(input)
    swapped := true
    for swapped {
        swapped = false
        for i := 1; i < n; i++ {
            if input[i-1] > input[i] {
                input[i], input[i-1] = input[i-1], input[i]
                swapped = true
            }
        }
    }
}

func main() {
    for c := 0; c < 1000000; c++ {
        bubbleSort(toBeSorted)
    }
}

Go

def bubbleSort(input):
    length = len(input)
    swapped = True

    while swapped:
        swapped = False
        for i in range(1,length):
            if input[i - 1] > input[i]:
                input[i], input[i - 1] = input[i - 1], input[i]
                swapped = True

for i in range(1000000):
    toBeSorted = [1, 3, 2, 4, 8, 6, 7, 2, 3, 0]
    bubbleSort(toBeSorted)

Python

結果

像剛才一樣,Python 的表現是最差的,大約花費 15 秒完成了任務。 Go 完成任務的速度是 Node 的 16 倍。

判決

Go 無疑是這三個測試中的贏家,而 Node 大部分表現都很出色。Python 也表現不錯。要清楚,性能不是選擇編程語言需要考慮的全部內容。如果您的應用不需要處理大量數據,那麼 Node 和 Go 之間的性能差異可能是微不足道的。 有關性能的一些其他比較,請參閱以下內容:

結論

這個帖子不是為了證明一種語言比另一種語言更好。由於各種原因,每種編程語言都在軟體開發社區中佔有一席之地。 我的意圖是強調 Go 和 Node 之間的差異,並且促進展示一種新的 Web 開發語言。 在為一個項目選擇語言時,有各種因素需要考慮,比如開發人員的熟悉程度、花費和實用性。 我鼓勵在決定哪種語言適合您時進行一次徹底的底層分析。

正如我們所看到的,Go 有如下的優點:接近底層語言的性能,簡單的語法和相對簡單的學習曲線使它成為構建可拓展和安全的 Web 應用的理想選擇。隨著 Go 的使用率和社區活動的快速增長,它將會成為現代網路開發中的重要角色。話雖如此,我相信如果 Node 被正確地實現,它正在向正確的方向努力,仍然是一種強大而有用的語言。它具有大量的追隨者和活躍的社區,使其成為一個簡單的平台,可以讓 Web 應用在任何時候啟動和運行。

資料

如果你對學習 Go 語言感興趣,可以參閱下面的資源:

via: https://medium.com/xo-tech/from-node-to-go-a-high-level-comparison-56c8b717324a#.byltlz535

作者:John Stamatakos 譯者:trnhoe 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國