概要
このシリーズの最後の投稿です。
JavaScriptエンジンであるgoja
は仕様がECMAScript 5.1なのでProxy
などはありません。そこで未定義のプロパティの参照があったらオブジェクトの特定のメソッドを呼び出すように改造します。
用途は「Go言語でJavaScriptを使ってオリジナルなコマンドシェルを作る」のAppendix のsqlite3の例の最後の方に書きました。
gojaの改造
改造しないときのデータベースアクセスの例
データベースアクセスの疑似オブジェクトの例
次のようなクラスをdb.js
として作成します。
db.js
DB=function(name) {
this.name = name
//Go言語のsql.Openを呼び出し、*sql.DBをラップしたオブジェクトを返す
}
DB.prototype.exec=function(sql) {
//sql.DB.Execを実行
print(sql+"; を実行しました")
}
DB.prototype.query=function(sql) {
print(sql+"; を実行しました")
//sql.DB.Queryを実行し
//*sql.Rowsをラップしたオブジェクトを返す
return [1,2,3,4]
}
DB.prototype.table = function(name) {
return new TABLE(this, name)
}
TABLE = function(db, name) {
this.db = db
this.name = name
}
TABLE.prototype.findAll = function() {
return this.db.query("select * from "+this.name)
}
実行
> load("db.js")
undefined
> db=new DB("database")
{"name":"database"}
> db.exec("create table sample(a integer, b varchar)")
create table sample(a integer, b varchar); を実行しました
undefined
> db.query("select * from sample")
select * from sample; を実行しました
[1,2,3,4]
> s=db.table("sample")
{"db":{"name":"database"},"name":"sample"}
> s.findAll()
select * from sample; を実行しました
[1,2,3,4]
>
s=db.table("sample")
をMongoDBのmongoシェルのようにs=db.sample
またはdb.sample.findAll()
のように使いたいがProxy
などがないので実現できない。当然、次のようにエラーになります。
> db.sample.findAll()
TypeError: Cannot read property 'findAll' of undefined at console:1:1(3)
>
そこで、gojaを改造します。
gojaのobject.goを改造
object.go
にfunc getPropStr()
があります。現在のソースだと120行目
です。次のようなコードです。
object.go
func (o *baseObject) getPropStr(name string) Value {
if val := o.getOwnProp(name); val != nil {
return val
}
if o.prototype != nil {
return o.prototype.self.getPropStr(name)
}
return nil
}
これを次のように改造します。未定義のプロパティを参照したとき__missing__
が定義されていたら、未定義のプロパティ名を引数にして呼び出します。
object.go
func (o *baseObject) getPropStr(name string) Value {
if val := o.getOwnProp(name); val != nil {
return val
}
if o.prototype == nil {
return nil
}
if val := o.prototype.self.getPropStr(name); val != nil {
return val
}
getProp := o.getOwnProp("__missing__")
if getProp == nil {
return nil
}
if getFunc, ok := AssertFunction(getProp); ok {
nameVal := o.val.runtime.ToValue(name)
if ret, err := getFunc(o.val, nameVal); err == nil {
return ret
} else {
o.val.runtime.Interrupt(err)
return nil
}
}
return nil
}
db.js
のDB
に__missing__
を追加します
db.js
DB=function(name) {
this.name = name
//Go言語のsql.Openを呼び出し、*sql.DBをラップしたオブジェクトを返す
this.__missing__ = function(prop) {
return this.table(prop)
}
}
実行
go run main.go
> load("db.js")
undefined
> db=new DB("database")
{"name":"database"}
> db.sample.findAll()
select * from sample; を実行しました
[1,2,3,4]
> s=db.sample
{"db":{"name":"database"},"name":"sample"}
> s.findAll()
select * from sample; を実行しました
[1,2,3,4]
>
考えた通りに動作しました。
まとめ
ちょっとした改造でオリジナルなJavaScriptエンジンも作れます。頑張ればProxy
クラスも作れそうです。