正则表达式:lastIndex

RegExp 实例的 lastIndex 数据属性指定开始下一个匹配的索引。

¥The lastIndex data property of a RegExp instance specifies the index at which to start the next match.

Try it

¥Value

非负整数。

¥A non-negative integer.

Property attributes of 正则表达式:lastIndex
Writable yes
Enumerable no
Configurable no

描述

¥Description

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

¥This property is set only if the regular expression instance used the g flag to indicate a global search, or the y flag to indicate a sticky search. The following rules apply when exec() is called on a given input:

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

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

¥Other regex-related methods, such as RegExp.prototype.test(), String.prototype.match(), String.prototype.replace(), etc., call exec() under the hood, so they have different effects on lastIndex. See their respective pages for details.

示例

¥Examples

使用最后一个索引

¥Using lastIndex

考虑以下语句序列:

¥Consider the following sequence of statements:

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

匹配空字符串。

¥Matches the empty string.

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

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

¥Returns ["hi", "hi"] with lastIndex equal to 2.

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

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

¥Returns ["", undefined], an empty array whose zeroth element is the match string. In this case, the empty string because lastIndex was 2 (and still is 2) and hi has length 2.

将 lastIndex 与粘性正则表达式一起使用

¥Using lastIndex with sticky regexes

lastIndex 属性是可写的。你可以将其设置为使正则表达式在给定索引处开始下一次搜索。

¥The lastIndex property is writable. You can set it to make the regex start its next search at a given index.

y 标志几乎总是需要设置 lastIndex。它始终严格匹配 lastIndex 并且不会尝试任何后面的位置。当你只想在当前位置匹配标记时,这通常对于编写解析器很有用。

¥The y flag almost always requires setting lastIndex. It always matches strictly at lastIndex and does not attempt any later positions. This is usually useful for writing parsers, when you want to match tokens only at the current position.

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"']

倒回最后一个索引

¥Rewinding lastIndex

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

¥The g flag also benefits from setting lastIndex. One common use case is when the string is modified in the middle of a global search. In this case, we may miss a particular match if the string is shortened. We can avoid this by rewinding 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 已经超过了链接的索引。

¥Try deleting the mdLinkPattern.lastIndex += resolvedLink.length - originalLink.length line and running the second example. You will find that the second link is not replaced correctly, because the lastIndex is already past the link's index after the string is shortened.

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

¥Warning: This example is for demonstration only. To deal with Markdown, you should probably use a parsing library instead of regex.

优化搜索

¥Optimizing searching

你可以通过将 lastIndex 设置为可以忽略先前可能出现的情况来优化搜索。例如,代替这个:

¥You can optimize searching by setting lastIndex to a point where previous possible occurrences can be ignored. For example, instead of this:

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;

考虑一下:

¥Consider this:

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

这可能会提高性能,因为我们避免了字符串切片。

¥This is potentially more performant because we avoid string slicing.

避免副作用

¥Avoiding side effects

exec() 引起的副作用可能会令人困惑,特别是如果每个 exec() 的输入都不同。

¥The side effects caused by exec() can be confusing, especially if the input is different for each 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

¥This is even more confusing when you are hand-modifying lastIndex. To contain the side effects, remember to reset lastIndex after each input is completely processed.

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

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

¥With some abstraction, you can require lastIndex to be set to a particular value before each exec() call.

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"

规范

Specification
ECMAScript Language Specification
# sec-properties-of-regexp-instances

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看