RegExp: lastIndex

基线 广泛可用

此功能已得到充分确立,并在许多设备和浏览器版本中有效。它自以下时间起在所有浏览器中可用: 2015 年 7 月.

lastIndexRegExp 实例的数据属性,它指定了开始下一次匹配的索引。

试一下

价值

非负整数。

RegExp: lastIndex 的属性属性
可写
可枚举
可配置

描述

仅当正则表达式实例使用 g 标志表示全局搜索,或 y 标志表示粘性搜索时才会设置此属性。当对给定输入调用 exec() 时,将应用以下规则:

  • 如果 lastIndex 大于输入的长度,则 exec() 不会找到匹配项,并且 lastIndex 将被设置为 0。
  • 如果 lastIndex 等于或小于输入的长度,则 exec() 将尝试从 lastIndex 开始匹配输入。
    • 如果 exec() 找到匹配项,则 lastIndex 将被设置为输入中匹配字符串末尾的位置。
    • 如果 exec() 没有找到匹配项,则 lastIndex 将被设置为 0。

其他与正则表达式相关的函数,例如 RegExp.prototype.test()String.prototype.match()String.prototype.replace() 等,在内部调用 exec(),因此它们对 lastIndex 的影响不同。有关详细信息,请参见其各自的页面。

示例

使用 lastIndex

考虑以下语句序列

js
const re = /(hi)?/g;

匹配空字符串。

js
console.log(re.exec("hi"));
console.log(re.lastIndex);

返回 ["hi", "hi"],其中 lastIndex 等于 2。

js
console.log(re.exec("hi"));
console.log(re.lastIndex);

返回 ["", undefined],这是一个空数组,其第零个元素是匹配字符串。在本例中,由于 lastIndex 为 2(并且仍然为 2),而 hi 的长度为 2,因此为空字符串。

在粘性正则表达式中使用 lastIndex

lastIndex 属性是可写的。您可以设置它以使正则表达式从给定索引开始下一次搜索。

y 标志几乎总是需要设置 lastIndex。它始终在 lastIndex 处严格匹配,并且不尝试任何后续位置。这通常对编写解析器很有用,因为您希望仅在当前位置匹配标记。

js
const stringPattern = /"[^"]*"/y;
const input = `const message = "Hello world";`;

stringPattern.lastIndex = 6;
console.log(stringPattern.exec(input)); // null

stringPattern.lastIndex = 16;
console.log(stringPattern.exec(input)); // ['"Hello world"']

倒带 lastIndex

g 标志也受益于设置 lastIndex。一种常见的用例是当字符串在全局搜索的中间被修改时。在这种情况下,如果字符串被缩短,我们可能会错过特定的匹配项。我们可以通过倒带 lastIndex 来避免这种情况。

js
const mdLinkPattern = /\[[^[\]]+\]\((?<link>[^()\s]+)\)/dg;

function resolveMDLink(line) {
  let match;
  let modifiedLine = line;
  while ((match = mdLinkPattern.exec(modifiedLine))) {
    const originalLink = match.groups.link;
    const resolvedLink = originalLink.replaceAll(/^files|\/index\.md$/g, "");
    modifiedLine =
      modifiedLine.slice(0, match.indices.groups.link[0]) +
      resolvedLink +
      modifiedLine.slice(match.indices.groups.link[1]);
    // Rewind the pattern to the end of the resolved link
    mdLinkPattern.lastIndex += resolvedLink.length - originalLink.length;
  }
  return modifiedLine;
}

console.log(
  resolveMDLink(
    "[`lastIndex`](files/en-us/web/javascript/reference/global_objects/regexp/lastindex/index.md)",
  ),
); // [`lastIndex`](/en-us/web/javascript/reference/global_objects/regexp/lastindex)
console.log(
  resolveMDLink(
    "[`ServiceWorker`](files/en-us/web/api/serviceworker/index.md) and [`SharedWorker`](files/en-us/web/api/sharedworker/index.md)",
  ),
); // [`ServiceWorker`](/en-us/web/api/serviceworker) and [`SharedWorker`](/en-us/web/api/sharedworker)

尝试删除 mdLinkPattern.lastIndex += resolvedLink.length - originalLink.length 行并运行第二个示例。您会发现第二个链接没有正确替换,因为字符串缩短后,lastIndex 已经超过了链接的索引。

警告:此示例仅用于演示。要处理 Markdown,您可能应该使用解析库而不是正则表达式。

优化搜索

您可以通过将 lastIndex 设置为可以忽略先前可能出现的点来优化搜索。例如,而不是这样

js
const stringPattern = /"[^"]*"/g;
const input = `const message = "Hello " + "world";`;

// Pretend we've already dealt with the previous parts of the string
let offset = 26;
const remainingInput = input.slice(offset);
const nextString = stringPattern.exec(remainingInput);
console.log(nextString[0]); // "world"
offset += nextString.index + nextString.length;

考虑这样

js
stringPattern.lastIndex = offset;
const nextString = stringPattern.exec(remainingInput);
console.log(nextString[0]); // "world"
offset = stringPattern.lastIndex;

这在性能上可能更高,因为我们避免了字符串切片。

避免副作用

exec() 造成的副作用可能会让人困惑,尤其是在每次 exec() 的输入都不同时。

js
const re = /foo/g;
console.log(re.test("foo bar")); // true
console.log(re.test("foo baz")); // false, because lastIndex is non-zero

当您手动修改 lastIndex 时,这会更加令人困惑。要控制副作用,请记住在每个输入完全处理后重置 lastIndex

js
const re = /foo/g;
console.log(re.test("foo bar")); // true
re.lastIndex = 0;
console.log(re.test("foo baz")); // true

通过一些抽象,您可以要求在每次 exec() 调用之前将 lastIndex 设置为特定值。

js
function createMatcher(pattern) {
  // Create a copy, so that the original regex is never updated
  const regex = new RegExp(pattern, "g");
  return (input, offset) => {
    regex.lastIndex = offset;
    return regex.exec(input);
  };
}

const matchFoo = createMatcher(/foo/);
console.log(matchFoo("foo bar", 0)[0]); // "foo"
console.log(matchFoo("foo baz", 0)[0]); // "foo"

规范

规范
ECMAScript 语言规范
# sec-properties-of-regexp-instances

浏览器兼容性

BCD 表格仅在启用 JavaScript 的浏览器中加载。

另请参见