本文探讨JavaScript中with语句的性能瓶颈及严格模式下的禁用问题,并提供基于正则替换的模拟实现方案。同时详细解析String原型上的split、match、search等正则相关方法的参数行为、捕获组影响及返回值差异,帮助开发者规避常见陷阱并提升代码质量。
with 语法及其替代方案
with 语法
在代码中,需要执行一个特定的函数逻辑。
function computeExpression(exp, scope) {
try {
with (scope) {
return eval(exp);
}
} catch (e) {
console.error('ERROR', e);
}
}
该函数要求在指定的 scope 作用域内执行,这具体意味着什么?
例如:
scope = {a:10,b:5};
exp = a*b;
假设要求计算结果为 15,在常规情况下,通常需要借助 with 语法来实现这一目标。
然而:
JavaScript 解释器在运行 with 块时,需要检查其中的变量是否属于 with 包含的对象。这一过程会显著降低 with 语句的执行速度,并导致 JavaScript 代码难以被引擎优化。
此外,在严格模式(strict mode)中,with 语法是被明确禁用的。
由于我使用 ES6 语法编写该库,且默认启用了严格模式,因此无法直接使用 with。为了解决这一问题,我自行模拟了一个与 with 等效的语法实现。
首先查看相关代码实现。
function replaceWith(scopeName, exp) {
exp = " " + exp.trim();
let quickRegex = /([\s\+\-\*\/%&\|\^!\*~]\s*?)([a-zA-Z_$][a-zA-Z_$0-9]*?)/g;
exp = exp.replace(quickRegex, (a, b, c) => {
return b + scopeName + '.' + c;
});
return exp;
}
代码首先去除表达式首尾的空格,为了与后续处理逻辑保持一致,在字符串开头显式添加一个空格。
接着使用正则表达式匹配类似 "t.e==0 ? f : d" 形式的表达式,从中提取变量名作为 c,将运算符符号提取为 b。
进行初步测试。

此时已能返回处理后的表达式,进一步改造代码,使表达式直接在内部执行。
function replaceWith(scope, exp) {
exp = " " + exp.trim();
let quickRegex = /([\s\+\-\*\/%&\|\^!\*~]\s*?)([a-zA-Z_$][a-zA-Z_$0-9]*?)/g;
exp = exp.replace(quickRegex, (a, b, c) => {
return b + 'scope.' + c;
});
let func = new Function("scope", "return " + exp);
return func(scope);
}
再次进行测试验证。

至此功能实现。
中间的正则表达式编写可能不够完善,存在其他不严密之处,欢迎读者补充指正。
String 原型上的正则方法
String 原型上的正则方法
许久未编写大量代码,看到 Sring.prototype.split(//); 这个正则表达式时,发现分割符号也被添加到了返回数组中,对此感到惊讶。随后查阅 MDN 文档学习,并在此做整理补充。
String.prototype.split(separator,[limit])
separator:指定用来分割字符串的字符(串)。separator 可以是字符串或正则表达式。如果忽略 separator,则返回包含整个字符串的数组。如果 separator 是一个空字符串,则 str 会被转换为由原字符串中每个字符组成的数组。
limit:一个整数,用于限定返回的分割片段数量。split 方法仍然会分割每一个匹配的 separator,但返回的数组只会截取最多 limit 个元素。
当找到 separator 时,separator 会从字符串中被移除,子字符串存入返回的数组中。如果忽略 separator 参数,返回的数组仅包含一个元素,即原字符串。若 separator 为空字符串,str 将被转换为由字符组成的数组。
注意:
Note: 当字符串为空时,split 返回一个包含一个空字符串的数组,而不是一个空数组。
如果 separator 是一个正则表达式,且包含捕获括号(capturing parentheses),则每次匹配到 separator 时,捕获括号匹配的结果将会插入到返回的数组中。
var myString = "hello world";
var splits = myString.split(();
console.log(splits);
输出示例:
["hello world"]
var myString = "Hello 1 word. Sentence number 2.";
var splits = myString.split(/(\d)/);
console.log(splits);
输出示例:
Hello ,1, word. Sentence number ,2,.
String.prototype.replace()
此方法在前文已有所涉及,此处不再赘述。
String.prototype.match(Regex)
当字符串匹配到正则表达式(regular expression)时,match() 方法会提取匹配项。
如果正则表达式没有 g 标志,返回结果与 RegExp.exec(str) 相同。此外,返回的数组拥有一个额外的 input 属性,包含原始字符串。另外,还拥有一个 index 属性,表示匹配结果在原字符串中的索引(从 0 开始)。
"1aef2af3ef4 5".match(/[a-z]*(\d)/)
输出示例:
["1", "1"]
如果正则表达式包含 g 标志,则该方法返回一个包含所有匹配结果的数组。如果没有匹配到,则返回 null。
注意,如果添加 g 标志,则分组信息将不再保留在返回数组中。
"1aef2af3ef4 5".match(/[a-z]*(\d)/g)
输出示例:
["1", "aef2", "af3", "ef4", "5"]
String.prototype.search(Regex)
search() 方法执行查找操作,检查字符串对象是否与一个正则表达式匹配。
如果匹配成功,则 search() 返回正则表达式在字符串中首次匹配项的索引。否则,返回 -1。
其行为类似于正则表达式的 test 方法。
"aeg56".search(/[a-z]\d+/)
输出示例:
2
总结
以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。