ponyfoo.com

Are Regular Expressions Stateful?

I seem to have stumbled across a bug regarding regular expressions using the g modifier, where they seem to preserve internal state across calls to RegExp.prototype.test. I’m looking for confirmation or a “you’re stupid, this is what is going on” dismissal.

Improve this article
Nicolás Bevacqua
| a minute read | 2

Apparently regular expressions with the g modifier are stateful. See for yourself.

<button id='bad'>Wait, WHAT?</button>
stateful = /button/ig;
bad.onclick = function (e) {
  alert(stateful.test(e.target.tagName));
};

The button alternates between alerting true and false in Google Chrome 38.0.2125.122, and also on Firefox 31.

As it turns out, this is the “expected” behavior. I did not expect that. Regular expressions with the g modifier on them will keep track of the lastIndex where a match occurred, even across calls to .test.

You can check out the live example on CodePen, too.

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments (2)

Jyrki Laurila wrote

Short answer to your question: yes, global modifier makes the RegEx instance “stateful”.

Here’s the reason: When you define a global modifier, the RegExp object keeps track of the lastIndex. Second time you run it and it doesn’t match anymore, it resets it back to 0. You can access it by wtf.lastIndex. This is a writable property that you can reset back to 0 manually.

The reason why you don’t see more people having problems with this is mostly because many people are misusing regexp literals by not caching the pattern. ie. always checking with /foo/.test(myString), which creates a NEW RegExp instance every time that is run.

You probably know this already, but just wanted to mention this to anyone not familiar with regular expressions: there’s two things wrong with doing: /^foo$/g.test(‘foo’)

  1. You’re expecting the expression to be very strict by demanding the matching string to start(^) and end($) with what you specified, which renders the global modifier redundant.

  2. The purpose of the test() -method is to see if this expression matches once or more, so again the global modifier is redundant.

Patrick Malouin wrote

As per RegExp.prototype.test() documentation:

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.

Counter-intuitive indeed, that’s why I usually use String.prototype.match() which doesn’t suffer the same problem.