2010-04-15 3 views
6

У меня есть этот кусок кода:JavaScript регулярное выражение буквальные сохраняется между вызовами функций

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'); 
     } 
     ... 
    } 
} 

Это работает, если оператор броска не выполняется. В этом случае, в следующий раз, когда я вызову функцию, вызов exec() начнется там, где он остановился, даже если я поставляю ему новое значение «text».

я могу это исправить, написав

уага шаблона = новый 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.

+1

Я взял на себя смелость опубликовать полный, упрощенный тестовый пример, надеюсь, что вы не против. Я тоже видел это поведение и задавался вопросом, почему это было бы. Он выглядит и пахнет ошибкой, но потом, иногда вещи очень тонкие, и удивительно, что и FF, и Chrome будут иметь свои * полностью * различные базовые Javascript-движки. –

+0

Чтобы быть понятным, оно работает до тех пор, пока ошибка/исключение не выбрасывается, но если «какое-то условие» становится истинным и генерируется исключение, тогда функция будет терпеть неудачу при следующем вызове, потому что шаблон продолжается, исключение было брошено? Это наверняка звучит как ошибка, которая из ваших рук. – PatrikAkerstrand

ответ

7

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

+0

В блоге говорится, что он будет исправлен в Firefox 3.7 (и я на 3.6.3). Я думаю, что я просто перестану использовать литералы RE, хотя, как кросс-браузерное решение этого поведения. –

+0

Отлично, спасибо. Обратите внимание, что «... кэшируются ...» должны быть «... * были кешированы некоторыми реализациями по версии ECMAScript 3rd edition ...», за которым следует утверждение о том, что они больше не могут кэшироваться по последней спецификации (к счастью!). –

+0

@Charles: Если вы прекратите использовать литералы, вы окажетесь в мире обид с ускользающими правилами. :-) Просто перезагрузите 'lastIndex' перед использованием (если вы также не гадаете с другими флагами после создания экземпляра). И радуйся, что последняя спецификация зафиксировала эту маленькую глупость. –

0

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

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

+0

@Colin: кроме этого * не имеет * глобальную область действия, не более, чем объект в 'var x = {};' имеет глобальную область видимости. Это также буквально, но вы будете получать разные объекты при каждом вызове функции. –

1

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

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

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

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

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

Update Алан Мур points toarticle by Steve Levithan, в котором Levithan делает заявление о том, что ECMAScript третье издание спецификации может иметь разрешено этот вид кэширования. К счастью, это не разрешено в версии ECMAScript 5th (спецификация, над которой я работал) и, следовательно, будет ошибкой Real Soon Now. Спасибо Алан!

Смежные вопросы