2010-11-17 8 views
2

Je viens de découvrir le bug le plus étrange dans un code javascript traitant de RegExp.test. J'ai eu une expression régulière regexp déclarée à l'intérieur d'une fonction et j'avais une fermeture qui avait une référence à regexp et j'utilisais la fermeture pour itérer sur un tableau de chaînes pour les tester à l'aide de collect de prototype.js, soitdifférence entre 'var a = 2' et 'this.a = 2'

function some_func() { 
    var regexp = /regular_expression/; 
    an_array_of_strings.collect(
    function(str) { 
     if (regexp.test(str)) { 
     do_something(); 
     } 
    } 
); 
} 

la chose vraiment bizarre était que l'appel regexp.test(str) à l'intérieur de la fermeture alternait entre true et false sur la même entrée. J'ai regardé la source pour RegExp.test et je n'ai rien vu de poisson mais il se passait quelque chose parce que la même chaîne peut passer et échouer la même expression régulière. Après avoir regardé un peu plus à RegExp.test j'ai fondamentalement conclu que les variables déclarées dans RegExp.test continuaient d'exister entre les invocations et gâchaient les invocations ultérieures. Alors, voici la question: Quelle est la différence entre

this.a = 2; 

et

var a = 2; 

lorsque les déclarations ci-dessus apparaissent à l'intérieur d'une méthode appelée sur un objet à l'intérieur d'une fermeture qui contient une référence à cet objet? Je demande parce que le bug disparaît quand je me déplace regexp.test en dehors de la fermeture. Lorsque regexp.test est appelé en dehors de la fermeture alors il ne bascule pas entre true et false à chaque appel. Je n'ai aucune idée de pourquoi cela se passe.

Modifier: Quand je bougeais la regexp en dehors de la fermeture, j'oubliais d'ajouter l'option globale de sorte que est la raison pour laquelle le bug a été en train de disparaître. Merci Ivo.

Répondre

3

Puisque vous n'avez pas montré votre RegExp mais que j'ai juste répondu quelque chose de similaire, je suppose que vous utilisez l'option global dans votre RegExp, qui a quelques effets secondaires intéressants.

Comme exec (ou en combinaison avec il), test appelé plusieurs fois sur la même expression régulière mondiale exemple avancera au-delà du match précédent.

Source: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/RegExp/test

Alors qu'est-ce qui se passe ici est exactement ce que puisque vous utilisez l'option globale pour l'expression rationnelle, il continuera à rechercher la chaîne après avoir trouvé une correspondance.

regexp.test("d") 

Ceci trouvera d à la position 0 .

regexp.test("d") 

Cela va maintenant chercher d à partir de la position 1 , mais puisque c'est la fin de la chaîne, il ne trouvera rien de revenir donc faux.

Nous pouvons utiliser la propriété lastIndex de la regex à la preuve que:

regexp.lastIndex 
>> 0 
regexp.test("d") 
>> true 
regexp.lastIndex 
>> 1 
regexp.test("d") 
>> false 

Donc, pour résoudre le problème, vous devez supprimer l'option global de votre RegExp.

Avertissement, ceci est une copie de ma réponse précédente:
Unusual javascript Regex result, explanation please!

+0

Quel est le point de cette réponse? Qu'est-ce que cela a à voir avec 'var' et' this'? – Gumbo

+2

Il n'y a pas 'var' et' this' dans sa question, il spécule sur les internes de 'RegExp.test' dont je suis sûr qu'il n'y a pas de' this.a' dedans, puisqu'il s'agit d'un code C natif. –

+0

Oh mec comment diable ai-je manqué ça. Tu as raison. C'est exactement ce qui se passe. Le problème n'est pas dû à la fermeture, mais à l'option globale. – davidk01

1

C'est une question délicate, car à première vue, ils semblent identiques.

Je suppose que this.a appartient à la fonction, mais peut également s'étendre pendant la durée de vie de cette fonction, et pas seulement l'appel de fonction lui-même; tandis que var a serait créé et détruit chaque fois que la fonction serait exécutée.

1

Jetez un oeil à this page sur les merveilles de this dans JS. La signification de this change assez radicalement, selon la façon dont vous utilisez vos fonctions.

+0

Ah, mais il ne devrait pas être difficile du tout lorsque la fonction est appelée comme méthode. Quand il est appelé comme une méthode comme dans 'regexp.test (str)' il ne devrait y avoir aucune ambiguïté à propos de 'this' car il fait référence à' regexp' – davidk01

+0

Je devinerais que votre fonction do_something() et votre fermeture appartiennent à la fenêtre . Vous pourriez inclure un code de débogage idiot similaire à console.debug (this) ou console.debug (this.getAttribute ('id')). Il pourrait au moins fournir des indices quant à ce que c'est, en ce qui concerne votre exécution js. – LaustN