Go言語(golang)でJavaScriptが実行できるottoの使い方
otto は、JavaScript をGo言語に変換して実行できるパッケージです。
Goのプログラムをコンパイルして JavaScript のファイルを読み込めば動的に実行できるので、
設定ファイル以上のことをする場合などに便利です。
ただし ES5 なので let
, const
, () =>
, などは使えませんし、正規表現もGo言語にない先読みなどは使えません。
同様に、Go言語やPythonの構文を動的に実行できるパッケージがあるので検索してみてください。
目次
- ダウンロード
- ファイルを読み込む - Compile
- スクリプトの実行 - Run
- Go側で変数を定義してスクリプト内で使う - Set
- Go側で定義した構造体をスクリプト内で使う - Set
- レシーバ付き関数を渡す - Set
- 引数付き関数 - FunctionCall
- スクリプト内で定義した変数をGo側で使う - Get
- スクリプト内で定義した関数をGo側で実行 - Call
ダウンロード
$ go get github.com/robertkrimen/otto
ファイルを読み込む - Compile
package main import ( "fmt" "github.com/robertkrimen/otto" ) func main() { vm := otto.New() { // ファイルから読み込む script, err := vm.Compile("index.js", nil) if err != nil { fmt.Println(err) return } _, err = vm.Run(script) if err != nil { fmt.Println(err) return } } { // ファイルではなく第二引数の src が優先される script, err := vm.Compile("index.js", `console.log("hello")`) if err != nil { fmt.Println(err) return } _, err = vm.Run(script) if err != nil { fmt.Println(err) return } } }
スクリプトの実行 - Run
package main import ( "fmt" "github.com/robertkrimen/otto" ) func main() { vm := otto.New() script := ` var n = 100; console.log("hello-" + n); // hello-100 n; // 最後に返すと Run の戻り値 value になる ` value, err := vm.Run(script) if err != nil { fmt.Println(err) return } if !value.IsUndefined() { fmt.Println("value:", value.String()) // 100 } }
Go側で変数を定義してスクリプト内で使う - Set
package main import ( "fmt" "github.com/robertkrimen/otto" ) func main() { vm := otto.New() vm.Set("variable", "hello") script := ` console.log("variable: " + variable); // hello ` _, err := vm.Run(script) if err != nil { fmt.Println(err) return } }
Go側で定義した構造体をスクリプト内で使う - Set
mapも同じように . で使うことができる
package main import ( "fmt" "github.com/robertkrimen/otto" ) type User struct { Name string Age int } func main() { vm := otto.New() user := User{"Tanaka", 30} vm.Set("user", user) script := ` console.log("name: " + user.Name + ", age: " + user.Age); ` _, err := vm.Run(script) if err != nil { fmt.Println(err) return } }
レシーバ付き関数を渡す - Set
func (u *User) Profile() { fmt.Println("call func Profile: name:", u.Name, "age:", u.Age) } func main() { vm := otto.New() user := User{"Tanaka", 30} vm.Set("profile", user.Profile) script := ` profile(); ` _, err := vm.Run(script) if err != nil { fmt.Println(err) return } }
引数付き関数 - FunctionCall
package main import ( "fmt" "github.com/robertkrimen/otto" ) func main() { vm := otto.New() sum := func(call otto.FunctionCall) otto.Value { // スクリプトの実行箇所。 fmt.Println("Caller location:", call.CallerLocation()) var n int64 for _, v := range call.ArgumentList { i, _ := v.ToInteger() n += i } value, _ := otto.ToValue(n) return value } vm.Set("sum", sum) // スクリプト側で実行 script := ` console.log(sum(10, 20, 30, 40)); // 100 ` _, err := vm.Run(script) if err != nil { fmt.Println(err) return } }
スクリプト内で定義した変数をGo側で使う - Get
package main import ( "fmt" "github.com/robertkrimen/otto" ) func main() { vm := otto.New() script := ` var variable = 100; ` _, err := vm.Run(script) if err != nil { fmt.Println(err) return } value, err := vm.Get("variable") if err != nil { fmt.Println(err) return } if value.IsNumber() { n, _ := value.ToInteger() fmt.Println("value:", n) } }
スクリプト内で定義した関数をGo側で実行 - Call
package main import ( "fmt" "github.com/robertkrimen/otto" ) func main() { vm := otto.New() script := ` function sum(x, y) { return x + y; } ` _, err := vm.Run(script) if err != nil { fmt.Println(err) return } sum, err := vm.Get("sum") if err != nil { fmt.Println(err) return } value, err := sum.Call(sum, 100, 200) if err != nil { fmt.Println(err) return } if n, err := value.ToInteger(); err == nil { fmt.Println("value:", n) } }