Better match results with String.prototype.matchAll()
Chrome 73 introduces the String.prototype.matchAll()
method. It behaves
similarly to match()
, but returns an iterator with all regular expression
matches in a global or sticky regular expression. This offers a simple way to
iterate over matches, especially when you need access to capture groups.
What's wrong with match()?
The short answer is nothing, unless you're trying to return global matches with capturing groups. Here's a programming puzzle for you. Consider the following code:
const regex = /t(e)(st(\d?))/g;
const string = 'test1test2';
const results = string.match(regex);
console.log(results);
// → ['test1', 'test2']
Run this in a console and notice that it returns an array containing the
strings 'test1'
and 'test2'
. If I remove the g flag from the regular
expression what I get has all of my capturing groups, but I only get the first
match. It looks like this:
['test1', 'e', 'st1', '2', index: 0, input: 'test1test2', groups: undefined]
This string contains a second possible match beginning with 'test2'
but I don't
have it. Now here's the puzzle: how do I get all of the capturing groups for
each match? The explainer for the String.prototype.matchAll()
proposal
shows two possible approaches. I won't describe them because hopefully you
won't need them much longer.
String.prototype.matchAll()
What would the explainer examples look like with matchAll()
? Have a look.
const regex = /t(e)(st(\d?))/g;
const string = 'test1test2';
const matches = string.matchAll(regex);
for (const match of matches) {
console.log(match);
}
There are a few things to note about this. Unlike match()
which returns an
array on a global search, matchAll()
returns an iterable object that works
beautifully with for...of
loops. The iterable object produces an array for
each match, including the capturing groups with a few extras. If you print
these to the console they'll look like this:
['test1', 'e', 'st1', '2', index: 0, input: 'test1test2', groups: undefined]
['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', groups: undefined]
You may notice that the value for each match is an array in exactly the same
format returned by match()
for non-global regular expressions.
Bonus material
This is mainly for people who are new to regular expressions or who aren't experts at it. You may have noticed the results of both match() and matchAll() (for each iteration) are arrays with some additional named properties. While preparing this article, I noticed that these properties have some documentation deficiencies on MDN (which I've fixed). Here's a quick description.
index
- The index of the first result in the original string. In the above example
test2
starts at position 5 henceindex
has the value 5. input
- The complete string that
matchAll()
was run against. In my example, that was'test1test2'
. groups
- Contains the results of any named capturing groups specified in your regular expression.
Conclusion
If I've missed anything please let me know in the comments below. You can read more about recent changes to JavaScript in previous updates or on the V8 website.