JavaScript のJSONはprimitiveな変数だけしか扱えなかった。
JSONというのは、本当にお前さんらはね。サムライになれないんだよ。
JSON
var obj = {"a": "takuya", "say":function(){ console.log(`My name is ${this.a}.`) } } obj.say() //=>My name is takuya.
このオブジェクトをJSONでString化すると
stringify すると関数消える。
JSONがサムライになれない理由がコレ。
JSON.stringify(obj) "{"a":"takuya"}" // function 消される。
これは仕方ないんだよ。 function が定義されるタイミングで、変数のスコープもあるし。でも初回ロードとか別にスコープを気にしない時になんとかならないか調べた。
JSON で関数も保存したい!
なんとかならないか調べた。
JSON.stringify/parseの第二引数を使う。
JSON.stringify には第二引数を指定できて、オブジェクトの値をどのようにして、文字列化するか指定できる。
JSON.stringify(value[, replacer[, space]])
ここでReplacerを使えばなんとかなりそう。
文字列化関数を指定してみる。
function replacer (k,v){ e if(typeof v === "function"){ return v.toString() }; return v ; } str= JSON.stringify(obj, replacer) //=> "{"a":"takuya","say":"function (){ console.log(`My name is ${this.a}.`) }"}"
おお?文字列化出来る。 可能性が出てきた。
JSON.parse指定してみると
JSON.parse(str)
//=>Object {a: "takuya", say: "function (){ console.log(`My name is ${this.a}.`) }"}
文字列化したから、関数も文字列になってしまう。そりゃそうだ。
JSON.parse を調べてみると
JSON.parse(text[, reviver])
replacer の逆のreciever があることが分かった。試してみよう。
JSON.parse の第二引数を使ってみる
第二引数で、文字列化した関数
を元に戻せないか試してみよう。
a = '{"a":"takuya","say":"function(){ console.log(this.a)}"}' function reciever (k,v ) { if ( typeof v === "string" && v.match(/^function/) ){ return Function.call(this, "return "+ v )(); } return v } var a = JSON.parse( a ,reciever) a.say() //=>takuya
おお!?戻った戻ったよ! メソッドを入れたObjectをシリアライズして元に戻せるじゃん。
出来るじゃん!
これ、メッチャ便利じゃね?
ネストしたJSON.stringify() も大丈夫!
ネストしたらどうなるのか、試してみた。
a = '{"name":"takuya","pets": [{ "name": "taro", "say": "function(){ console.log(this.name)}" }] } ' function reciever (k,v ) { if ( typeof v === "string" && v.match(/^function/) ){ return Function.call(this, "return "+ v )(); } return v } var a = JSON.parse( a ,reciever) a.pets[0].say(); //=> taro
出来るじゃん。
まとめ
- JSON.parseで関数も渡せる
- JSON .stringify で関数が渡せる
- JSON.stringify ( obj ,replacer) でfunction もString化出来る
- メソッドを持ったObjectをJSONでシリアライズ出来る
- JSONで作ったオブジェクトにメソッドをもたせたまま、転送できる
- JSONはデータ交換だけじゃなく、使い方では「インスタンス」を渡せそう。
- JSONで関数もデシリアライズできるから、状態保存と状態の復元が手軽にできる。
ただし、parse 時の変数のスコープに差異があり、変数のスコープまで意識して完全に同じになる保証はできない。ただthis くらいはなんとかなるとわかった。工夫次第で同じスコープは再現できそう。
JSONの痒い所に手が届かない感じで、世界一のサムライになれない感じがあった。 世界が広がる。Javascriptは戦うサムライになれる。
参考資料
JSON.stringify() - JavaScript | MDN
Function.prototype.call() - JavaScript | MDN
JS 形式の設定ファイルを読み込むときは eval の代わりに new Function を使う - たそがれ日記