JavaScriptのString#matchと正規表現

どうもよくわかってなかったというか、誤解していた部分があったようなので、とりあえずFirebugで書いてみた。

var log = console.log;
log("a/a".match("a"));                        // "a"
log(RegExp.$1);                               //

log("a/a".match("(a)/"));                     // "a/", "a"
log(RegExp.$1);                               // a

log("b/b".match(/(\w)\//));                   // "b/" , "b"
log(RegExp.$1);                               // b

log("c/c".match("(\w)"));                     // null
log(RegExp.$1);                               // b

log("d/d".match("(\\w)"));                    // "d" ,"d"
log(RegExp.$1);                               // d

log("e/e/e/".match(new RegExp("(e)/","g")));  // "e/", "e/", "e/"
log(RegExp.$1);                               // d

log("e/e".match(new RegExp("(\w)","g")));     // null
log(RegExp.$1);                               // d

log("f/ff".match(new RegExp("(\\w)","g")));   // "f", "f", "f"
log(RegExp.$1);                               // f
  • String#matchはnew RegExp("expression")や/expression/を使わなくても正規表現が有効
  • /expression/以外では、\を\でエスケープする必要がある(\\w)
  • /は/expression/で正規表現を書いたときのみエスケープが必要(/\//など)で、そうでなければ必要ない

matchをreplaceに変えてみると、

var log = console.log;
log("replace");
log("a/a".replace("a","x"));                        // x/a
log(RegExp.$1);                                     //

log("a/a".replace("(a)/","x"));                     // a/a
log(RegExp.$1);                                     //

log("b/b".replace(/(\w)\//,"x"));                   // xb
log(RegExp.$1);                                     // b

log("c/c".replace("(\w)","x"));                     // c/c
log(RegExp.$1);                                     // b

log("d/d".replace("(\\w)","x"));                    // d/d
log(RegExp.$1);                                     // b

log("e/e/e/".replace(new RegExp("(e)/","g"),"x"));  // xxx
log(RegExp.$1);                                     // e

log("e/e".replace(new RegExp("(\w)","g"),"x"));     // e/e
log(RegExp.$1);                                     // e

log("f/ff".replace(new RegExp("(\\w)","g"),"x"));   // x/xx
log(RegExp.$1);                                     // f

log("g".replace(new RegExp("(\\w)","g"), RegExp.$1 + "$1x"));   // fgx
log("h".replace(new RegExp("(\\w)","g"), function(){return RegExp.$1 + "x"} ));   // hx
  • String#replaceはnew RegExp("expression")や/expression/を使わないと正規表現が有効にならない
  • replaceの場合も、RegExp.$nは有効
  • replaceの第二引数にRegExp.$1を書くとreplaceの実行前にRegExp.$1が参照されるので、前回の値が(存在すれば)入っていることになる。第二引数をfunctionにして、その中でRegExp.$1を参照するとそのときマッチした値が入っている。(考えてみれば当たり前。functionには引数としてマッチした値が渡ってくるのでRegExp.$nを使う必要は特にない。)

正直String#matchが、デフォで正規表現が有効になっていることに驚きました。
で、oreillyのJavaScript第5版で確認してみると、p.215あたりにちゃんと書いてありました。
String#search、String#matchは正規表現以外の引数を渡されるとRegExpに渡され、String#replaceは文字列を渡すと文字列そのもの使うので、注意してください。と。
知らぬは己ばかりなり。