Important note about RegExp.prototype.test

Check the running example here!

I was doing some code refactoring the other day, and one of the usual refactors I always do (move code that could be declared once out of the loops) introduced a weird bug and it was related to RegExp.prototype.test.

I moved the regular expression literal out of the loop. And the code started to fail.

var regex = /\/\/\s*region/g;

var testCases = [
  '// region',
  '//   region',
  '//region'
];

var allPass = null; 
testCases.forEach(function (entry) {

  if (allPass === null) { // firstTime
    allPass = true;
  }

  var result = regex.test(entry);

  allPass = allPass && result;

  console.log(entry + ' ==> ' + result);
});

console.log('all true? ' + (allPass === true));

Well, allPass was false. And I started to think… this should be a bug in the browser, I tried a different one, same result. I tried in node, and it failed too.

Then I did, what I probably had to do since the beginning: Check the MDN documentation… 🙂

It says:

You can use test() whenever want to know whether a pattern is found in a string (similar to the String.search method); for more information (but slower execution) use the exec method (similar to the String.match method). As with exec (or in combination with it), test called multiple times on the same global regular expression instance will advance past the previous match.

So basically, calling test on the same RegExp instance alter the result, because somehow it expect you to call it on the same string instance… I guess to continue looking for matches… It was not what I was expecting…

So moving it back to the loop fixed the issue.

allPass = null;
testCases.forEach(function (entry) {
  if (allPass === null) { // firstTime
    allPass = true;
  }

  var regex = /\/\/\s*region/g; //create a new Instance

  var result = regex.test(entry);

  allPass = allPass && result;              
  console.log(entry + ' ==> ' + result);
});

console.log('what about now? all true? ' + (allPass === true) );

I could have used RegExp.prototype.match, but I had to find why RegExp.prototype.test was failing…

Update
Also probably the proper way to fix the weird behavior and still keep the variable regex outside the loop could be to remove the global modifier. I guess this is more appropriate for the case I was trying to fix since I don’t have to get all the matches…

var regex = /\/\/\s*region/; // no "g" at the end.

allPass = null;
testCases.forEach(function (entry) {
  if (allPass === null) { // firstTime
    allPass = true;
  }

  var result = regex.test(entry);

  allPass = allPass && result;              
  console.log(entry + ' ==> ' + result);
});

console.log('and now? all true? ' + (allPass === true) ); // <<=== true
Advertisements

One thought on “Important note about RegExp.prototype.test

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s