写在前面
本文是《go执行js代码引擎系列》文章之第一篇,其他相关文章如下
goja
它实现了ES 5.1
的所有语法和大部分的ES 6
语法,比 Python 的execJS
要厉害得多。在一定程度上和特定场景下,它可以完全替代Chrome 的 V8引擎
基本用法
package main
import (
"fmt"
"github.com/dop251/goja"
"go-js/common"
"os"
)
func main() {
filePath := os.Args[1]
jsData, err := common.ReadFile(filePath)
if err != nil {
panic(err)
}
vm := goja.New()
v, err := vm.RunString(jsData)
if err != nil {
panic(err)
}
// Export:将结果转换成go基础类型
result, ok := v.Export().(int64)
fmt.Printf("result:%d,ok:%t\n", result, ok)
}
执行:go run main.go ../data/add.js
var a = 2
var b = 3
result = a+b
输出:result:5,ok:true
执行:go run main.go ../data/func.js
var a = 2
var b = 3
result = min(a,b)
function min(a,b){
if(a<b){
return a
}
return b
}
输出:result:2,ok:true
go加载js function并执行
package main
import (
"fmt"
"github.com/dop251/goja"
"go-js/common"
"os"
)
func main() {
filePath := os.Args[1]
jsData, err := common.ReadFile(filePath)
if err != nil {
panic(err)
}
vm := goja.New()
_, err = vm.RunString(jsData)
if err != nil {
panic(err)
}
// 加载函数
max, ok := goja.AssertFunction(vm.Get("max"))
if !ok {
panic("Not a function")
}
result, err := max(goja.Undefined(), vm.ToValue(4), vm.ToValue(5))
if err != nil {
panic(err)
}
fmt.Printf("result:%v\n", result)
}
执行:go run main.go ../data/max.js
function max(a,b){
if(a>=b){
return a
}
return b
}
输出:result:5
ExportTo
将js对象导出成go对象
package main
import (
"fmt"
"github.com/dop251/goja"
"go-js/common"
"os"
)
func main() {
filePath := os.Args[1]
jsData, err := common.ReadFile(filePath)
if err != nil {
panic(err)
}
vm := goja.New()
_, err = vm.RunString(jsData)
if err != nil {
panic(err)
}
// 加载函数
var max func(int, int) int
if err := vm.ExportTo(vm.Get("max"), &max); err != nil {
panic("Not a function")
}
fmt.Printf("result:%v\n", max(6, 7))
}
执行:go run main.go ../data/max.js
输出:result:7
js访问go对象
package main
import (
"fmt"
"github.com/dop251/goja"
"go-js/common"
"os"
)
type Person struct {
Name string
Age int64
}
func main() {
p := &Person{
Name: "张三",
Age: 18,
}
vm := goja.New()
// 设置go对象到js运行时环境中
vm.Set("person", p)
filePath := os.Args[1]
jsData, err := common.ReadFile(filePath)
if err != nil {
panic(err)
}
// 执行js代码
v, err := vm.RunString(jsData)
if err != nil {
panic(err)
}
result, ok := v.Export().(string)
fmt.Printf("result:%s, ok:%t\n", result, ok)
}
执行:go run main.go ../data/print_person.js
result = '姓名:'+person.Name+',年龄:'+person.Age
输出:result:姓名:张三,年龄:18, ok:true
js访问go函数
package main
import (
"fmt"
"github.com/dop251/goja"
"go-js/common"
"os"
)
type Person struct {
Name string
Age int64
}
func main() {
p := Person{
Name: "张三",
Age: 18,
}
vm := goja.New()
// 设置go对象到js运行时环境中
vm.Set("p", &p)
vm.Set("visit", visit)
filePath := os.Args[1]
jsData, err := common.ReadFile(filePath)
if err != nil {
panic(err)
}
// 执行js代码
v, err := vm.RunString(jsData)
if err != nil {
panic(err)
}
result, ok := v.Export().(string)
fmt.Printf("result:%s, ok:%t\n", result, ok)
}
func visit(p *Person, a int) string {
return fmt.Sprintf("go visit. Name:%s, Age:%d, a:%d", p.Name, p.Age, a)
}
执行: go run main.go ../data/call_go_func.js
// p是go对象,123是js变量
var a = 123
visit(p, a)
输出:result:go visit. Name:张三, Age:18, a:123, ok:true
异常
package main
import (
"fmt"
"github.com/dop251/goja"
"go-js/common"
"os"
)
func main() {
vm := goja.New()
// 设置go对象到js运行时环境中
filePath := os.Args[1]
jsData, err := common.ReadFile(filePath)
if err != nil {
panic(err)
}
// 执行js代码
_, err = vm.RunString(jsData)
if err != nil {
if jserr, ok := err.(*goja.Exception); ok {
fmt.Printf("%v\n", jserr)
} else {
panic(fmt.Sprintf("wrong type err:%+v\n", jserr))
}
}
}
执行: go run main.go ../data/js_throw.js
throw('test js throw')
输出:test js throw at <eval>:2:1(1)
package main
import (
"fmt"
"github.com/dop251/goja"
"go-js/common"
"os"
"time"
)
func main() {
vm := goja.New()
// 设置超时时间200ms
time.AfterFunc(200*time.Millisecond, func() {
vm.Interrupt("halt")
})
filePath := os.Args[1]
jsData, err := common.ReadFile(filePath)
if err != nil {
panic(err)
}
// 执行js代码
_, err = vm.RunString(jsData)
if err != nil {
if jserr, ok := err.(*goja.Exception); ok {
fmt.Printf("%v\n", jserr)
} else {
panic(fmt.Sprintf("wrong type err:%+v\n", err))
}
}
}
执行:go run main.go ../data/Interrupting.js
输出:
panic: wrong type err:halt at <eval>:3:6(12)
goroutine 1 [running]:
main.main()
go/src/go-js/goja/main.go:29 +0x17b
exit status 2
demo代码
.
├── common
│ └── util.go
├── data
│ ├── Interrupting.js
│ ├── add.js
│ ├── call_go_func.js
│ ├── func.js
│ ├── js_throw.js
│ ├── max.js
│ └── print_person.js
├── go.mod
├── go.sum
├── goja
│ └── main.go
├── main.go
├── otto
│ └── main.go
源码:https://github.com/ZBIGBEAR/go-js
总结
- js调用go对象、函数,都是将go对象和函数看成对象,因为在js中是不区分对象和函数的。所以在go中封装对象和函数到js运行时环境,js就可以调用。
- go调用js函数,这种情况一般用的少
- js throw处理
- js function超时处理
参考
[1]一日一技:在 Golang 中运行 JavaScript
[2]go执行js代码引擎系列之二:otto库
[3]go执行js代码引擎系列之三:v8go库
[4]go执行js代码引擎系列总结篇:比较goja、otto、v8go
[5]源码:https://github.com/dop251/goja
[6]https://github.com/ZBIGBEAR/go-js