Global regexes & .test()
The Problem
The more I've used regular expressions in JavaScript, the more I've tended to use the global flag, since this is typically the behavior I want (for example, when doing str.replace(regex)
or regex.exec(str)
).
Recently, I created a regex to determine if a SQL statement manipulates the DB (insert/update/delete) or if it's a simple query (select):
let is_change_regex = /^INSERT|^UPDATE|^DELETE/gi;
Notice that I included a g
flag at the end to make it global
out of habit.
However, when I ran is_change_regex.test(sql)
against several DELETE
statements, every other one returned true
. They are all DELETE
statements, so I had expected all of them to return true
.
Understanding Why
As usual, MDN gives a clear explanation of what's going on in the RegExp.test()
docs:
When a regex has the global flag set,
test()
will advance thelastIndex
of the regex. Further calls totest(str)
will resume searchingstr
starting fromlastIndex
. ThelastIndex
property will continue to increase each timetest()
returnstrue
.
Then it highlights the gotcha with a Note:
Note: As long as
test()
returnstrue
,lastIndex
will not reset — even when testing a different string!
This mostly makes sense when thinking about it from an implementation standpoint. The .lastIndex
property is how a global
regex can maintain state when replacing something multiple times throughout a string, or when .exec()
is called multiple times against the same string. It makes sense that using a global
regex with .test()
would allow the caller to test()
the string multiple times, walking through the string to see how many regex matches there are in it.
But it's counter-intuitive that it doesn't reset the .lastIndex
when testing a different string. Perhaps the ECMAScript Committee felt like that was too much magic, or may not be performant.
TL/DR
- Unless you're intentionally calling
.test()
multiple times with the same string to see how many matches are in the string, you probably don't want to use.test()
with aglobal
regex. It's best to use a regular regex, to avoid unintuitive behavior. - It's probably better to only use
global
regexes when you know you need them (typically forstr.replace(regex)
andregex.exec(str)
), and in other cases to default to regular, non-global regexes.