Incorrect regex matching

Replay scenario (it is recommended to open the console to try) :

let reg = /J/gi

for (let i = 0; i <= 10; i++) {
  console.log(reg.test('Jioho'))}// Alternate true and false
Copy the code

Do you think it’s a circulating pot? Let’s do an acyclic scenario

let reg = /i/gi
console.log(reg.test('Jioho')) // true
console.log(reg.test('Jioho')) // false
console.log(reg.test('Jioho')) // true
console.log(reg.test('Jioho')) // false
console.log(reg.test('Jioho')) // true
Copy the code

Why is the second match of the re invalid

Because of g in the regular expression, the problem of loop matching

The lastIndex of the regular expression is set to the index position of the last character of the matching string, and the index position of the last character of the matching string is set to 1. The second lookup will start at lastIndex, and so on. If not, lastIndex is reset to 0. Note that the lastIndex attribute is only useful in regular expressions with global flags

So in the example above, the reG is only declared once, but it is used multiple times to make that happen

If the code were changed to this, the output would be as expected:

// 1
let reg = /j/i
console.log(reg.test('Jioho'))
console.log(reg.test('Jioho'))
console.log(reg.test('Jioho'))

// 2. Re-create the re in the loop
for (let i = 0; i <= 10; i++) {
  const reg = /j/gi
  console.log(reg.test('Jioho'))}Copy the code

Learn more about regular Regex

This is explained in detail in the MDN documentation: RegExp

See today’s hero: RegExp. LastIndex

LastIndex is a readable and writable integer property of the regular expression that specifies the starting index for the next match.

Note that it’s readable and writable. And check out the detailed introduction rules:

This property only works if the regular expression uses the “G” flag to indicate global retrieval. Apply the following rules:

  • If lastIndex is greater than the length of the string, regexp.test and regexp.exec will fail to match, and lastIndex will be set to 0.
  • If lastIndex is equal to the length of the string, and the regular expression matches an empty string, then the regular expression matches the string starting with lastIndex. Then the regular expression matches input starting at lastIndex.
  • If lastIndex is equal to the length of the string and the regular expression does not match the empty string, then the regular expression does not match the string and lastIndex is set to 0.
  • Otherwise, lastIndex is set to the next position immediately following the last successful match.

The documentation also describes a regexp.prototype.compile (re) regular expression compilation during a script run by regexp.prototype.compile ().

So we can recompile regular expressions in loops or under specific business logic

The last

The possible cause of the problem of the re multiple matches being invalid is found because the g loop match flag is used, and the re is not recompiled after being used once, but there are already related matches, and lastInedx is not 0

There are many ways to solve this problem

  • Redeclare the regular expression each time
for (let i = 0; i <= 10; i++) {
  const reg = /j/gi
  console.log(reg.test('Jioho'))}Copy the code
  • Reset lastIndex
const reg = /j/gi
for (let i = 0; i <= 10; i++) {
  reg.lastIndex = 0
  console.log(reg.test('Jioho'))}Copy the code
  • Recompile the re
const reg = /j/gi
for (let i = 0; i <= 10; i++) {
  reg.compile()
  console.log(reg.test('Jioho'))}Copy the code