Help us understand the problem. What is going on with this article?

gojaを使ってGoでJavaScriptの実行を試してみた

More than 3 years have passed since last update.

第2のドワンゴ Advent Calendar 2016の3日目の記事です。

来年使い倒そうと思ってるgojaについて紹介しようと思います。

goja とは

https://github.com/dop251/goja

gojaはEcmaScript5.1をGoで実装しているライブラリです。
GoでJavaScriptを実行するメリットとしてプラグインにJavaScriptを使えたりサーバーサイドレンダリングに使えるのではないかなと思います。

インストール

Goを入れていればgo get github.com/dop251/gojaでインストールできます。

Hello World

gojaのHello Worldです。
スクリプトで最後に実行した行の戻り値がRunStringの戻り値として取得できます。

package main

import (
    "fmt"

    "github.com/dop251/goja"
)

func main() {
    vm := goja.New()
    v, err := vm.RunString(`2+2`)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%T, %#v\n", v, v) // Output: goja.valueInt, 4
}

Goとの値のやり取り

Setを使うことでスクリプト実行前に変数を定義することができます。

package main

import (
    "fmt"

    "github.com/dop251/goja"
)

func main() {
    vm := goja.New()
    x, y := 5, 7
    vm.Set("x", x)
    vm.Set("y", y)
    v, err := vm.RunString(`x+y`)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%T, %#v\n", v, v) // Output: goja.valueInt, 7
}

関数も同じ様にセットすることができます。

package main

import (
    "fmt"

    "github.com/dop251/goja"
)

func Sum(x, y int) int {
    return x + y
}

func main() {
    vm := goja.New()
    vm.Set("sum", Sum)
    v, err := vm.RunString(`sum(9, 4)`)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%T, %#v\n", v, v) // Output: goja.valueInt, 13
}

逆にJavaScriptで定義した変数をGoに渡すこともできます。
その場合はGetを使います。

package main

import (
    "fmt"

    "github.com/dop251/goja"
)

func main() {
    vm := goja.New()
    _, err := vm.RunString(`
        var result = 8 + 7
    `)
    if err != nil {
        panic(err)
    }

    v := vm.Get("result")

    fmt.Printf("%T, %#v\n", v, v) // Output: goja.valueInt, 15
}

変数だけではなく関数もJavaScriptで定義してGoで呼び出すこともできます。

package main

import (
    "fmt"

    "github.com/dop251/goja"
)

func main() {
    vm := goja.New()
    _, err := vm.RunString(`
        function sum(x, y) {
            return x + y
        }
    `)
    if err != nil {
        panic(err)
    }

    var v goja.Value
    if sum, ok := goja.AssertFunction(vm.Get("sum")); ok {
        v, err = sum(goja.Undefined(), vm.ToValue(4), vm.ToValue(10))
        if err != nil {
            panic(err)
        }
    }

    fmt.Printf("%T, %#v\n", v, v) // Output: goja.valueInt, 14
}

モジュールの読み込み

サイドプロジェクトとしてNode.jsのrequireとconsoleを実装しているパッケージがあります。

https://github.com/dop251/goja_nodejs

このパッケージのrequireを使うことでモジュール読み込みができます。
インストールはgo get github.com/dop251/goja_nodejs/...でできます。

sum.js
module.exports = function sum(x, y) {
    return x + y
}

モジュールをJavaScriptから呼び出すなら以下のように。

package main

import (
    "fmt"

    "github.com/dop251/goja"
    "github.com/dop251/goja_nodejs/require"
)

func main() {
    registry := new(require.Registry)

    vm := goja.New()
    registry.Enable(vm)
    v, err := vm.RunString(`
        var sum = require('./sum.js')
        sum(10, 5)
    `)

    if err != nil {
        panic(err)
    }

    fmt.Printf("%T, %#v\n", v, v) // Output: goja.valueInt, 15
}

モジュールをGoで呼び出すなら以下のようになります。

package main

import (
    "fmt"

    "github.com/dop251/goja"
    "github.com/dop251/goja_nodejs/require"
)

func main() {
    registry := new(require.Registry)

    vm := goja.New()
    rm := registry.Enable(vm)

    var v goja.Value
    if sum, err := rm.Require("sum.js"); err != nil {
        panic(err)
    } else {
        if sum, ok := goja.AssertFunction(sum); ok {
            v, err = sum(goja.Undefined(), vm.ToValue(6), vm.ToValue(7))
            if err != nil {
                panic(err)
            }
        }
    }

    fmt.Printf("%T, %#v\n", v, v) // Output: goja.valueInt, 13
}

まとめ

gojaを使えば手軽にJavaScriptを組み込むことができました。
ただsetTimeoutなどvue.jsでサーバーサイドレンダリングをしようと思うと
実装しなければならないものが多くてちょっと間に合わなかったので
来年こそはGoでSSRしたいと思います。

あと僕がGoを触るキッカケにもなったdwanGoが復活してくれたらいいなぁ(チラチラ

koki_cheese
Go/JavaScript/Firebase/GCP/Securityとかが好きです。
http://github.com/k2wanko
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away