根据我的研究,正则表达式 replace
不是异步的。所以我有点困惑为什么在 do-while
循环中匹配和替换有时会匹配失败。
我在下面创建了一个片段,可以选择“双重检查”该值是否包含任何匹配项,但我不知道为什么双重检查实际上会阻止它忽略匹配项。
您将在控制台中看到,当 injectableRegex.exec()
在 do-while
循环中运行两次时,它会正确替换所有匹配项。
更新:即使这种方法也不一致,因为我发现有时 doubleCheck 部分正确匹配,然后随后的 exec
调用失败
const getInjectedPhrase = (phrase, injections, doubleCheck) => {
let value = phrase;
// only need to attempt to replace the injectables in the phrase is we've been passed injections
if (injections && phrase.length > 1) {
const injectableRegex = /{{\s?([\w-]+)\s?}}/g; // find any matching injectable, and extract its key
let match;
window.console.log('initial phrase:', phrase);
// check if dictionary value contains injectable sections ie. sections surrounded by {{ }}
do {
// WHY IS THIS A THING!?
if (doubleCheck) {
injectableRegex.exec(value)
}
match = injectableRegex.exec(value);
if (match) {
/*
match[0] -> {{ x }}
match[1] -> x
*/
const injectionValue = injections[match[1]];
const injectionValueType = typeof injectionValue;
if (
injectionValueType === "string" ||
injectionValueType === "number"
) {
// globally replace the value with the injection's value
value = value.replace(new RegExp(match[0], "g"), `${injectionValue}`);
window.console.log('partial replace phrase:', value);
}
}
} while (match !== null);
}
window.console.log('returned phrase:', value);
return value;
};
window.console.log('WITHOUT DOUBLE CHECKING');
getInjectedPhrase(
"foo {{partialCount}} of {{count}} bars", {
partialCount: 3,
count: 4
},
false
);
window.console.log('USING DOUBLE CHECKING');
getInjectedPhrase(
"foo {{partialCount}} of {{count}} bars", {
partialCount: 3,
count: 4
},
true
);
最佳答案
问题是正则表达式保留了一个 lastIndex
属性,它跟踪最后一个匹配项的结束索引。在
foo {{partialCount}} of {{count}} bars
匹配
{{partialCount}}
导致 lastIndex
属性随后被设置为 20
- 第二个 之后的位置。
然后,当您重新分配字符串到
foo 3 of {{count}} bars
使用相同的正则表达式尝试匹配将从该字符串的索引 20 开始,过去 {{count}}
部分,所以匹配失败。
一种选择是每次手动将 lastIndex
重置为 0:
const getInjectedPhrase = (phrase, injections, doubleCheck) => {
let value = phrase;
// only need to attempt to replace the injectables in the phrase is we've been passed injections
if (injections && phrase.length > 1) {
const injectableRegex = /{{\s?([\w-]+)\s?}}/g; // find any matching injectable, and extract its key
let match;
window.console.log('initial phrase:', phrase);
// check if dictionary value contains injectable sections ie. sections surrounded by {{ }}
do {
injectableRegex.lastIndex = 0;
match = injectableRegex.exec(value);
if (match) {
/*
match[0] -> {{ x }}
match[1] -> x
*/
const injectionValue = injections[match[1]];
const injectionValueType = typeof injectionValue;
if (
injectionValueType === "string" ||
injectionValueType === "number"
) {
// globally replace the value with the injection's value
value = value.replace(new RegExp(match[0], "g"), `${injectionValue}`);
window.console.log('partial replace phrase:', value);
}
}
} while (match !== null);
}
window.console.log('returned phrase:', value);
return value;
};
window.console.log('WITHOUT DOUBLE CHECKING');
getInjectedPhrase(
"foo {{partialCount}} of {{count}} bars", {
partialCount: 3,
count: 4
},
false
);
更好的选择是替换
一次全部,使用回调函数,无需手动迭代、替换和重置正则表达式对象:
const getInjectedPhrase = (str, obj) => str.replace(
/{{\s?([\w-]+)\s?}}/g,
(_, key) => obj[key]
);
console.log(
getInjectedPhrase(
"foo {{partialCount}} of {{count}} bars", {
partialCount: 3,
count: 4
},
)
);
关于javascript - 即使有匹配项,while 循环中的 Regex exec 也会返回 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53753754/