试一试
const regex = /foo/g;
const str = "table football, foosball";
regex.test(str);
console.log(regex.lastIndex);
// Expected output: 9
regex.test(str);
console.log(regex.lastIndex);
// Expected output: 19
值
一个非负整数。
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
考虑以下语句序列:
const re = /(hi)?/g;
匹配空字符串。
console.log(re.exec("hi"));
console.log(re.lastIndex);
返回 ["hi", "hi"],此时 lastIndex 等于 2。
console.log(re.exec("hi"));
console.log(re.lastIndex);
返回 ["", undefined],一个空数组,其第零个元素是匹配的字符串。在这种情况下,空字符串是因为 lastIndex 是 2(并且仍然是 2),而 hi 的长度是 2。
将 lastIndex 与粘性正则表达式一起使用
lastIndex 属性是可写的。你可以设置它来让正则表达式从指定的索引开始下一次搜索。
y 标志几乎总是需要设置 lastIndex。它总是严格地在 lastIndex 处匹配,并且不会尝试后面的位置。这对于编写解析器非常有用,当你只想在当前位置匹配标记时。
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 来避免这种情况。
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 设置到一个可以忽略先前可能出现的匹配的位置来优化搜索。例如,而不是这样做:
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;
考虑这个:
stringPattern.lastIndex = offset;
const nextString = stringPattern.exec(remainingInput);
console.log(nextString[0]); // "world"
offset = stringPattern.lastIndex;
这可能更具性能优势,因为我们避免了字符串切片。
避免副作用
exec() 引起的副作用可能令人困惑,特别是当每次 exec() 的输入都不同时。
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。
const re = /foo/g;
console.log(re.test("foo bar")); // true
re.lastIndex = 0;
console.log(re.test("foo baz")); // true
通过一些抽象,你可以要求在每次 exec() 调用之前将 lastIndex 设置为特定值。
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® 2026 语言规范 # sec-properties-of-regexp-instances |
浏览器兼容性
加载中…