2010-04-15 10 views
6

ich dieses Stück Code haben:JavaScript regulären Ausdruck wörtliche verharrt zwischen Funktionsaufrufe

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

Dies funktioniert, es sei denn, die throw-Anweisung ausgeführt wird. In diesem Fall beginnt der Aufruf exec() beim nächsten Aufruf der Funktion dort, wo er aufgehört hat, obwohl ich ihn mit einem neuen Wert von 'text' versehe.

Ich kann das Problem beheben, indem er

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

stattdessen, aber ich verstehe nicht, warum die erste Version fehlschlägt. Wie bleibt der reguläre Ausdruck zwischen Funktionsaufrufen? (Dies ist in den neuesten Versionen von Firefox und Chrome geschieht.)

bearbeiten komplette Testfall:

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

Der reguläre Ausdruck setzt sich mit ‚vier‘ ab dem zweiten Anruf auf FF und Chrome, nicht auf IE7 oder Opera.

+1

Ich habe mir die Freiheit genommen, einen vollständigen, vereinfachten Testfall zu veröffentlichen, ich hoffe, es macht Ihnen nichts aus. Ich habe auch dieses Verhalten gesehen und habe mich gefragt, warum es so wäre. Es sieht aus und riecht wie ein Käfer, aber manchmal sind die Dinge sehr subtil und es ist überraschend, dass sowohl FF als auch Chrome ihre * komplett * verschiedenen zugrundeliegenden Javascript-Engines bekommen haben. –

+0

Nur um klar zu sein, es funktioniert, solange der Fehler/Ausnahme nicht ausgelöst wird, aber wenn 'eine Bedingung' wahr wird und die Ausnahme ausgelöst wird, wird die Funktion beim nächsten Aufruf fehlschlagen, weil das Muster von wo fortgesetzt wird Ausnahme wurde geworfen? Das klingt sicher wie ein Fehler, der nicht in deinen Händen liegt. – PatrikAkerstrand

Antwort

7

RegExp-Objekte, die mit einem Regex-Literal erstellt werden, werden zwischengespeichert, aber new RegExp erstellt immer ein neues Objekt. Die zwischengespeicherten Objekte speichern auch ihren Zustand, aber die Regeln, die diesen Aspekt steuern, sind offensichtlich nicht sehr klar. Steve Levithan spricht darüber in this blog post (in der Nähe der Unterseite).

+0

Der Blog sagt, es wird in Firefox 3.7 behoben (und ich bin auf 3.6.3). Ich denke, ich werde RE-Literale einfach nicht mehr verwenden, als eine Browser-übergreifende Lösung für dieses Verhalten. –

+0

Ausgezeichnet, danke. Beachten Sie, dass "... cached ..." sollte "... * wurden von einigen Implementierungen als von ECMAScript 3rd edition ..." zwischengespeichert "gefolgt von der Aussage, dass sie nicht mehr im Cache der neuesten Spezifikation gespeichert werden (Gott sei Dank!). –

+0

@Charles: Wenn Sie aufhören, Literale zu verwenden, sind Sie in einer Welt der Verletzung mit fliehenden Regeln. :-) Setze einfach 'lastIndex' vor der Benutzung zurück (es sei denn du machst auch nach der Instantiierung mit anderen Flags). Und sei froh, dass die neueste Spezifikation diese kleine Dummheit behoben hat. –

0

Ich weiß nicht die Antwort, aber ich werde ahne:

Die wörtliche Ausdruck, ist das Muster globale Reichweite hat und ausgewertet wird (in ein RegExp-Objekt) nur einmal, während, wenn Sie verwenden new Regexp sein Argument ist immer noch global, aber ist nur eine Zeichenfolge, keine RegExp.

+0

@Colin: Außer es * hat * keinen globalen Gültigkeitsbereich, mehr als das Objekt in 'var x = {};' hat globalen Geltungsbereich. Das ist auch ein Literal, aber Sie erhalten bei jedem Funktionsaufruf andere Objekte. –

1

Ich werde hier auf ein Bein gehen: Ich denke, das Verhalten, das Sie sehen, ist ein Fehler in FFs und Chrome's Javascript-Engines (Häresie!). Überraschend, dass es in zwei so unterschiedlichen Motoren passieren sollte. Sieht wie ein Optimierungsfehler aus. Insbesondere Abschnitt 7.8.5 von the spec sagt:

Ein regulärer Ausdruck literal ist ein Eingabeelement, das auf ein Objekt RegExp umgewandelt wird (siehe 15.10) jedes Mal der wörtlichen ausgewertet wird.

Der einzige Wiggleraum ich sehe, ist in der Phrase „..each Zeit die wörtliche ausgewertet“ (Hervorhebung von mir). Aber ich sehe nicht, warum sollte das resultierende Objekt auf magische Weise mehr als jedes andere Objektliteral beibehalten werden, wie zum Beispiel:

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

Dort nachfolgende Aufrufe func1 geben Ihnen verschiedene Objekte. Daher mein Sprichwort, es sieht für mich wie ein Käfer aus.

aktualisieren Alan Moore points to ein article by Steve Levithan, in dem die Levithan Anspruch macht, dass die ECMAScript 3. Auflage Spezifikation diese Art von Caching erlaubt haben. Glücklicherweise ist es ab der ECMAScript 5th Edition (der Spezifikation, von der ich gearbeitet habe) nicht erlaubt und wird daher ein Bug Real Soon Now sein. Danke Alan!

Verwandte Themen