プリミティブクラスの拡張
CouchDBのスレでテンプレートエンジンの話があがっていたので、首を突っ込んでみた。いろいろあるんだがEJSをおすすめしたら、<%= %> がデフォでHTMLエスケープされないのが気に入らん、だそうで。Railsはじめたときに、確かに気に入らなかったんだが、もう慣れちゃったよ、と思っていたら、RailsでもデフォルトでHTMLエスケープされるようになった、ということを別の人から教えてもらった。
ちなみに、MLの議論のほうは、wikiにまとめられています。
http://wiki.apache.org/couchdb/Generating%20HTML%20from%20Javascript%20shows%20and%20lists
mustache.js はRubyの実装の移植版ですね、おすすめらしい。
それはともかく、もうEJSで作っちゃったコードがたくさんあるので、今後もEJSを使うんだろう。
ということで、EJSをハックしにかかって、Railsと同じように <%= foo %> <%= foo.htmlSafe() %> と書けるようにした。
それはともかく、prototype拡張でよく分からないことがある。htmlSafe() を文字列で透過的に呼べるようにStringを拡張した。拡張の危険性は分かっているつもりだが。
String.prototype.htmlSafe = function(){ this._html_safe = true; return this; } String.prototype.isHtmlSafe = function(){ return this._html_safe == true; }
でもって、次のプログラムを実行する。結果は分かりやすいように、 // -> で書いた。print = console.log と書いているのはRhino と SpiderMonkey と Firebug のコンソールの3つで確認するため。
print = console.log; var a = new String("a"); var b = "a"; print(typeof a); // -> object print(typeof b); // -> string a.htmlSafe(); b.htmlSafe(); print(a.isHtmlSafe()); // -> true print(b.isHtmlSafe()); // -> false
tyepof が違うのはまぁよしとしよう。参照型と値型。 で、b.htmlSafe() が自身を書き換えない点に注意をしたい。というか、ここまで書いた時点で、値型でメソッド呼び出しができるの辺りに疑問を持った。
var b = "a"; String.prototype.htmlSafe = function(){ this._html_safe = true; print(this === b); // -> false return this; } b.htmlSafe();
ええっと、これ、暗黙でStringオブジェクトにしているってこと?となると、"" のような値型とStringのような参照型でオートボクシングしているってこと?
なにこの〜と思って、apply でも確認する。
print = console.log; var b = "a"; String.prototype.htmlSafe = function(){ this._html_safe = true; print(this === b); // -> false return this; } String.prototype.htmlSafe.apply(b, []);
なんかそうっぽい。
var a = new String("a"); String.prototype.htmlSafe = function(){ this._html_safe = true; print(this === a); // -> true return this; } a.htmlSafe();
OK。それはともかく、元の値には影響を及ぼさないので、拡張する副作用系メソッドを定義する場合には、return this; を必ずつけて、
var a = b.htmlSafe();
のようにやるのがよさそうですね。
- まぁでも、Rails の htmlSafe() のようなやり方よりも、そもそも <%= %> じゃなく [%= %] とか別のラベルにすればいいじゃん、と思っている。
- ところで、javascript + "オートボクシング" で検索するとJavaが引っかかって邪魔なことこの上ない。