2014-04-16

JavaScript規格の正規表現リテラルの文面の疑問点

JavaScript規格、ECMA-262 Edition 5.1を読み進めていて、以下の文面が気になった。

A regular expression literal is an input element that is converted to a RegExp object (see 15.10) each time the literal is evaluated. Two regular expression literals in a program evaluate to regular expression objects that never compare as === to each other even if the two literals' contents are identical.

正規表現リテラルは、リテラルが評価されるごとに、RegExpオブジェクトに変換される入力要素である。プログラム中の二つの正規表現リテラルは、たとえ二つのリテラルの中身が同一であっても、===と比較されることのない正規表現をオブジェクトに評価される。

§7.8.5 Regular Expression Literals

つまりは、/./ === /./ はfalseになるということだ。正規表現リテラルが評価されるごとに別のオブジェクトになるということは、以下のようになる。


for ( var elem in [ true, false ] )
{
    var r = /./ ; // ひとつの正規表現リテラル 
    if ( elem )
        var a = r ;
    else
        var b = r ;
}

a === b ; // false

なるほど、確かに複数回評価されている。

しかし、文面では二つの正規表現リテラルとあるが、このプログラム中に、正規表現リテラルはひとつしかない。ひとつの正規表現リテラルが二回評価されるだけだ。と、屁理屈めいた疑問も生ずる。

筆者が規格を読むときは、文面は全て間違っているという前提で読むので、このような屁理屈のような疑問が生ずる。

ドワンゴ広告

この記事はドワンゴ勤務中にやんごとなき理由により自分の机を離れて書いた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

10 comments:

  1. for (var e in [true, false])
    ..;

    これeはtrueとfalseではなく0と1になります。
    結果は間違ってませんがコードの意味的にわかりづらくなってますね。

    [true, false].forEach(function(e) {
    ..;
    });

    ReplyDelete
  2. うーん、JavaScriptの型変換あたりの文面はまだ読んでない。

    ReplyDelete
  3. 型変換の話ではなく、for inはobjectのkeyを列挙するものです。つまりArrayならindexの0,1,2,3,...を列挙します。

    また仕様の文は、文字列リテラルとの比較のための補足ではないでしょうか。つまり、 "." === "." はtrueだけど、/X/ === /Y/ は中が同じでも必ずfalseだと。

    ReplyDelete
    Replies
    1. > /X/ === /Y/ は中が同じでも必ずfalseだと。
      んなこたぁわかったうえで、リテラル1個だったらどうなんのよ、ていう話でしょ。

      Delete
  4. まず話を正さないといけないのですがこれは正規表現の問題ではありません。
    forループないでのコンテキスト生成と、オブジェクトの等価評価の問題です

    ReplyDelete
  5. 3rd -> 5.1th の仕様変更で、従来のスキャン時から、評価時されるたびにnew RegExpして毎回オブジェクトが生成されるようになりました。
    http://nazomikan.hateblo.jp/entry/2014/03/12/020734

    ReplyDelete
  6. ES3 では、以下のコードは true になります。ES5では false です。

    var r = function(){return /./;}
    var a = r();
    var b = r();
    a === b // {ES3:true, ES5:false}

    §7.8.5 Regular Expression Literals の最初は ES3 では以下と書かれていました。

    A regular expression literal is an input element that is converted to a RegExp object (section 15.10) when it is scanned. The object is created before evaluation of the containing program or function begins. Evaluation of the literal produces a reference to that object; it does not create a new object.

    ブログの記事で指摘されているように、ES5では評価されるたびに、
    毎回オブジェクトを新規作成する仕様となりました。

    当然ですが、以下のような書き方をすれば ES3 でも ES5 でも同じオブジェクトになります。

    var r = (function(){return /./;})();
    var a = r;
    var b = r;
    a === b // {ES3:true, ES5:true}

    ReplyDelete
  7. で、Hikaruさんもコメントで指摘されていますが、ブログに書かれている検証コードは、

    var r = /./;
    var a = r;
    var r = /./;
    var b = r;

    a === b // false

    と同じようなことをしているものなので、例としては適切ではありませんね。

    ReplyDelete
  8. RegExpにはlastIndexのように、副作用のあるプロパティが存在している、という点の認識こそが重要ではないでしょうか。文字列とに違いとしても。

    こういう仕様ならば、
    ES5での評価方法が自然で、
    ES3のリテラルごとに一つのインスタンス(とも限らない?)を再利用では、問題のほうが大きいでしょう。

    ReplyDelete
  9. 関係ないけど、Javaで
    var a = "hoge";
    System.out.println(a == a);
    System.out.println("fuga" == "fuga");
    System.out.println("foobar" == ("foo" + "bar"));
    を比較してみると面白いかも。

    ReplyDelete

You can use some HTML elements, such as <b>, <i>, <a>, also, some characters need to be entity referenced such as <, > and & Your comment may need to be confirmed by blog author. Your comment will be published under GFDL 1.3 or later license with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.