22

A simple test script:

<script type="text/javascript">
    var reg = new RegExp('#([a-f0-9]{3})$', 'gi');
    for (var i = 0; i < 10; i++) {
        console.log(reg.exec('#fff'));
    }
</script>

Console output:

["#fff", "fff"]
null
["#fff", "fff"]
null
["#fff", "fff"]
null
["#fff", "fff"]
null
["#fff", "fff"]
null

Why is every other result null when the input remains constant?

T. Stone
  • 19,209
  • 15
  • 69
  • 97
  • $ anchors the pattern to the end of the line. Without that, something like `#fff blah blah blah` would also match. Of course, at it stands now, the regex will match `blah blah blah #fff`, since it's not anchored to the start of the line with a `^` – Marc B Feb 09 '11 at 21:13

4 Answers4

53

When you use the global flag, the regex becomes "sticky." That is to say, it uses a counter variable to track where the last match was found. Rather than matching from the beginning each time, a sticky regex will actually pick up where the last match ended. This counter will only reset back to 0 (the beginning) if the entire match fails (which is why it works every-other-time)

In your case, my suggestion would be to drop the g flag.

For more information: RegExp @ MDC

Matt
  • 43,482
  • 6
  • 101
  • 102
9

Matt has answered why. I just wanted to add that you can see this by checking the value of lastIndex on the RegExp object before and after the exec call

var reg = new RegExp('#([a-f0-9]{3})$', 'gi');
for (var i = 0; i < 10; i++) {
    console.log(reg.lastIndex);
    console.log(reg.exec('#fff'));
    console.log(reg.lastIndex);
}

output is

0
["#fff", "fff"]
4

4
null
0

0
["#fff", "fff"]
4

4
null
0

0
["#fff", "fff"]
4

4
null
0

0
["#fff", "fff"]
4

4
null
0

0
["#fff", "fff"]
4

4
null
0

undefined

demonstrating that on every other iteration of the loop, the regex matching is starting from where the lastIndex finished. You could set lastIndex to 0 before each exec call or drop the flag altogether.

Community
  • 1
  • 1
Russ Cam
  • 124,184
  • 33
  • 204
  • 266
3

Your problem is the "gi" in the second argument, I just removed it and tested on the Chrome console:

var reg = new RegExp('#([a-f0-9]{3})$');
JCOC611
  • 19,111
  • 14
  • 69
  • 90
1

I found this on the web...

Calling the exec() function also changes the lastIndex property of the RegExp object.
It stores the index in the subject string at which the next match attempt will begin.
You can modify this value to change the starting position of the next call to exec().

The behavior you describe indicates that the RegExp object has a state that is modified by calling the exec() method.

I think you need to modify the lastindex property before you re-run the exec method.
read more here: Javascript Regexp Object

funkymushroom
  • 2,079
  • 2
  • 26
  • 39