Dec 29, 2005

Safariでreplace callback のエミュレーション

replaceメソッドをhackしてsafariでもreplace callbackを使えるようにしてみた。Safari バージョン 2.0.2(416.13)で上手く動いたそうです。手元に環境が無いので細かい検証はできませんが、とりあえず。
http://la.ma.la/misc/js/replace_callback/

解説

JavaScriptのString#replaceメソッドは文字列を置換して新しい文字列を返すメソッドですが、第二引数に置換後の文字列を指定する代わりにfunctionオブジェクトを渡してやると、Perlでいうところのeオプションみたいなことができます。

// 大文字を小文字に、小文字を大文字に
String.prototype.swapcase = function(){
    return this.replace(/([a-z])|([A-Z])/g,function($0,$1,$2){
        return ($1) ? $0.toUpperCase() : $0.toLowerCase()
    })
}
"Hello World!!".swapcase() // hELLO wORLD!!

第一引数にはマッチ文字全体、それ以降には後方参照用にカッコで囲った部分が渡されます。その後にはマッチ箇所の先頭からのoffset値、String全体が渡されます(ここでは使ってません)

// 少しわかりやすく書くと
String.prototype.swapcase = function(){
    var reg = /([a-z])|([A-Z])/g;
    var callback = function(match_text,lower,upper,index,self){
        return (lower) ? lower.toUpperCase() : upper.toLowerCase()
    };
    return this.replace(reg, callback)
}

こんな感じになります。単純な置換だけではなくて、文字列の出現頻度を調べたりとか、簡単なテンプレートシステムを組んだりすることもできます。

// Ruby風の変数展開
String.prototype.fill = function(param){
    return this.replace(/#\{(.*?)\}/g,function($0,$1){
        return (param[$1] != undefined) ? param[$1] : ""
    })
}
"My name is #{name}.".fill({ name : "John" }) // My name is John

で、これを知ってからゴリゴリ使ってるのですが、どうもSafariでは使えないという話らしく
http://jszen.blogspot.com/2005/12/safari-and-stringreplace-method.html

次のリリースで直るそうですが、直ったところで古いバージョン使ってる人はいるわけなので作ってみました。本来のreplaceメソッドより動作は遅いはずなのでsafariの場合だけ適用するのが良いです。

いくらなんでもsafari酷いなあ、と思ってたのですが、互換性を気にして便利なものを使えないよりは、自前で実装しまったほうが楽なケースも多いんじゃないかと。

ブラウザの差異は大体こういう方法で吸収できるはず。

参考

ECMA-262 3rd Edition 邦訳 String.prototype.replaceのところ
http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/15-5_String_Objects.html#section-15.5.4.11

正規表現のeオプションをJavaScriptでエミュレート
http://hail2u.net/blog/coding/emulate_regexp_e_option_in_js.html
Posted at 14:13 | WriteBacks (9) | Edit
Edit this entry...

wikieditish message: Ready to edit this entry.
















A quick preview will be rendered here when you click "Preview" button.