2010-04-15 6 views
6

Mam ten fragment kodu:JavaScript wyrażenie regularne dosłowne powtarzał między wywołań funkcji

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

To działa, chyba że oświadczenie rzut jest wykonywany. W takim przypadku, gdy następnym razem zadzwonię do funkcji, wywołanie exec() rozpocznie się tam, gdzie zostało przerwane, mimo że dostarczam mu nową wartość "text".

mogę go naprawić pisząc

var pattern = new RegExp ('.....');

zamiast tego, ale nie rozumiem, dlaczego pierwsza wersja zawodzi. Jak zachowuje się wyrażenie regularne między wywołaniami funkcji? (To się dzieje w najnowszych wersjach przeglądarek Firefox i Chrome).

Edit Kompletny przypadek testowy:

<!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> 

Wyrażenie regularne kontynuuje „cztery”, jak z drugiego naboru na FF i Chrome nie na IE7 lub Opera.

+1

Pozwoliłem sobie opublikować kompletny, uproszczony test, mam nadzieję, że nie masz nic przeciwko. Widziałem również to zachowanie i zastanawiałem się, dlaczego tak się stanie. Wygląda i pachnie jak robak, ale czasami rzeczy są bardzo subtelne i zaskakujące jest to, że zarówno FF, jak i Chrome otrzymałyby * całkowicie * inne podstawowe silniki JavaScript. –

+0

Po prostu, aby było jasne, działa tak długo, dopóki błąd/wyjątek nie zostanie zgłoszony, ale jeśli "jakiś warunek" stanie się prawdziwy i zostanie zgłoszony wyjątek, funkcja nie powiedzie się przy następnym wywołaniu, ponieważ wzorzec jest kontynuowany od miejsca, w którym wyjątek został rzucony? To z pewnością brzmi jak błąd, który jest poza twoimi rękami. – PatrikAkerstrand

Odpowiedz

7

Obiekty RegExp, które są tworzone za pomocą regex literału, są buforowane, ale new RegExp zawsze tworzy nowy obiekt. Zbuforowane obiekty również zachowują swój stan, ale reguły rządzące tym aspektem najwyraźniej nie są zbyt jasne. Steve Levithan mówi o tym w this blog post (w dolnej części).

+0

Na blogu napisano, że zostanie naprawiony w Firefoksie 3.7 (i jestem na 3.6.3). Myślę, że po prostu przestanę używać literackiego RE, jako rozwiązania tego zachowania dla różnych przeglądarek. –

+0

Doskonale, dzięki. Zauważ, że "... są buforowane ..." powinno być "... * było * buforowane przez niektóre implementacje od ECMAScript 3rd edition ...", a następnie stwierdzenie, że nie mogą być już buforowane od najnowszej wersji specyfikacji (na szczęście!). –

+0

@Charles: Jeśli przestaniesz używać literałów, czeka Cię świat, w którym ucierpią zasady ucieczki. :-) Po prostu zresetuj 'lastIndex' przed użyciem (chyba, że ​​zaraz po inwentaryzacji także będziesz tłumaczyć innymi flagami). I ciesz się, że najnowszy spec naprawił tę małą głupotę. –

0

ja nie znam odpowiedzi, ale będę zgadywać:

Dosłowne wyrażenie, które jest wzór ma zasięg globalny i jest oceniany (do obiektu RegExp) tylko raz, natomiast jeśli używasz new Regexp jego argument jest nadal globalny, ale jest po prostu ciągiem znaków, a nie wyrażeniem RegExp.

+0

@Colin: Z wyjątkiem tego, że * nie * ma zasięg globalny, tak samo jak obiekt w 'var x = {};' ma zasięg globalny. Jest to również dosłowne, ale dostaniesz różne obiekty w każdym wywołaniu funkcji. –

1

Wychodzę tu na kończynę: myślę, że obserwowane zachowanie jest błędem w silnikach FF i Chrome Javascript (herezja!). Zaskakujące, że powinno się to zdarzyć w dwóch tak różnych silnikach. Wygląda na błąd optymalizacji. W szczególności, sekcja 7.8.5 od the spec mówi:

Wyrażenie regularne dosłownym jest element wejściowy, który jest przekształcany do obiektu RegExp (patrz 15.10) każdorazowo dosłownym jest uwzględniany.

Jedynym manewru co widzę to w zdaniu „..each czasie dosłowne jest uwzględniany” (moje podkreślenie). Ale ja nie rozumiem, dlaczego powstały obiekt powinien być magicznie zachowane dłużej niż jakikolwiek inny obiekt dosłowne, takie jak:

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

Tam kolejne wywołania func1 daje odrębne obiektów. Stąd moje powiedzenie, że wygląda mi na błąd.

Aktualizacja Alan Moore points toarticle by Steve Levithan w którym Levithan sprawia, że ​​twierdzenie, że wydanie specyfikacji ECMAScript 3-ty może mieć wolno tego rodzaju buforowania. Na szczęście nie jest to dozwolone na ECMAScript 5th edition (specyfik, z którego pracowałem) i dlatego będzie to bug Real Soon Now. Dzięki Alan!

Powiązane problemy