- Updated on

Javascript to Go: A primer

JS to GO

Here is a primer for javascript developers looking to learn GO.

Javascript is a dynamic scripting language built for the web. It’s the most popular programming language according to github, it’s the language used to script the web and included in every web browser but also popular in the backend.

Go is a statically-typed, compiled programming language designed for building efficient, scalable and concurrent network servers and systems. Go was born out of the frustrations of the C++ language, with a focus on simplicity.

Why go?

The main advantages using go for me are, it’s simplicity and type safety. Go is all about simplicity, it has limited syntax so it’s easy to read and write, it’s easy to install dependencies, build and run it. Go has been around a good few years now and has a large community surrounding it. Typescript has become an essential tool when developing with javascript but its still a superset of javascript and you can still write untyped code. Sick of arguing over typescript config or eslint rules or prettier formatting, go has a built in formatter.

JIT vs Compiled

Javascript is a just in time compiled language, where the code is interpreted and compiled during the execution/running of the code. In comparison GO is a compiled language, meaning the code needs to be compiled to native machine code before it can be executed. Compiled languages are usually faster than JIT languages due to the on-the-fly interpretation step JIT languages make.

Typing

Javascript is a dynamically typed language. Variable types are determined at runtime and a variables type can be changed throughout its lifetime.

let x = 1;
x = “hello world”;
x = true;

Go is statically typed. Variables types are declared and checked at compile time and a variables type cannot be changed throughout its lifetime

var x int = 1
x = “hello world” // compile time error

In go you can declare a variable and the type is inferred from the initialising value

var int = 1
// or
int := 1

Go has similar primitive types to javascript, int, float, string, bool, but also has more complex types like structs.

The dynamic nature of javascript is a common source of confusion and bugs, and more difficult to follow an read complex code. GO’s static typing makes reading, maintaining and refactoring code easier, ahead-of-time compilation also catches a host of potential runtime errors that would only be caught at runtime in javascript.

Classes

You can create a class in javascript but are seen as templates for creating objects and are “special functions” that use prototyping behind the scenes, and not as commonly used as more object oriented languages like C#. Methods and properties can be shared with instances of a function by adding to it’s prototype.

function person(name) {
	this.name = name
}

Person.prototype.greet = function () {
	console.log(“Hello ”+this.name);
}

GO lang does not have classes, structs are used to define types and methods can be added to types. GO does not have inheritance but you can embed other struct in a similar fashion to inheritance, go favours composition.

type Age struct {
 	age int
}

type Person struct {
	Age // struct embedding
	Name string
}

To create an instance of a struct you have two options, you can compose a literal and set the values:

age := Age{age: 30}

or can use the new keyword to create a new instance of a struct and it will return a pointer to the struct.

age := new(Age)
age.age = 30

You can give a type methods:

func (p Person) SayHello() {
	fmt.Sprintf(“Hello %s”, p.Name)
}

Notice the receiver is the first argument to the method, and the method is called on the instance of the struct, more of a composition over inheritance approach.

Functions

In GO and Javascript functions are first class citizens, meaning they can be treated as values, assigned to variables, passed around as arguments and returned from other functions.

func calculate(operation func(int, int) int, x, y int) int {
    return operation(x, y) 
}

Here is an example of a higher order function.

Go does not have the concept of overrides like in other languages but like javascript you can pass in a variable amount of arguments, in go this is a variadic function.

func sum(values ...int) int {
    total := 0
    for _, value := range values {
        total += value
    }
    return total
}

Interfaces

Javascript does not have interfaces, but with typescript can be added.

interface Person {
name: string;
age: number;
}

Go also has interfaces but uniquely they are implicit, if a type has all the methods that match an interface it has implicity implemented it. Typescripts implementation is similiar as an object does not need to have it explicitly applied.

type Person interface {
	Greet()
}

If a type has the Greet method then it implements the Person interface.

Exceptions

Go does not have traditional exceptions like Javascript, where errors are thrown and caught.

Exceptions are returned from functions and must be handled by the caller.

func getNumber() (int, error) {
    res, err := someFunction()
    if err != nil {
        return nil, err
    }
    return res, nil
}

Functions can return multiple values, in this case an int and ad usually an error as the second value.

This is one of the criticisms of go as it can lead to lots of error checking code, but it keeps things simple and explicit, inline with Go’s philosophy, compared to javascript where you must remember to catch errors and think about how to handle them.

Pointers

A pointer in Go is a reference to a memory address, and is used to store the memory address of another variable. If you want to modify a variable in a function you need to pass a pointer to the variable.

var val = 1;
const changeValue = (v) => {
  v = 99;
};
changeValue(val)
console.log(val);

In the javascript code above the value remains 1, because in javascript when you pass a value primitive to a function you cannot change the original variable as only the value is passed. When you pass an object your passing a reference to the object so you can edit its properties.

var obj = { name: "john" };
const changeObject = (o) => {
 o.name = "jane";
};
changeValue(obj)
console.log(obj.name);

In go this is not the default, you can pass an object to a function and it creates a copy of the values, but if you want to modify the object you need to pass the pointer or reference to the object.

func changeStruct(p *Person) {
	p.Name = "Jane"
	fmt.Println(p.Name)
}

The star before the type tell us we need to pass a pointer to the person struct.

To create a new instance of struct pointer you can prefix it with an ampersand.

person := &Person {
	name: “John”
}
changeStruct(person)

Collections

Go has arrays, slices and maps, which are similiar to javascript arrays and Map. Arrays are fixed length and slices are dynamic.

var a [5]int
s := make([]int, 0, 5)

Slices are more commonly used in go as they are more flexible and can be resized.

slc := []int{1, 2, 3} // note no length
slc = append(slc, 4) // add to the slice
x := slc[1:3] // slice the slice, equivalent to javasctipt slice

Sorting functions are not built into the array or slice type, you need to use the sort package.

import “sort”

slc := []int{3, 2, 1}
sort.Ints(slc) // sort a slice of integers

There are no standard functions for filtering or mapping over slices, you need to use a for loop.

Maps are a key value store, similar to a Set in javascript as keys are unique.

myMap := make(map[string]string)
myMap[“key”] = “value”

for key, value := range myMap {
    fmt.Println(key, value)
}

Loops

Go has a single loop construct, the for loop, but it can be used in a variety of ways.

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

for n := range 10 {
    println(n)
}

for n < 5 {
    n++
    println(n)
}

// iterate over a slice
for index, value := range []int{1, 2, 3} {
    println(index, value)
}

Goroutines

Javascript is single threaded, with support for asynchronous operations using async and await, and the event loop. Go can utilise multiple threads and CPU cores and has a built in concurrency model called goroutines, they are lightweight threads of execution that run concurrently with other goroutines. Goroutines and go’s concurrency model are one of it’s main advantages over other languages particularly for network servers and systems.

func main() {
    go sayHello()
}  

To use goroutines you just prefix the function call with the go keyword.

Go uses channels as a way to communicate between goroutines and avoid shared memory issues.

func main() {
    ch := make(chan string)
    go sayHello(ch)
    fmt.Println(<-ch) // read from a channel
}

func sayHello(ch chan string) {
    ch <- "Hello" // write to a channel
}

Syntax

Go has a simple and limited syntax in the pursuit of simplicity and readability.

Unlike javascript go does not use semicolons.

Variable declaration in go, var or const.

var x int = 1
x := 1 // short variable declaration and type infering
const x = 1

Function syntax is go is similar to Typescript, with the type after the variable name.

func square(x int) int {
    return x * x
}

Go has a switch statement, similar to javascript but with a few differences. Go does not require a break statement as it will break automatically, you can also use multiple values in a case.

switch x {
    case 1, 3:
        fmt.Println(“one”)
    case 2:
        fmt.Println(“two”)
    default:
        fmt.Println(“many”)
}

If statements are similar, without evaluation parenthesis.

if x > 1 {
    fmt.Println(“greater than one”)
} else {
    fmt.Println(“less than or equal to one”)
}

You can declare a variable in an if statement and it will be scoped to the if block.

if x := 1; x > 0 {
    fmt.Println(“greater than zero”)
}

Go uses a convention to keep variables and functions public or private, if a variable or function starts with an uppercase letter it is public and can be accessed from other code when the package is imported, if it starts with a lowercase letter it is private to the package and can only be used internally.

var x int // private
var X int // public

func square() {} // private
func Square() {} // public

Formatting

This might be one of my favourite aspects of go, but go provides a formatting tool go fmt that will format your code to a standard format, eliminating the need for code style arguments and endless configuration files. There’s good editor support to run go fmt when you save a file.

Standard library

Javascript has a large standard library depending on the runtime environment, node has many apis that are not available in the browser and vice versa, new runtimes like Deno and bun have their own standard libraries.

Go has an extensive standard library that provides everything from working with files, creating a web server, working with http requests, creating HTML templates, encoding decoding JSON, image manipulation, math, a standard interface for SQL databases, and many more.

Go modules

Since v 1.11 Go has had support for modules, which are a way to manage dependencies in your project. Much like npm or yarn for javascript, you can add dependencies to your project and they will be downloaded and cached in your GOPATH. A go.mod file is created in the root of your project and lists the dependencies and their versions like a package.json file.

Go also has an npm equivalent called pkg.go.dev where you can find and install packages from 3rd parties, unlike npm external packages are usually downloaded from a source repository using go get.

go get github.com/gorilla/mux

Go tools

Got has a set of tools that come with the language that cover building, running, testing, profiling, and debugging your code.

go build  # builds the executable
go run # runs the code
go test # runs the tests
go fmt # formats the code
go vet # checks for errors
go mod # manages dependencies

To run your go app locally you can use go run, which will compile and run your code in one command.

go run main.go

Idiomatic go

Go has a set of idioms and best practices that are generally followed by the community

// constant declarations, dont uppercase
const apiUrl = ""

// functions that panic have panic suffix
func thisWillPanic() {
	panic("Panic!")
}

// use variable grouping
var (
    a = 1
    b = 2
)

// struct initialisation, use field name on their own line
person := person{
    name: "Tim",
    age:  30,
}

// use short variable declaration
x := 1

// use short variable names for short lived variables
func square(x int) int {
    return x * x
}

A small app

A simple go app. Package main is used as the entry point for an executable app, and the main function is the entry point for the app.

package main

func main() {
    println(“Hello world”)
}

We can create a new module:

go mod init myapp

To run the app you can use

go run main.go
//or assuming you have a main package 
go run .

or to create an executable

go build

you can also install the app locally

go install

and run it assuming you have added your go bin to your path

./myapp
© 2024 Timney.