JavaScript重新编译函数中regex文本的频率

How often does JavaScript recompile regex literals in functions?

本文关键字:regex 文本 频率 函数 新编译 编译 JavaScript      更新时间:2023-09-26

给定此函数:

function doThing(values,things){
  var thatRegex = /^http:'/'//i; // is this created once or on every execution?
  if (values.match(thatRegex)) return values;
  return things;
}

JavaScript引擎必须多久创建一次正则表达式?每次执行一次还是每次页面加载/脚本解析一次?

为了避免不必要的回答或评论,我个人赞成将regex放在函数之外,而不是放在函数内部。问题是关于语言的行为,因为我不确定在哪里查找,或者这是否是引擎问题。


编辑:

有人提醒我,我没有提到这将被循环使用。抱歉:

var newList = [];
foreach(item1 in ListOfItems1){ 
  foreach(item2 in ListOfItems2){ 
    newList.push(doThing(item1, item2));
  }
}

因此,考虑到它将在循环中被多次使用,在函数之外定义正则表达式是有意义的,但这就是想法。

还请注意,该脚本是相当通用的,目的是只检查正则表达式创建的行为和成本<>

来自Mozilla的正则表达式JavaScript指南:

正则表达式文字在计算脚本时提供正则表达式的编译。当正则表达式保持不变时,请使用此选项以获得更好的性能。

根据ECMA-262规范,§7.8.5正则表达式文字:

正则表达式文本是一个输入元素,每次计算文本时都会转换为RegExp对象(请参见15.10)。

换言之,当脚本第一次被解析时,它会被编译一次。

同样值得注意的是,根据ES5规范,两个文字将编译为RegExp的两个不同实例,即使文字本身相同。因此,如果给定的文字在脚本中出现两次,它将被编译两次,分别为两个不同的实例:

程序中的两个正则表达式文字的计算结果是正则表达式对象,即使这两个文字的内容相同,它们也不会以===的形式相互比较。

每次计算文本时,都会创建一个新对象,就好像是由表达式new RegExp(Pattern, Flags)创建的一样,其中RegExp是具有该名称的标准内置构造函数。

所提供的答案无法清楚区分场景背后的两个不同过程:当点击regexp对象创建表达式时,regexp编译正则表达式对象创建

是的,使用regexp字面语法,您可以获得一次性regexp编译的性能优势。

但是,如果您的代码在ES5+环境中执行,在您的示例中,每次代码路径进入doThing()函数时,它实际上都会创建一个新的RegExp对象,而不需要一次又一次地编译regexp。

在ES5中,每次代码路径命中表达式时,文本语法都会生成一个新的RegExp对象,该表达式通过文本创建正则表达式:

function getRE() {
    var re = /[a-z]/;
    re.foo = "bar";
    return re;
}
var reg = getRE(),
    re2 = getRE();
console.log(reg === re2); // false
reg.foo = "baz";
console.log(re2.foo); // "bar"

为了从实际数字的角度说明上述语句,请查看本jsperf中storedRegExpinlineRegExp测试之间的性能差异。

storedRegExp在浏览器中的速度将比inlineRegExp快5-20%,这是每次创建(和垃圾收集)新RegExp对象的开销。

共谋:
如果您大量使用文字regexp,请考虑将它们缓存在需要它们的范围之外,这样它们不仅可以编译一次,而且还可以为它们创建一次实际的regexp对象。

javascript中有两个"正则表达式"类型的对象。正则表达式实例和RegExp对象。

此外,有两种方法可以创建正则表达式实例:

  1. 使用/regex/语法和
  2. 使用新的RegExp("Regx")

每一次都会创建新的正则表达式实例。

但是,只有一个全局RegExp对象。

var input = 'abcdef';
var r1 = /(abc)/;
var r2 = /(def)/;
r1.exec(input);
alert(RegExp.$1); //outputs 'abc'
r2.exec(input);
alert(RegExp.$1); //outputs 'def'

实际模式是在使用语法1 时加载脚本时编译的

模式参数在使用前会编译成内部格式。对于语法1,模式是在加载脚本时编译的。对于语法2,模式是在使用之前编译的,或者在调用编译方法时编译的。

但是您仍然可以在每次方法调用时获得不同的正则表达式实例。铬与萤火虫的测试

function testregex() {
    var localreg = /abc/;
    if (testregex.reg != null){
        alert(localreg === testregex.reg);
    };
    testregex.reg = localreg;
}
testregex();
testregex();

这是非常小的开销,但如果您只想要一个正则表达式,那么最安全的做法是只在函数

的外部创建一个实例

如果函数不是字面形式的,则每次调用函数时都会编译正则表达式
既然你是以字面的形式包含它,你就没有什么可担心的了。

以下是来自websina.com的一句话:

正则表达式文字在计算脚本时提供正则表达式的编译。当正则表达式保持不变时,请使用此选项以获得更好的性能。

调用RegExp对象的构造函数,如下所示:
re = new RegExp("ab+c")

使用构造函数函数可以在运行时编译正则表达式。当您知道正则表达式模式将要更改,或者您不知道该模式并且正在从其他来源(如用户输入)获取该模式时,请使用构造函数。