Литерал регулярного выражения JavaScript сохраняется между вызовами функций - PullRequest
6 голосов
/ 15 апреля 2010

У меня есть этот кусок кода:

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>

Регулярное выражение продолжается с 'four' на втором вызове в FF и Chrome, а не в IE7 или Opera.

Ответы [ 3 ]

7 голосов
/ 15 апреля 2010

Объекты RegExp, созданные с помощью литерала регулярных выражений, кэшируются, но new RegExp всегда создает новый объект. Кэшированные объекты также сохраняют свое состояние, но правила, регулирующие этот аспект, по-видимому, не очень ясны. Стив Левитан говорит об этом в этом блоге (внизу).

1 голос
/ 15 апреля 2010

Я выхожу на передний план: я думаю, что поведение, которое вы видите, является ошибкой в ​​движках FF и Chrome Javascript (ересь!). Удивительно, но это должно произойти в двух таких разных двигателях. Похоже, ошибка оптимизации. В частности, в разделе 7.8.5 спецификация гласит:

Литерал регулярного выражения - это элемент ввода, который преобразуется в объект RegExp (см. 15.10) при каждой оценке литерала.

Единственная комната для маневра, которую я вижу, - это фраза "... каждый раз, когда литерал оценивается " (мой акцент). Но я не понимаю, почему полученный объект должен быть магическим образом сохранен больше, чем любой другой литерал объекта, такой как:

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

Там последующие вызовы func1 дадут вам различных объектов. Поэтому мое высказывание выглядит для меня как ошибка.

Обновление Алан Мур указывает на и статью Стива Левитана , в которой Левитан утверждает, что спецификация ECMAScript 3-го издания может иметь разрешено такое кэширование. К счастью, это не разрешено в ECMAScript 5-й редакции (спецификация, из которой я работал) и, следовательно, станет ошибкой в ​​ближайшее время. Спасибо Алан!

0 голосов
/ 15 апреля 2010

Я не знаю ответа, но рискну догадаться:

Литеральное выражение, являющееся шаблоном, имеет глобальную область действия и вычисляется (в объект RegExp) только один раз, тогда как если вы используете new Regexp, его аргумент все еще глобален, но представляет собой просто строку, а не RegExp.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...