RegExp: lastIndex
lastIndex
是 RegExp
实例的数据属性,它指定了开始下一次匹配的索引。
试一下
价值
非负整数。
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 语言规范 # sec-properties-of-regexp-instances |
浏览器兼容性
BCD 表格仅在启用 JavaScript 的浏览器中加载。