【问题标题】:RegExp.exec() returns null when called repeatedly in Firefox?RegExp.exec() 在 Firefox 中重复调用时返回 null?
【发布时间】:2019-04-13 01:58:00
【问题描述】:

我在 Firefox 10 和 11 中使用正则表达式函数 exec() 时遇到了问题。

该函数在被大量调用时似乎表现不稳定。在正确的结果中,也返回了很多null。从 Safari 5.1.3、Chrome 18 和上述 Firefox 版本,我只能在 Firefox 中看到问题。

我创建了一个 JSFiddle 来演示这个问题:http://jsfiddle.net/KSH3S/,来源:

var i, x = "";

for (i = 0; i < 10000; i++) {
    var matches = /foo/g.exec('sdkfjfooasdknal');
    x += matches + "<br>";
}

$('body').html(x);

在我的两个 Firefox 版本中,这会返回 40x foo、1x null、41x foo,然后每次调用都会在这两者之间进行交换。

你遇到过吗?

【问题讨论】:

  • 如果使用 same 正则表达式对象调用会发生什么? (例如var foo = /foo/g 在循环外)
  • 您真正想解决什么问题?对带有“g”标志的正则表达式重复调用 exec() 会累积从一次调用到下一次调用的某些状态,以便您在同一源字符串中找到多个匹配项。
  • @jfriend00 但在这种情况下它是一个 new 正则表达式对象,即使 的情况,它也应该是一个可预测的模式。问题是 FF 行为不规律。
  • @pst - 你确定这是一个新的正则表达式对象吗? JS 有函数作用域,而不是块作用域。
  • @jfriend00 变量的作用域是函数。对象不是。 /foo/g 构造一个新的 RegExp 对象,就像 "foo" 构造一个新的字符串对象一样。除非当然有一些“有趣的缓存”正在进行(也许在第 80 次这样的 RegExp 构造之后?)。无论如何,仍然不能解释不稳定的行为:-)

标签: javascript regex firefox


【解决方案1】:

这是 Firefox 中 JS 引擎中的一个错误,就像 jfriend00 所说的那样。应该在 Firefox 12 中修复。见https://bugzilla.mozilla.org/show_bug.cgi?id=728021

【讨论】:

    【解决方案2】:

    问题与循环内正则表达式的隐式声明有关。我的猜测是浏览器将它缓存在某个地方或不知何故被它弄糊涂了。

    如果你每次都通过for循环显式地创建一个新的正则表达式对象,那么Firefox就不再有这个问题了:

    var x = "";
    
    for (var i=0; i<10000; i++) {
        var re = new RegExp("foo", "g");
        var matches = re.exec( 'sdkfjfooasdknal' );
        x += matches+"<br>";
    }
    
    $('body').html( x );
    

    http://jsfiddle.net/jfriend00/F49db/

    而且,您显式声明正则表达式的方式并不重要,因为此方法也适用:

    var x = "";
    
    for (var i=0; i<10000; i++) {
        var re = /foo/g;
        var matches = re.exec( 'sdkfjfooasdknal' );
        x += matches+"<br>";
    }
    
    $('body').html( x );
    

    【讨论】:

    • +1 表明它可能与 RegExp 文字有关。我也怀疑它与 VM 缓存有关,这可能与 40-good(新)、40-null(与 /g? 重用前 40 个)、交替循环(与 /g 循环重用)有关?)。
    • “如果显式创建”的说法是错误的。正如答案所示,work-about使用中间变量 消除了 FF 中的问题。在所有情况下,应该创建相同数量的正则表达式对象。大概没有变量 FF 积极地缓存 RegExp 对象有点太多了。 (例如,它没有注意到 /g 或假设它永远不会在立即值情况下使用。)
    • (我的论点是re = /foo/g; re.exec 并不比(/foo/g).exec 更“显式地创建”。在这两种情况下,只有一个RegExp 对象字面量。)
    • @pst - 我并不是说这两种情况应该有区别,但显然有。并且显式地创建一个新的正则表达式对象并将其分配给另一个变量,这使得解释器更难做你想做的事情之外的任何事情。显然,OP 的情况已经遇到了一些边缘情况(可能与缓存相关),并且使用中间变量将解释器从边缘情况中拉开以强制其正常运行。
    • 同样,创建对象没有区别。区别在于变量赋值(或删除表达式中的直接用法,如前所述)。
    【解决方案3】:

    这只是部分解释。如果您尝试this fiddle,它似乎工作正常。那么问题或错误可能在于文字的正则表达式解析?我不确定,但循环优化是否会以不同于变量的方式处理文字。

    这里是小提琴代码:

    var x = "";
    
    for (var i=0; i<10000; i++) {
        var a='sdkfjfooasdknal';
        var b=/foo/g;
        var matches = b.exec(a);
        x += matches+"<br>";
    }
    
    $('body').html( x );​
    

    更新: 这是我试图强迫它脱离正常循环的东西,问题又出现了。 这是小提琴http://jsfiddle.net/xXYdF/

    var x = "";
    i=200;
    function reg(){
        var matches = /foo/g.exec('sdkfjfooasdknal');
        x += matches+"<br>";
    }
        function run(){
      setTimeout(function(){
        if(i--){
          reg();
          run();
          $('body').html( x );
        }
      },10);
    }
    
    run();
    

    【讨论】:

    • +1 对于半计数器 jfriend00 并显示它不是正则表达式文字的功能。我猜仍然可能与缓存和对象重用有关。
    • @pst - 这与我的想法有何不同?就像我的一样,这对循环内的正则表达式进行了明确的重新声明。他们使用var b = /foo/g 而不是new RegExp(),但无论哪种方式,每次循环都会显式创建一个新对象。
    • 仅供参考,将字符串分解为单独的 a 变量在这里没有什么不同。使这一项工作的原因在于,每次循环都会显式地重新创建正则表达式。
    • @jfriend00 我有点猜到了,但我只是在尝试不同的事情来看看发生了什么。为了更清楚,我可能应该撤消那个。
    • @jfriend00 这是一个半计数器 因为它不使用RegExp构造函数而是仍然使用RegExp文字。我同意结论(这是一个错误),但不同意原因(文字与显式构造函数调用)。 work-about 因此使用变量,而不是切换到new RegExp
    猜你喜欢
    • 2011-06-11
    • 2020-02-02
    • 2017-08-12
    • 1970-01-01
    • 2018-11-29
    • 1970-01-01
    • 2019-06-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多