— WORK IN PROGRESS —

book cover

Introduction

Preface

Often, a developer will use more than one programming language at a certain timeframe. Switching back and forth between languages can come with some overhead. These context switches can also result in bugs. For instance, if you switch back and forth between Python and Javascript, there’s a likelihood you’ll mistake evaluation of an empty array between truthy and falsey. Similarly, if you switch back and forth between Go and Javascript, there’s a likelihood you’ll mistake switch statements default behavior of break/fallthrough. Outlining the differences between languages can help mitigate these potential issues, and make it easier to transition back and forth.

This document compares between two programming languages, Golang (or “Go”) and ECMAScript (or “Javascript” / “JS”). The merits of this pairing is the popularity of these languages. That’s it. They are not similar, in fact, they are quite different. Javascript is an event driven, dynamically typed and interpreted language, while Go is a statically typed and compiled language.

If you’re reading this there’s a high chance you already know your Javascript and are just starting with Go. If so, make sure you first complete A tour of go and Effective go.

Which language should I use?

You should always pick the right tool for the right job. Unfortunately, there will never be a simple formula that will instruct you which programming language you should choose to complete a given task.

science is more art than science

Aside of technical considerations, other considerations, such as community adoption are also important. It was reported that Facebook moved away from the Erlang language because it was hard to find qualified programmers.

Having said that, it is worthy to note that Javascript excels in I/O intense applications, and less so in CPU intense applications.

Semantics

Each subchapter/subject is denoted with (D),(S) or (B) to indicate how it compares across both languages with ‘mostly Different’, ‘mostly Similar’ or ‘mostly Both’.

This document uses ECMAScript 2015 (ES6).
Also, some subjects will note the run-time environment “NodeJS”.

Contributions

This document is a work in progress. Contributions and PRs are most welcomed.
If you edit the chapters layout, be sure to rebuild the table of contents by

  1. npm install
  2. npm run toc

If you edit go code, be sure to format it with (requires mdgofmt-cli)

  1. npm run fmt

Or just run both commands with

  1. npm run build

Internals

(S) Heap/Stack Memory Allocation

Concepts such “Heap” and “Stack” are abstracted away in both languages. You do not needed to worry about it. Even though GO has pointers, it uses escape analysis in compile-time to figure out the memory allocation.

(S) Garbage Collection

Garbage collection is implemented in both languages.

(D) Compilation

Go is compiled. Javascript is not, though some Javascript runtimes use JIT compilation. From the developer experience perspective, the biggest effect of compiled languages is compile-time safety. You get compile-time safety with Go, while in Javascript you can use external code linters to ease the missing of this feature.

Concurrency & Parallelism

(D) Overview

JS

The best way to describe Parallelism in Javascript is with this quote by Felix Geisendörfer:

Well, in node everything runs in parallel, except your code.

So while your Javascript runtime may use multiple threads for IO, your own code is getting run just by one. That’s just how the evented model works.
Different Javascript runtimes offer some options for concurrency or parallelism: NodeJS offers clustering, and Browsers offer web workers.

Go

On the other hand, Go is all about a concurrency witch enables parallelism. It offers Goroutines which enables functions to run concurrently, and channels to communicate between them. While Go standard library has the “sync” package for synchronization primitives, it encourages more the use of Goroutines and channels, summarized as:

Do not communicate by sharing memory; instead, share memory by communicating

More on this subject:

(D) Async vs Sync APIs

JS

JS promotes writing async APIs, since sync APIs always block the caller, e.g:

  1. const fs = require('fs');
  2. // The caller to this function will be blocked while the file is being read.
  3. function fetchA() {
  4. return fs.readFileSync('a.txt');
  5. }

In the example above, the async fs.readFile() would be a better choice in most scenarios, making fetchA() async and unblocking its caller.

Go

On the other hand, Go promotes the sync APIs (see “Avoid concurrency in your API” in https://talks.golang.org/2013/bestpractices.slide#26)
The reasoning behind this is that it is completely up to the caller’s choice to be blocked or not by a sync API. Consider the following type definition and sync fetchA function:

  1. type fetchResult struct {
  2. Message string
  3. Error error
  4. }
  5. func fetchA() fetchResult {
  6. time.Sleep(time.Second * 4)
  7. return fetchResult{"A data", nil}
  8. }

If the caller wants to be blocked, then he can just call the function

  1. a := fetchA()

If the caller does not want to be blocked, then he could call the function inside a goroutine:

  1. aChan := make(chan fetchResult, 0)
  2. go func(c chan fetchResult) {
  3. c <- fetchA()
  4. }(aChan)

(D) Sequential and Concurrent Patterns

JS

Even without parallelism, we can structure Javascript code in both sequential and concurrent flows.
For the following exmaples, let’s assume fetchA(), fetchB() and fetchC() are all async functions returning a promise.

Sequential

  1. function fetchSequential() {
  2. fetchA().then( (a) => {
  3. console.log(a);
  4. return fetchB();
  5. }.then( (b) => {
  6. console.log(b);
  7. return fetchC();
  8. }.then( (c) => {
  9. console.log(c);
  10. }
  11. }

Concurrent

  1. function fetchConcurrent() {
  2. Promise.all([fetchA(), fetchB(), fetchC()]).then(values => {
  3. console.log(values);
  4. }
  5. }

Go

For the following examples, assume fetchB() and fetchC() are defined as a sync function similarly to fetchA in the previous section (The full example is available here https://play.golang.org/p/2BVwtos4-j)

Sequential

  1. func fetchSequential() {
  2. a := fetchA()
  3. fmt.Println(a)
  4. b := fetchB()
  5. fmt.Println(b)
  6. c := fetchC()
  7. fmt.Println(c)
  8. }

Concurrent

  1. func fetchConcurrent() {
  2. aChan := make(chan fetchResult, 0)
  3. go func(c chan fetchResult) {
  4. c <- fetchA()
  5. }(aChan)
  6. bChan := make(chan fetchResult, 0)
  7. go func(c chan fetchResult) {
  8. c <- fetchB()
  9. }(bChan)
  10. cChan := make(chan fetchResult, 0)
  11. go func(c chan fetchResult) {
  12. c <- fetchC()
  13. }(cChan)
  14. // order doesn't really matter!
  15. a := <-aChan
  16. b := <-bChan
  17. c := <-cChan
  18. fmt.Println(a)
  19. fmt.Println(b)
  20. fmt.Println(c)
  21. }

Modules / Packages

Spec & Practice

JS

As of es6, the Javascript spec includes a module system, however the external specs of AMD and CommonJS are also popular since the language began to address this issue rather late.

Before es6 modules, the spec only supported the script mode, of which every file shares the same top-level global scope. This means that there was no official “file scope” for scripts. In practice, file-module scope was common since it was either introduced by code (window.moduleA = …), an external tool (requireJS), or by a runtime that baked-in a module system (NodeJS).

Therefore, it is safe to say that Javascript programs are commonly structured with a 1-to-1 relationship between files and modules with local scope.

Go

Go’s import statement and package support were part of the spec from the beginning. In Go there is no file scope, only package scope. As of Go 1.6 (or 1.5 + flag), there’s better support for encapsulating dependent packages inside a project with the vendor folder. However, it doesn’t attempt to solve everything:

… this does not attempt to solve the problem of vendoring resulting in multiple copies of a package being linked into a single binary. Sometimes having multiple copies of a library is not a problem; sometimes it is. At least for now, it doesn’t seem that the go command should be in charge of policing or solving that problem.

The differences

A Javascript module can be any valid Javascript type. By exporting an object, it can package multiple functionalities. By exporting a function it can surface a single functionality. On the other hand, a Go package, is as its a name- just a package. So while a Javascript module can be directly invoked if it is a function type, this is not a possibility with a Go package.

Another difference is the consumption of other internal components within your project. In Javascript, since each file is (usually) a module, then each of the files that were decoupled from the current file must be imported. On the other hand, in Go, all files within the same package can have access to each other since there is no file scope.

Management

For Javascript development, NPM is the de-facto package manager for NodeJS, and may also be used for client side projects. Bower is also a popular for client side projects.

The go get tool will only get you as far as getting a dependency latest master code. This will not suffice if you need accurate dependency management with pinned versions. The Go community came up with several package managers, here’s a partial list:

Go has acknowledged the need for a dependency management tool by starting its own project: dep. As of the time of writing, it is still in Alpha phase, and not part of official Go toolchain yet. Watch that project roadmap for status updates!

Error Handling

(B) Flow control and values

Both languages pass errors as regular values. Also, both languages leverage flow control constructs: Javascript uses throw catch finally block, and Go uses panic recover defer

(D) Usage

Despite the similarity claimed above, the languages differ on how and when errors are handled:

JS
In JS, the way to propegate an error is determined by the synchorinic nature of the function.
If a function is synchronous, then it should use throw when an error occurs, and the caller should use try/catch blocks.

Otherwise, an asynchronous function should propagate the error by passing it as a first value to a callback function, or it should return a rejected promise.

Note the async/await mechanism, which is in draft, will consolidate both worlds by having asynchronous errors being handled inside try/catch blocks.

Go
In Go on the other hand, the way to propagate an error is determined by the degree of severity with context of the whole application.

For example, for a web-server application, if errors occur in a request handling code path, they should not crash the entire server. Therefore, these errors should be returned as a last argument to the caller.

On the other hand, if an error occurs during the application init, it can be argued that there’s no reason to continue, and therefore panic would make sense.

(S) Loss of stack trace

While passing errors as values, one drawback is the loss of stack trace. Both languages suffer from this. Some runtimes and libraries try to help. Some libraries:

Keywords & Syntax Comparison

(D) this keyword

JS

Inside an object method, this refers to the object (with some exceptions).

Go

In Go, the closest analogy would be receivers inside method functions.
You may use this as a receiver:

  1. type Boo struct {
  2. foo string
  3. }
  4. func (this *Boo) Foo() string {
  5. return this.foo
  6. }

It is more idiomatic to use short variables as receivers. In the example above b would have been a better fit over this.

(D) new keyword

JS

new Foo() instantiates an object from Foo, a constructor function or a class.

Go

new(T) allocates zeroed storage for a new item of type T and returns a pointer, *T. This is different than Javascript and most other languages where new will initialize the object, while in Golang it only zeros it.

It is worthy to mention that it is idiomatic to name methods with a “New” prefix to denote it returns a pointer to the type following in the method name. e.g:

  1. timer := time.NewTimer(d) // timer is a *time.Timer

(D) bind / method values

JS

  1. var f = boo.foo.bind(boo2); // when calling f(), "this" will refer to boo2

Go

  1. f := boo.foo // f(), is same as boo.foo()

(S) setTimeout / timer

JS

  1. setTimeout(somefunction, 3*1000)

Go

  1. time.AfterFunc(3*time.Second, somefunction)

(D) setInterval / ticker

JS

  1. setInterval(somefunction, 3*1000)

Go

  1. ticker := time.NewTicker(3 * time.Second)
  2. go func() {
  3. for t := range ticker.C {
  4. somefunction()
  5. }
  6. }()

(D) String literals

JS

Strings are initialized with single quotes ('hello') or double quotes ("hello"), yet most coding styles prefer the single quotes variation. Raw string literals use backticks (`hello` ).

Go

Strings are initialized with double quotes ("hello") or raw string literals with backticks (`hello` )

(S) Comments

Both languages use the same /* block comments */ and // line comments.

Variables

(D) Values, Pointers, References

In Javascript there are value types and reference types. Primitives such as string and number are value types. Objects, including arrays and functions, are reference types.

In Go, there are value types, reference types, and pointers. References types are slices, maps, and channels. All the rest are value types, but have the ability “to be referenced“ with pointers.
The most practical difference to remember between references and pointers, is that while you can use both to mutate the underlaying value (when it is mutable), with pointers you can also reassign it.

JS

  1. var a = {
  2. message: 'hello'
  3. }
  4. var b = a;
  5. // mutate
  6. b.message = 'goodbye';
  7. console.log(a.message === b.message); // prints 'true'
  8. // reassign
  9. b = {
  10. message: 'galaxy'
  11. }
  12. console.log(a.message === b.message); // prints 'false'

Go

  1. a := struct {
  2. message string
  3. }{"hello"}
  4. b := &a
  5. // mutate
  6. // note b.message is short for (*b).message
  7. b.message = "goodbye"
  8. fmt.Println(a.message == b.message) // prints "true"
  9. // reassign
  10. *b = struct {
  11. message string
  12. }{"galaxy"}
  13. fmt.Println(a.message == b.message) // prints "true"

Types

TBD

Flow control statements

(B) Loops and iteration

For

JS

  1. for(let i=0;i<10;i++){
  2. console.log(i);
  3. }

Go

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

While

In Go, the for‘s init and post statement are optional, effectively making it also a “while” statement:

JS

  1. var i=0;
  2. while(i<10){
  3. console.log(i);
  4. i++;
  5. }

Go

  1. i := 0
  2. for i < 10 {
  3. fmt.Println(i)
  4. i++
  5. }

Iterating over an Array/Slice

JS

  1. ['Rick','Morty','Beth','Summer','Jerry'].forEach(function(value,index){
  2. console.log(value + ' at index ' + index);
  3. });

Go

  1. for i, v := range []string{"Rick", "Morty", "Beth", "Summer", "Jerry"} {
  2. fmt.Printf("%v at index %d", v, i)
  3. }

(B) If/Else

Go’s if can contain an init statement, with variables declared scoped only to the if and else blocks.

Go

  1. if value := getSomeValue(); value < limit {
  2. return value
  3. } else {
  4. return value / 2
  5. }

(D) Switch

The switch statement was one of the motivation for writing this document.

Go defaults to break, and fallthrough needed for otherwise.

Javascript defaults to fallthrough, and break needed for otherwise.

JS

  1. switch (favorite) {
  2. case "yellow":
  3. console.log("yellow");
  4. break;
  5. case "red":
  6. console.log("red");
  7. case "pruple":
  8. console.log("(and) purple");
  9. default:
  10. console.log("white");
  11. }

Go

  1. switch favorite {
  2. case "yellow":
  3. fmt.Println("yellow")
  4. case "red":
  5. fmt.Println("red")
  6. fallthrough
  7. case "pruple":
  8. fmt.Println("(and) purple")
  9. default:
  10. fmt.Println("white")
  11. }

Functions

(S) first-class functions

Both languages treat functions as first-class citizens. Both allow functions to be passed as arguments, to be a returned value, to be nested, and have closures.

Function nesting in Javascript can be done both with named and anonymous functions, while in Go this can only be done with anonymous functions.

(D) Multiple returns

Go functions can return multiple values

Go

  1. func hello() (string, string) {
  2. return "hello", "world"
  3. }
  4. func main() {
  5. a, b := hello()
  6. fmt.Println(a, b)
  7. }

Javascript cannot, however by using destructuring assignment syntax, we can get a similar behavior

JS

  1. function hello() {
  2. return ["hello", "world"];
  3. }
  4. var [a, b] = hello();
  5. console.log(a,b);

(S) IIFE

JS

  1. (function () {
  2. console.log('hello');
  3. }());

Go

  1. func main() {
  2. func() {
  3. fmt.Println("Hello")
  4. }()
  5. }

(S) Closures

Both languages have closures. Both require caution when creating closures inside loops. Here are examples in both languages that demonstrate a similar technique to bypass the closure/loop trap:

JS (with bug)

  1. var i = 0;
  2. for (; i < 10 ; i++) {
  3. setTimeout((function() {console.log(i);}),0);
  4. }

JS (solved) (note that using for(let i=0; … instead of var is a more practical solution)

  1. var i = 0;
  2. for (; i < 10 ; i++) {
  3. (function (i) {
  4. setTimeout((function() {console.log(i);}),0);
  5. }(i));
  6. }

Go (with bug)

  1. var wg sync.WaitGroup
  2. wg.Add(10)
  3. for i := 0; i < 10; i++ {
  4. go func() {
  5. defer wg.Done()
  6. fmt.Println(i)
  7. }()
  8. }
  9. wg.Wait()

Go (solved)

  1. var wg sync.WaitGroup
  2. wg.Add(10)
  3. for i := 0; i < 10; i++ {
  4. go func(i int) {
  5. defer wg.Done()
  6. fmt.Println(i)
  7. }(i)
  8. }
  9. wg.Wait()

License

Copyright Maor Zamski & Daniel Singer

“Go for Javascript Developers” is released under the Creative Commons Attribution-ShareAlike 4.0 International License..

The “gopher” used at the cover was created by Takuya Ueda. It is licensed under the Creative Commons 3.0 Attributions license.

ft_authoradmin  ft_create_time2017-10-31 10:12
 ft_update_time2017-10-31 10:13