The Hidden Secret of Regex in JavaScript That Drives You Crazy!

NicolasBrondinBernard

Author
@NicolasBrondinBernard

Warning, once you’ve read this article, you won’t come out of it unscathed!

Article published on 23/10/2023, last updated on 10/08/2026

If you play around with regular expressions in JavaScript, you may have run into this problem, which at first glance seems incomprehensible:

const regex = /hello/g;
const text = 'hello world';

regex.exec(text); // => [ 'hello', index: 0, input: 'hello world', groups: undefined ]
regex.exec(text); // => null

At first glance, this already seems weird…

And yet, this code, on the other hand, works perfectly:

const text = 'hello world';

/hello/g.exec(text); // => [ 'hello', index: 0, input: 'hello world', groups: undefined ]
/hello/g.exec(text); // => [ 'hello', index: 0, input: 'hello world', groups: undefined ]

Where does the problem come from?

The syntax of regular expressions in JavaScript makes us too often forget that, in reality, this code:

const regex = /hello/g;

is strictly equal to this one:

const regex = new RegExp(/hello/,'g');

Even when it is declared using the shorthand syntax, a regular expression is a complex object!

And the second specificity is that the behavior of this object is not the same depending on the "flags" (parameters) passed to it…

The "g" flag

Here, we use the "g" flag for "global," which means that the regular expression is supposed to detect all occurrences of the pattern defined in the Regex, instead of stopping at the first one.

Example:

const str = "hello hello hello world";

// Without flag
str.replace(/hello/, "bonjour"); // => "bonjour hello hello world"
// With flag
str.replace(/hello/g, "bonjour"); // => "bonjour bonjour bonjour world"

But one of the side effects of using the "g" parameter of a regular expression, when calling its .exec(…) method, is not to return all the detected groups, but to return the first detected element, and to keep the position of the last "match" in memory.

The attribute in question is called "lastIndex"

const regex = /hello/g;
const text = 'hello world';

console.log(regex.lastIndex); // => 0
regex.exec(text);
console.log(regex.lastIndex); // => 5
regex.exec(text);

The solution

If this behavior seems strange at first glance, it nonetheless makes it possible to handle global detections with a simple while loop, like this:

let str = 'hello hello hello world';
let regex = /hello/g;
let currentMatch = regex.exec(str);

while(currentMatch !== null) {
  console.log(currentMatch);
  currentMatch = regex.exec(string);
}

That is the one and only reason this famous "lastIndex" exists, which makes certain regular expressions in JavaScript "stateful."

A "hack"

If you don't want to use your RegEx in a loop, but simply want to use it on different strings, then you can simply reset this index to 0:

const regex = /hello/g;
const text1 = 'hello world';
const text2 = 'hello you';

const match1 = regex.exec(text1);
regex.lastIndex = 0;
const match2 = regex.exec(text2);
regex.lastIndex = 0;
// ...

Finished reading this article?
Our newsletter

No spam. Only free content, news, and ever more resources to level up your skills!

Join +1500 developers

Comments (0)

to leave a comment

No comments yet