Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the 'g' flag change the result of a JavaScript regular expression? [duplicate]

Consider:

var reg = new RegExp("^19[-\\d]*","g");
reg.test('1973-02-01') // true
reg.test('1973-01-01') // false

Why does the third line return false? If I remove the "g" flag, then it returns true.

like image 492
Chopper Lee Avatar asked Sep 06 '15 08:09

Chopper Lee


1 Answers

In JavaScript, regular expression objects have state. This matters when the g flag ("global") is applied to them, and sometimes applies in odd ways. This state is the index where the match last occurred, which is the regex's .lastIndex property. When you call exec or test on the same regex object again, it picks up from where it left off.

What's happening in your example is for the second call, it's picking up where it left off the last time, and so it's looking starting after the 10th character in the string — and doesn't find a match there, because there's no text there at all (and even if there were, the ^ assertion wouldn't match).

We can see what's happening if we look at the lastIndex property:

var reg = new RegExp("^19[-\\d]*","g");
snippet.log("Before first test: " + reg.lastIndex);
snippet.log(reg.test('1973-02-01')); //return true
snippet.log("Before second test: " + reg.lastIndex);
snippet.log(reg.test('1973-01-01')); //return false
snippet.log("After second test: " + reg.lastIndex);
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Without the g flag, the regex object doesn't keep any state and starts from the beginning of the string each time:

var reg = new RegExp("^19[-\\d]*");
snippet.log("Before first test: " + reg.lastIndex);
snippet.log(reg.test('1973-02-01')); //return true
snippet.log("Before second test: " + reg.lastIndex);
snippet.log(reg.test('1973-01-01')); //return false
snippet.log("After second test: " + reg.lastIndex);
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Side note: In general, it's best to write regular expressions in JvaScript using regex literals rather than using the RegExp constructor and strings. In your case, that would be

var reg = /^19[-\d]*/g;
// or without the g flag:
var reg = /^19[-\d]*/;

Side note 2: It doesn't make much sense to define a regular expression with a ^ or $ anchor and the g flag unless you're also using the m (multiline) flag to change what those anchors mean. without m, they mean "beginning (^) or end ($) of input." With the m flag, they mean "beginning (^) or end ($) of line."

like image 190
T.J. Crowder Avatar answered Sep 20 '22 12:09

T.J. Crowder