本記事は Go3 Advent Calendar 2020 15日目の記事になります。
こんにちは、株式会社カミナシのエンジニア @tomiです。
JavaScript, Node.jsをメインに扱ってきたエンジニアがGoに触れるときにどう解釈したかを、JavaScriptとGolangを比較しながら、理解を深める記事となっています。
はじめに
まず私自身のJavaScriptへの理解としては、VueやReact,Redux.Next.jsなどのフロントエンド周りに加え、ExpressやAdonisJSなどのバックエンドもかじっており、Node.jsでフルスタックに開発できるレベルです。
また、TypeScriptもここ1年ほどは個人開発でも積極的に取り入れております。
そんなJavaScriptにしがみついてきた私が、カミナシへのジョインをきっかけにGo言語を本格的に触れることになり、コードをどう噛み砕いったかを改めてまとめました。
ひとつひとつの機能を紹介するというよりは、Goではこう書くのかという視点となります。
さっそく、コードを見ながら比較してきます!
(Goが静的型付け言語なので、JavaScriptではなくTypeScriptとの比較がほとんどとなります。)
import/export
まずはじめに、コードを開くとpackage
とimport
がほとんどのファイルに書かれています。
// hello/world.go package hello import ( "fmt" ) func World() { fmt.Println("Hello, World!") }
上のソースを見たらだいたい察しは付くと思いますが、
importは外部パッケージを読み込んだり、作成したpackage
を読み込むことができます。
JavaScriptでいうとこの部分。
import fmt from "fmt"
exportに関しては、packageに書いたパッケージ名から関数を読み出せるようにしており、先程のHello, World!を出力するには以下のように書きます。
package main import ( "./hello" ) func main() { hello.World() } // Hello, World!
これをJavaScriptで書くと、以下のようになります。
// hello/World.js export default function World () { console.log('Hello, World!') } // hello/index.js import World from './World.js' export default { World, } // index.js import hello from './hello' hello.World() // => Hello, World!
Goでは同じpackage名のファイルを複数用意できるので、関数を複数のファイルに分割することがとても簡単になっています。
// hello/local.go package hello import ( "fmt" ) func Local() { fmt.Println("Hello, Local!") } // main.go package main import ( "./hello" ) func main() { hello.World() hello.Local() } // Hello, World! // Hello, Local!
Var(変数)
変数でよく使う書き方が2パターンあります。
// ひとつめ var hello string hello = "Hello, World" // ふたつめ hello := "Hello, World"
:=
とすることで、宣言と代入をまとめることができます。
Typescriptで書くと、以下のようになります。
// ひとつめ let hello: string hello = "Hello, World" // ふたつめ let hello = "Hello, World"
Function(関数)
上でもさらっと書いていますが、func
が関数となっています。
Goでは、Typescriptのように引数や返り値には型が必須になります。
func GetHello(name string) string { message := "Hello, " + name return message }
(name string)
で引数nameはString型であることを示し、関数getHello
の返り値の型を引数( )
の後ろ書きます。
これをTypescriptで書くと、以下のようになります。
function GetHello(name: string): string { const message = `Hello, ${name}` return message }
for ... range(繰り返し)
fruits := []string{"apple", "banana", "orange"} for index, fruit := range fruits { fmt.Println(index, fruit) } // 0 apple // 1 banana // 2 orange
for i := 0; i < 10; i++ { ... }
という書き方もありますが、for ... range
を使ったやり方の方が良く見かけます。
indexが不要な場合は、ブランク修飾子_
を使って、for _, fruit := range fruits
と書きます。
これをTypescriptで書くと、以下のようになります。
const fruits = ["apple", "banana", "orange"] fruits.forEach(fruit, index) { console.log(index, fruit) }
Struct(構造体)
type User struct { name string age int } tom := User{"Thomas", 20} fmt.Println(tom.name, tom.age) // Thomas 20
これをTypescriptで書くと、以下のようになります。
type User = { name: string age: number } const tom = new User("Thomas", 20) console.log(tom.name, tom.age)
classを使った書き方もできます。
class User { name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } } const tom = new User("Thomas", 20) console.log(tom.name, tom.age)
レシーバ(構造体と関数の紐付け)
先程作成したtomが、tom.intro()
という関数で自己紹介を出力したいとする。
type User = { name: string age: number } func (u *User) intro () { fmt.Println("I am " + u.name) } const tom = new User("Thomas", 20) tom.intro() // I am Thomas
関数名の前に構造体を記述することで、構造体用の関数であることを示し、関数内で構造体を扱うことができる。
これをTypescriptで書くと、以下のようになります。
class User { name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } intro() { console.log(`I am ${this.name}`) } } const tom = new User("Thomas", 20) tom.intro()
はじめてこの関数を見たときは、関数名の前後に( )
がある・・・なんだ?🤔となりました。
実際には↓のように引数も返り値もあるので、さらに複雑に見えたのが初見の印象。
func (r *Report) GetReports(limit, offset int64, query *entity.GetReportsQuery) (*entity.Reports, int64, error) { ... }
まとめ
JavaScript脳がGolangを理解するために、JavaScriptとGolangを比較してみました。
この書き方で戸惑っていたのかと思うと自分のレベルの低さに落ち込みますが、Goを触ってみたいという人も同じところで悩むはず!
そんな人のお役に立てる記事となれば幸いです。