我偶然发现了性能测试,说JavaScript中的RegExps不一定很慢:http://jsperf.com/regexp-indexof-perf
有一件事我没有得到:两件案件涉及我认为完全相同的事情:
RegExp('(?:^| )foo(?: |$)').test(node.className);
和
/(?:^| )foo(?: |$)/.test(node.className);
在我看来,这两行完全相同,第二行是创建一个RegExp对象的某种简写。不过,它比第一个 快两倍。
这些案例被称为“动态正则表达式”和“内联正则表达式”。
有人可以帮我理解这两者之间的差异(以及性能差距)吗?
答案 0 :(得分:24)
如今,这里给出的答案并不完全正确。
从ES5开始,文字语法行为与关于对象创建的RegExp()
语法相同:每次代码路径到达表达式时都会创建一个新的RegExp对象正在参加。
因此,现在它们之间唯一的区别就是编译正则表达式的频率:
RegExp()
语法 - 每次创建新对象例如,请参阅Stoyan Stefanov's JavaScript Patterns book:
正则表达式文字与文本之间的另一个区别 构造函数是文字只在一段时间内创建一个对象 解析时间。如果在循环中创建相同的正则表达式,则 以前创建的对象将返回其所有属性 (例如lastIndex)已经从第一次设置。考虑一下 以下示例作为同一对象的说明 两次返回。
function getRE() {
var re = /[a-z]/;
re.foo = "bar";
return re;
}
var reg = getRE(),
re2 = getRE();
console.log(reg === re2); // true
reg.foo = "baz";
console.log(re2.foo); // "baz"
此行为在ES5中已更改,文字也会创建新对象。许多浏览器也纠正了这种行为 环境,所以不能依赖它。
如果您在所有现代浏览器或NodeJS中运行此示例,则会得到以下内容:
false
bar
意味着e 您正在调用getRE()
函数时,即使使用文字语法方法,也会创建一个新的RegExp
对象。
以上不仅解释了为什么你不应该将RegExp()
用于不可变的regexp(今天这是众所周知的性能问题),而且还解释了:
(我更感到惊讶的是,inlineRegExp和storedRegExp有所不同 的结果。)
storedRegExp
跨浏览器的速度比inlineRegExp
快5到20%,因为每次创建(和垃圾回收)新的RegExp
对象都没有开销。
<强>结论:强>
始终使用文字语法创建不可变的regexp,并在重新使用时对其进行缓存。换句话说,不要依赖ES5下的envs中的行为差异,并在上面的envs中继续适当缓存。
为什么是字面语法?与构造函数语法相比,它具有一些优势:
- 它更短,不会强迫你从类的角度思考 构造函数。
- 使用
醇>RegExp()
构造函数时,还需要转义引号和双转义反斜杠。它使正则表达式 他们的性质难以阅读和理解更加困难。
(来自同一本Stoyan Stefanov's JavaScript Patterns书的免费引用) 因此,除非在编译时不知道正则表达式,否则坚持使用文字语法总是一个好主意。
答案 1 :(得分:9)
性能差异与使用的语法无关部分与所使用的语法相关:/pattern/
和RegExp(/pattern/)
(你没有使用的语法)测试后者)正则表达式只编译一次,但对于RegExp('pattern')
,表达式在每次使用时编译。请参阅Alexander's answer,这应该是今天接受的答案。
除了上述内容之外,在inlineRegExp
和storedRegExp
的测试中,您正在查看在解析源代码文本时初始化的代码,而对于dynamicRegExp
常规代码为每次调用方法创建表达式。请注意,actual tests多次执行r = dynamicRegExp(element)
之类的操作,而准备代码只运行一次。
以下内容为您提供了相同的结果,according to another jsPerf:
var reContains = /(?:^| )foo(?: |$)/;
...和
var reContains = RegExp('(?:^| )foo(?: |$)');
...当两者都与
一起使用时function storedRegExp(node) {
return reContains.test(node.className);
}
当然,RegExp('(?:^| )foo(?: |$)')
的源代码可能首先被解析为String
,然后被解析为RegExp
,但我怀疑它本身会慢两倍。但是,以下内容将为每次方法调用一次又一次地为<{1}} 创建:
RegExp(..)
如果在原始测试中你只调用一次方法,那么内联版本的速度将不会快2倍。
(我更感到惊讶的是function dynamicRegExp(node) {
return RegExp('(?:^| )foo(?: |$)').test(node.className);
}
和inlineRegExp
有不同的结果。Alexander's answer也解释了这一点。)
答案 2 :(得分:6)
在第二种情况下,在解析语言期间创建正则表达式对象,在第一种情况下,RegExp类构造函数必须解析任意字符串。