0

I don't understand the results from the following bit of JavaScript:

const reg = new RegExp(/@test1|@test2|@test3|@test4|@test5|@test6/g)

reg.test('normal text'); // false
reg.test('@test1');      // true
reg.test('@test2');      // false
reg.test('@test3');      // true
reg.test('@test4');      // false
reg.test('@test5');      // true
reg.test('@test6');      // false

The really weird thing is that it doesn't matter what order you call test(b) through test(g) the result is always the same... WHY???

t3dodson
  • 3,949
  • 2
  • 29
  • 40
RichardForrester
  • 1,048
  • 11
  • 19
  • 2
    That's because of the `g` flag, `lastIndex` is updated to advance search. Remove `g` flag, it is not necessary when using `test()` – Tushar Jan 07 '16 at 03:39

1 Answers1

3

From MDN Docs for RegExp.lastIndex

The lastIndex is a read/write integer property of regular expressions that specifies the index at which to start the next match.

Description:

This property is set only if the regular expression used the "g" flag to indicate a global search. The following rules apply:

  • If lastIndex is greater than the length of the string, test() and exec() fail, then lastIndex is set to 0.
  • If lastIndex is equal to the length of the string and if the regular expression matches the empty string, then the regular expression matches input starting at lastIndex.
  • If lastIndex is equal to the length of the string and if the regular expression does not match the empty string, then the regular expression mismatches input, and lastIndex is reset to 0.
  • Otherwise, lastIndex is set to the next position following the most recent match.

When first test is executed, the lastIndex is set to the end of the string. Next time, when the regex is used, it'll start search from the end of string (Rule #2) and it'll fail, so test() will return false. After that, as from the above statement(Rule #1), lastIndex is set to 0 and next test() will return true.

var reg = new RegExp(/@test1|@test2|@test3|@test4|@test5|@test6/g);

console.log(reg.test('normal text'));
console.log(reg.lastIndex);

console.log(reg.test('@test1'));
console.log(reg.lastIndex);

console.log(reg.test('@test2'));
console.log(reg.lastIndex);

console.log(reg.test('@test3'));
console.log(reg.lastIndex);

The simple rule is when using RegExp#test don't use g identifier/flag. as test just checks the existence of pattern, you don't need g.

const reg = new RegExp(/@test1|@test2|@test3|@test4|@test5|@test6/);

console.log(reg.test('normal text'));
console.log(reg.lastIndex);

console.log(reg.test('@test1'));
console.log(reg.lastIndex);

console.log(reg.test('@test2'));
console.log(reg.lastIndex);

If you still want to use g flag:(don't know why), the lastIndex property can be set to 0 to start match from the start of the string.

Be careful, when using this inside loop.

var reg = new RegExp(/@test1|@test2|@test3|@test4|@test5|@test6/g);

console.log(reg.test('normal text'));
reg.lastIndex = 0;

console.log(reg.test('@test1'));
reg.lastIndex = 0;

console.log(reg.test('@test2'));
reg.lastIndex = 0;

console.log(reg.test('@test3'));
reg.lastIndex = 0;

Here's the same regex, written by taking out common part and using OR condition

/@test(1|2|3|4|5|6)/

OR

/@test[1-6]/

As, RegExp constructor accepts string, the backslashes need to be double-escaped. I'll recommend to use regex literal form.

Tushar
  • 85,780
  • 21
  • 159
  • 179
  • @Tushar Very excellent answer, thank you. – RichardForrester Jan 07 '16 at 06:42
  • I personally do not think such questions should be answered, they must be closed as dupes. Most "I-do-not-understand-this-method-behavior" questions that just require a bit of official documentation reading are described on SO well. This one has been rather frequent. For me, it has the same status as the questions about how to parse HTML with regex. – Wiktor Stribiżew Jan 07 '16 at 08:24