2010-04-15 8 views
6

tengo este pedazo de código:JavaScript expresiones regulares persiste literales entre llamadas a funciones

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

Esto funciona, a menos que se ejecute la sentencia throw. En ese caso, la próxima vez que llamo a la función, la llamada a exec() comienza donde se quedó, aunque le proporciono un nuevo valor de 'texto'.

puedo solucionarlo escribiendo

patrón var = new RegExp ('.....');

en su lugar, pero no entiendo por qué la primera versión está fallando. ¿Cómo persiste la expresión regular entre las llamadas a funciones? (Esto está ocurriendo en las últimas versiones de Firefox y Chrome.)

Editar caso de prueba completo:

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

La expresión regular continúa con 'cuatro' a partir de la segunda llamada en FF y Chrome, no en IE7 u Opera.

+1

Me he tomado la libertad de publicar un caso de prueba completo y simplificado, espero que no te importe. He visto este comportamiento también y me pregunté por qué sería. Se ve y huele como un error, pero a veces las cosas son muy sutiles, y es sorprendente que tanto FF como Chrome tengan sus * completamente * motores de JavaScript subyacentes. –

+0

Para que quede claro, funciona siempre que no se genere el error/excepción, pero si 'alguna condición' se convierte en verdadera y se lanza la excepción, entonces la función fallará en la siguiente invocación porque el patrón continúa desde donde el excepción fue arrojado? Eso seguramente suena como un error que está fuera de tus manos. – PatrikAkerstrand

Respuesta

7

Los objetos RegExp que se crean mediante un literal de expresión regular se almacenan en caché, pero new RegExp siempre crea un objeto nuevo. Los objetos en caché también guardan su estado, pero las reglas que rigen ese aspecto aparentemente no son muy claras. Steve Levithan habla de eso en this blog post (cerca de la parte inferior).

+0

El blog dice que se arreglará en Firefox 3.7 (y estoy en 3.6.3). Sin embargo, creo que dejaré de usar los literales de RE como una solución de navegador cruzada para este comportamiento. –

+0

Excelente, gracias. Tenga en cuenta que "... se almacenan en caché ..." deben ser "... * fueron * almacenados en caché por algunas implementaciones a partir de ECMAScript tercera edición ..." seguidas de la declaración de que ya no se pueden almacenar en caché a partir de la última especificación (¡agradecidamente!). –

+0

@Charles: si deja de usar literales, se encontrará con un mundo lastimado con reglas de escape. :-) Simplemente reinicie 'lastIndex' antes de usarlo (a menos que también lo arruine con otras banderas después de la creación de instancias). Y esté contento de que las últimas especificaciones hayan arreglado esta pequeña tontería. –

0

No sé la respuesta, pero voy a aventurar una respuesta:

La expresión literal que es el patrón tiene ámbito global y se evalúa (en un objeto RegExp) sólo una vez, mientras que si se utiliza new Regexp su argumento es aún global, pero es solo una cadena, no un RegExp.

+0

@Colin: Excepto que * no * tiene alcance global, del mismo modo que el objeto en 'var x = {};' tiene alcance global. Eso también es literal, pero obtendrás diferentes objetos en cada invocación de función. –

1

Voy a dar un paso aquí: creo que el comportamiento que está viendo es un error en los motores Javascript de FF y Chrome (¡herejía!). Aunque es sorprendente que ocurra en dos motores tan diferentes. Parece un error de optimización. Específicamente, la sección 7.8.5 de the spec dice:

Una expresión regular literal es un elemento de entrada que se convierte en un objeto RegExp (ver 15,10) cada vez que se evalúa el literal.

El único margen de maniobra que veo es en la frase "..each vez que el literal se evalúa" (el subrayado es mío). Pero no veo por qué el objeto resultante debe mantenerse mágicamente más que cualquier otro objeto literal, como por ejemplo:

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

Allí, las llamadas posteriores a func1 le dará distintos objetos. Por lo tanto, digo que parece un error para mí.

actualización Alan Moore points to un article by Steve Levithan en el que Levithan hace la afirmación de que la tercera especificación ECMAScript edición puede tener permitido que este tipo de almacenamiento en caché. Afortunadamente, no está permitido a partir de la 5ª edición de ECMAScript (la especificación de la que estaba trabajando) y, por lo tanto, va a ser un error Real Soon Now. Gracias Alan!

Cuestiones relacionadas