正規表現の挙動がブラウザ間で異なる件

category-icon

 ヘッドラインモードでの表示周りを整備していて、記事ごとのスタイルシート指定が効いていなかったので、効くようにスクリプトを書き換えましたが、正規表現周りの挙動がブラウザ間で異なっていて、ちょっとはまったのでメモ。簡単な例を示します。
<script type="text/javascript">
function doRex(){
 var example = "J'adore Firefox!"; // I like Firefox!
 var RE = /Fire/g;
 var result = RE.exec(example); 
 if(result) alert("マッチ: " + result + "¥nlastIndex: " + RE.lastIndex);
       else alert("マッチしません!:" + "¥nlastIndex: " + RE.lastIndex);
}
</script>
<button onclick="doRex();"> 実 行 1 </button>

 上のdoRex()関数を実行すると、最初ボタンを押したときはちゃんとマッチしますが、2回目にボタンを押したときの結果がブラウザで異なります。Firefox3.0.7, Chrome1.0, Opera9.64では、「マッチしない」、IE6-8とSafari4 public betaでは、マッチして1回目と同じ結果です。なぜ、こういうことになるかというと、lastIndex絡みです。1回目の実行に対して、lastIndexの値が保持されているからです。2回目の実行で、マッチしないとなると、RE.lastIndexはまた0に戻るので、3回目の結果は初回と同じく「マッチ」です。

 でも、疑問なのが、doRex()という関数の中で定義したローカルな変数だから、doRex()を実行する度に、lastIndexの値はリセットされるのが正しい気もするんだけどなぁ。doRex()の中ではなく、グローバルな空間で同様のことをやれば、SafariもIEも2回目は「マッチしない」となり、これは納得できる挙動なんですが。

 では、このブラウザ間の差異をなくすにはどうするか?ひとつは、RE.exec(example) した後に、
RE.lastIndex = 0; // 値をリセット
という記述を加えて、明示的にlastIndexを元に戻す方法。もうひとつは、new演算子を使う方法です。これなら、どのブラウザでも同じ挙動、何度ボタンを押しても、「マッチ」となります。new RegExp("foo", "g")/foo/g って同じと思っていたんだけど。
<script type="text/javascript">
function doRex2(){
 var example = "J'adore Firefox!"; // I like Firefox!
 var RE2 = new RegExp("Fire", "g"); // new演算子を使用する
 var result2 = RE2.exec(example);
 if(result2) alert("マッチ: " + result2 + "¥nlastIndex: " + RE2.lastIndex);
       else alert("マッチしません!:" + "¥nlastIndex: " + RE2.lastIndex);
}
</script>
<button onclick="doRex2();"> 実 行 2 </button>

 上のコードであれば、ブラウザ間の差異がない。

 でも、Safari/IE と Firefox/Chrome/Opera、どっちが正しい挙動なんだろうな。今イチ、すっきりしないです。

— posted by martin at 08:58 am   commentComment [4]  pingTrackBack [0]

この記事に対するコメント・トラックバック [4件]

scrollUp1. edvakf Website — 2009/03/10@01:46:58

http://d.hatena.ne.jp/javascripter/20090113/1231863436Link

http://mayokara.info/deadspace/log/d200901.htmlLink の中で "static" とページ内検索

http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/7_Lexical_Conventions.html#section-7.8.5Link (仕様の翻訳)

などにちょっと書いてありますけど、正規表現リテラルの仕様的には doRex 関数を最初に評価した時点で /Fire/ リテラルにオブジェクトが割り当てられて、RE 変数にはそれへの参照が入るそうです。doRex() を何度読んでも doRex の内部的なハッシュテーブルが変わらない限りは参照は変わらないはずなので、 RE 変数は同じものを差すということじゃないですかね。

たぶん仕様では Firefox/Chrome/Opera が正しいと思いますけど、詳しくは知りません。

Owner Comment martin Website  2009/03/10@05:42:38

こんばんは。有用なリンクありがとうございます。3つめにあった、

正規表現リテラルは走査時に RegExp オブジェクト に変換される入力要素である。オブジェクトはそれを含むプログラムまた関数の評価の開始前に生成される。

につきますね。なるほど、どこに置こうが、コンパイル時に評価されちゃうと。たまに /abc/ と new RegExp("abc") は同じですよといった記述を見ますが、全然別物なんですね。大変、参考になりました:)

3. edvakf Website — 2009/03/10@04:15:34

訂正です。

× doRex() を何度読んでも

○ doRex() を何度呼んでも

4. 名無し — 2013/02/12@01:53:39

現在Chromeで試してみるとマッチします

この記事に対する TrackBack URL:

設定によりTB元のページに、こちらの記事への言及(この記事へのリンク)がなければ、TB受付不可となりますのであらかじめご了承下さい。

コメントをどうぞ。 名前(ペンネーム)と画像認証のひらがな4文字は必須で、ウェブサイトURLはオプションです。

ウェブサイト (U):

タグは使えません。http://・・・ は自動的にリンク表示となります

:) :D 8-) ;-) :P :E :o :( (TT) ):T (--) (++!) ?;w) (-o-) (**!) ;v) f(--; :B l_P~

     
T: Y: ALL: Online:
Created in 0.0139 sec.
prev
2018.7
next
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31