【问题标题】:JavaScript regular expression literal persists between function callsJavaScript 正则表达式文字在函数调用之间保持不变
【发布时间】:2010-04-15 12:43:57
【问题描述】:

我有这段代码:

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 pattern = 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 上,第二次调用时,正则表达式以 'four' 继续。

【问题讨论】:

  • 我冒昧地发布了一个完整的、简化的测试用例,希望你不要介意。我也看到了这种行为,想知道为什么会这样。它看起来和闻起来都像一个错误,但有时事情非常微妙,令人惊讶的是,FF 和 Chrome 都会在其完全不同的底层 Javascript 引擎中拥有它​​。
  • 为了清楚起见,只要不抛出错误/异常,它就可以工作,但是如果“某些条件”变为真并抛出异常,则该函数将在下一次调用时失败因为模式从引发异常的地方继续?这听起来确实像是你无法控制的错误。

标签: javascript regex


【解决方案1】:

通过正则表达式创建的RegExp 对象被缓存,但new RegExp 总是创建一个新对象。缓存的对象也会保存它们的状态,但管理这方面的规则显然不是很清楚。 Steve Levithan 在this blog post(接近底部)中谈到了这一点。

【讨论】:

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

我会在这里冒险:我认为您看到的行为是 FF 和 Chrome 的 Javascript 引擎中的错误(异端!)。不过,令人惊讶的是它应该发生在两个这样不同的引擎中。看起来像一个优化错误。具体来说,the spec 的第 7.8.5 节说:

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

我看到的唯一回旋余地是短语“..每次文字被评估”(我的重点)。但我不明白为什么结果对象应该比任何其他对象文字更神奇地保留,例如:

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

在那里,随后对func1 的调用将为您提供不同 对象。因此我说这对我来说似乎是一个错误。

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

【讨论】:

    【解决方案3】:

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

    作为模式的文字表达式具有全局范围,并且只被评估一次(到 RegExp 对象中),而如果您使用 new Regexp,它的参数仍然是全局的,但只是一个字符串,而不是 RegExp。

    【讨论】:

    • @Colin:除了它没有具有全局范围之外,var x = {}; 中的对象具有全局范围。这也是一个字面量,但每次函数调用都会得到不同的对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-23
    • 1970-01-01
    • 1970-01-01
    • 2012-11-23
    • 2016-11-18
    • 2012-02-22
    • 2023-04-07
    相关资源
    最近更新 更多