JavaScript 正则表达式文字在函数调用之间持久存在

JavaScript regular expression literal persists between function calls

提问人:Charles Anderson 提问时间:4/15/2010 最后编辑:T.J. CrowderCharles Anderson 更新时间:4/15/2010 访问量:2104

问:

我有这段代码:

function func1(text) {

    var pattern = /([\s\S]*?)(\<\?(?:attrib |if |else-if |else|end-if|search |for |end-for)[\s\S]*?\?\>)/g;

    var result;
    while (result = pattern.exec(text)) {
        if (some condition) {
            throw new Error('failed');
        }
        ...
    }
}

除非执行 throw 语句,否则这有效。在这种情况下,下次我调用该函数时,exec() 调用会从它停止的地方开始,即使我为它提供了一个新的值 'text'。

我可以通过编写来修复它

var 模式 = new RegExp('.....');

相反,但我不明白为什么第一个版本会失败。正则表达式如何在函数调用之间持久化?(这在最新版本的 Firefox 和 Chrome 中发生。

编辑完整的测试用例:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
</style>
<script type='text/javascript'>
function func1(text, count) {

    var pattern = /(one|two|three|four|five|six|seven|eight)/g;

    log("func1");
    var result;
    while (result = pattern.exec(text)) {
        log("result[0] = " + result[0] + ", pattern.index = " + pattern.index);
        if (--count <= 0) {
            throw "Error";
        }
    }
}

function go() {
    try { func1("one two three four five six seven eight", 3); } catch (e) { }
    try { func1("one two three four five six seven eight", 2); } catch (e) { }
    try { func1("one two three four five six seven eight", 99); } catch (e) { }
    try { func1("one two three four five six seven eight", 2); } catch (e) { }
}

function log(msg) {
    var log = document.getElementById('log');
    var p = document.createElement('p');
    p.innerHTML = msg;
    log.appendChild(p);
}

</script>
</head>
<body><div>
<input type='button' id='btnGo' value='Go' onclick='go();'>
<hr>
<div id='log'></div>
</div></body>
</html>

在 FF 和 Chrome 上第二次调用时,正则表达式继续使用“四”,而不是在 IE7 或 Opera 上。

JavaScript的 正则表达式

评论

1赞 T.J. Crowder 4/15/2010
我冒昧地发布了一个完整的、简化的测试用例,希望你不介意。我也看到了这种行为,想知道为什么会这样。它看起来和闻起来都像一个错误,但是,有时事情非常微妙,令人惊讶的是,FF 和 Chrome 都会拥有完全不同的底层 Javascript 引擎。
0赞 PatrikAkerstrand 4/15/2010
需要明确的是,只要不抛出错误/异常,它就会起作用,但是如果“某个条件”变为真并且抛出异常,那么该函数将在下一次调用时失败,因为模式从抛出异常的位置继续?这听起来确实像是一个你无法控制的错误。

答:

0赞 Colin Fine 4/15/2010 #1

我不知道答案,但我会冒险猜测:

作为模式的文字表达式具有全局作用域,并且仅计算一次(进入 RegExp 对象),而如果您使用它的参数,它仍然是全局的,但只是一个字符串,而不是 RegExp。new Regexp

评论

0赞 T.J. Crowder 4/15/2010
@Colin:除了它没有全局作用域,就像对象具有全局作用域一样。这也是一个字面意思,但你会在每个函数调用时得到不同的对象。var x = {};
1赞 T.J. Crowder 4/15/2010 #2

我在这里要说的是:我认为你看到的行为是 FF 和 Chrome 的 Javascript 引擎中的一个错误(异端邪说!不过,令人惊讶的是,它应该发生在两个如此不同的引擎中。看起来像一个优化错误。具体来说,规范的第 7.8.5 节说:

正则表达式文本是一个输入元素,每次计算文本时都会转换为 RegExp 对象(参见 15.10)。

我看到的唯一回旋余地是在短语“..每次评估字面意思“(我的强调)。但我不明白为什么生成的对象应该比任何其他对象字面意思更神奇地保留,例如:

function func1() {
    var x = {};
    return x;
}

在那里,后续调用将为您提供不同的对象。因此,我说这对我来说看起来像一个错误。func1

更新Alan Moore 指出了 Steve Levithan 的一篇文章,其中 Levithan 声称 ECMAScript 第 3 版规范可能允许这种缓存。幸运的是,从 ECMAScript 第 5 版(我正在使用的规范)开始,它不允许使用,因此,它很快就会成为一个错误。谢谢艾伦!

7赞 Alan Moore 4/15/2010 #3

通过正则表达式文本创建的正则表达式对象将被缓存,但始终会创建一个新对象。缓存的对象也会保存它们的状态,但控制这方面的规则显然不是很清楚。史蒂夫·列维坦(Steve Levithan)在这篇博文中谈到了这一点(接近底部)。new RegExp

评论

0赞 Charles Anderson 4/15/2010
博客说它将在 Firefox 3.7 中修复(我在 3.6.3 上)。我想我会停止使用 RE 文字,作为这种行为的跨浏览器解决方案。
0赞 T.J. Crowder 4/15/2010
太好了,谢谢。请注意,“......被缓存......”应该是“...*were*从 ECMAScript 第 3 版开始被某些实现缓存......”然后是声明,从最新规范开始,它们可能不再被缓存(谢天谢地!
0赞 T.J. Crowder 4/15/2010
@Charles:如果你停止使用文字,你就会陷入一个逃避规则的伤害世界。:-)只需在使用前重置(除非您在实例化后还使用其他标志)。很高兴最新的规范解决了这个小问题。lastIndex