lastIndex属性

exec方法同样没提供方便的方法来指定字符串中的起始匹配位置。但我们可以使用一种比较麻烦的方法来实现该功能。

正则表达式对象包含了一些属性。其中一个属性是source,该属性包含用于创建正则表达式的字符串。另一个属性是lastIndex,可以在极少数情况下控制下一次匹配的起始位置。

所谓的极少数情况,指的是当正则表达式启用了全局(g)或者粘性(y),并且使用exec匹配模式的时候。此外,另一个解决方案应该是向exec传递的额外参数,但 JavaScript 的正则表达式接口能设计得如此合理才是怪事。

  1. let pattern = /y/g;
  2. pattern.lastIndex = 3;
  3. let match = pattern.exec("xyzzy");
  4. console.log(match.index);
  5. // → 4
  6. console.log(pattern.lastIndex);
  7. // → 5

如果成功匹配模式,exec调用会自动更新lastIndex属性,来指向匹配字符串后的位置。如果无法匹配,会将lastIndex清零(就像新构建的正则表达式对象lastIndex属性为零一样)。

全局和粘性选项之间的区别在于,启用粘性时,仅当匹配直接从lastIndex开始时,搜索才会成功,而全局搜索中,它会搜索匹配可能起始的所有位置。

  1. let global = /abc/g;
  2. console.log(global.exec("xyz abc"));
  3. // → ["abc"]
  4. let sticky = /abc/y;
  5. console.log(sticky.exec("xyz abc"));
  6. // → null

对多个exec调用使用共享的正则表达式值时,这些lastIndex属性的自动更新可能会导致问题。 你的正则表达式可能意外地在之前的调用留下的索引处开始。

  1. let digit = /\d/g;
  2. console.log(digit.exec("here it is: 1"));
  3. // → ["1"]
  4. console.log(digit.exec("and now: 1"));
  5. // → null

全局选项还有一个值得深思的效果,它会改变match匹配字符串的工作方式。如果调用match时使用了全局表达式,不像exec返回的数组,match会找出所有匹配模式的字符串,并返回一个包含所有匹配字符串的数组。

  1. console.log("Banana".match(/an/g));
  2. // → ["an", "an"]

因此使用全局正则表达式时需要倍加小心。只有以下几种情况中,你确实需要全局表达式即调用replace方法时,或是需要显示使用lastIndex时。这也基本是全局表达式唯一的应用场景了。