JavaScript regular expression objects are stateful when they have
/g
or/y
flag in the pattern to match.
When we create a regular expression that has /g
flag, it maintains the lastIndex
property which keeps track of the index where to start finding for the next match. So next time when we start testing using the same pattern, it starts searching from the index it found last match.
Consider, we have a regular expression like this
const pattern = /ab/g;
and you want to find if the pattern is present or not in any of the string passed, we can do like this
console.log(pattern.test('abcd')) // true
console.log(pattern.lastIndex) // 2
It returns true
because ab
is present in the string abcd
.
But as the lastIndex
property is maintained by the regular expression stored in variable pattern which is 0
initially and becomes 2
when it finds the match at 0th position in string abcd
, so when next time we call test
method, it starts from 2nd position to search for the match and so the match fails.
console.log(pattern.test('abcd')) // true
console.log(pattern.lastIndex) // 2
console.log(pattern.test('abcd')) // false
console.log(pattern.lastIndex) // 0
And as it’s not able to find string ab
inside abcd
starting from position 2
, it resets lastIndex
property to 0
so when we again call the method it returns true
console.log(pattern.test('abcd')) // true
console.log(pattern.lastIndex) // 2
console.log(pattern.test('abcd')) // false
console.log(pattern.lastIndex) // 0
console.log(pattern.test('abcd')) // true
This might not be the behavior you expected but this is how regular expression maintains the
lastIndex
property when either usingtest()
orexec()
method.
This behavior is sometimes useful in some scenarios.
Suppose you want to find position of all occurrences of vowels in a string, you can do something like this:
const source = "This is some text";
const pattern = /[aeiou]/g;
while((result = pattern.exec(source)) !== null) {
console.log("Character " + result[0] + " found at position " + (pattern.lastIndex - 1));
}
/* output:
Character i found at position 2
Character i found at position 5
Character o found at position 9
Character e found at position 11
Character e found at position 14
*/
But this may not be the behavior you always want.
So in that case, you can use the match()
method of regular expression instead of test()
or exec()
.
If you want to use test()
method only then you need to reset the lastIndex
to 0
after every search.
const pattern = /ab/g;
console.log(pattern.test('abcd')) // true
pattern.lastIndex = 0;
console.log(pattern.test('abcd')) // true
Don't forget to subscribe to get my weekly newsletter with amazing tips, tricks and articles directly in your inbox here.