coffeescriptでメタプログラミングをしようとしてハマったので、メモ。
最初に思いついたコード
最初、メタプログラミングしようとして下記のようなコードを書いた。
class Hoge i_methods = foo: message: 'foo' bar: message: 'bar' for i of i_methods @::[i] = -> console.log(i_methods[i].message) c_methods = fiz: message: "fiz" piyo: message: "piyo" for c of c_methods @[c] = -> console.log(c_methods[c].message) console.log('----') new Hoge().foo() new Hoge().bar() console.log('----') Hoge.fiz() Hoge.piyo()
実行結果
---- bar bar ---- piyo piyo
あれ・・・意図したのと結果が違う。
この時、出力されたJavaScriptはこんな感じ。
変換されたJavaScript
var Hoge; Hoge = (function() { var c, c_methods, i, i_methods; function Hoge() {} i_methods = { foo: { message: 'foo' }, bar: { message: 'bar' } }; for (i in i_methods) { Hoge.prototype[i] = function() { return console.log(i_methods[i].message); }; } c_methods = { fiz: { message: "fiz" }, piyo: { message: "piyo" } }; for (c in c_methods) { Hoge[c] = function() { return console.log(c_methods[c].message); }; } return Hoge; })(); console.log('----'); new Hoge().foo(); new Hoge().bar(); console.log('----'); Hoge.fiz(); Hoge.piyo();
ループで同じ変数が上書きされているのが原因っぽい。
という訳で、修正した。
class Hoge @template: (obj) -> -> console.log(obj.message) i_methods = foo: message: 'foo' bar: message: 'bar' for i of i_methods @::[i] = @template(i_methods[i]) c_methods = fiz: message: "fiz" piyo: message: "piyo" for c of c_methods @[c] = @template(c_methods[c]) console.log('----') new Hoge().foo() new Hoge().bar() console.log('----') Hoge.fiz() Hoge.piyo()
コードを読めば分かるけど、関数を返す関数を定義しておいて、ループ内で呼んでいる。
実行結果
---- foo bar ---- fiz piyo
これで意図した動きになった!