为什么使用lambda的String.replace()比重复调用RegExp.exec()的while循环慢?
Why is String.replace() with lambda slower than a while-loop repeatedly calling RegExp.exec()?
一个问题:
我想处理一个字符串(str
),以便任何带括号的数字(由rgx
匹配)被从数组(sub
)中的适当位置获取的值替换:
var rgx = /'(('d+)')/,
str = "this (0) a (1) sentence",
sub = [
"is",
"test"
],
result;
给定上面声明的变量,result
应该是'这是一个测试句子'。
两个解决方案:
如此:
var mch,
parsed = '',
remainder = str;
while (mch = rgx.exec(remainder)) { // Not JSLint approved.
parsed += remainder.substring(0, mch.index) + sub[mch[1]];
remainder = remainder.substring(mch.index + mch[0].length);
}
result = (parsed) ? parsed + remainder : str;
但是我认为下面的代码会更快。它有更少的变量,更简洁,并使用匿名函数表达式(或lambda):
result = str.replace(rgx, function() {
return sub[arguments[1]];
});
这也可以,但我对速度的看法是错误的;在Chrome中,这是令人惊讶的(~50%,上次我检查)慢!
…
三个问题:
- 为什么这个过程在Chrome中看起来更慢,而(例如)在Firefox中更快?
- 是否有机会
replace()
方法将比while()
循环给定更大的字符串或数组更快?如果不是,它在高尔夫代码之外的好处是什么? - 是否有一种方法可以优化这一过程,使其既高效又像第二种方法那样省事?
我欢迎任何关于这些过程背后发生的事情的见解。
…
[>Fo(u)r记录:我很高兴被称为我使用的词'lambda'和/或'functional'。我还在学习这些概念,所以不要以为我完全知道我在说什么,如果我在这里误用了术语,请随时纠正我。
为什么这个过程在Chrome中看起来更慢,而(例如)在Firefox中更快?
因为它必须调用(非本机)函数,这是昂贵的。Firefox的引擎也许可以通过识别和内联查找来优化这个问题。
是否有机会,replace()方法将比while()循环给定更大的字符串或数组更快?
是的,它必须做更少的字符串连接和赋值,并且-正如你所说-更少的变量初始化。然而,你只能测试它来证明我的假设(也可以看看http://jsperf.com/match-and-substitute/4的其他片段-例如,你可以看到Opera优化lambda-replace2,它不使用arguments
)。
如果没有,它在Code Golf之外的好处是什么?
我不认为代码高尔夫是正确的术语。软件质量是关于可读性和可理解性的,用它们的术语来说,功能代码的简洁性和优雅性(虽然这是主观的)是使用这种方法的原因(实际上我从未见过exec
、substring
和重新连接的替代品)。
是否有一种方法可以优化这个过程,使它既高效又像第二种方法那样省事?
您不需要remainder
变量。rgx
有一个lastIndex
属性,它会自动将匹配推进到str
。
使用exec()
的while
循环略慢,因为您在非全局正则表达式上使用exec()
时正在做额外的工作(substring
)。如果你需要循环遍历所有匹配,你应该在全局正则表达式上使用while
循环(g
标志启用);这样,您就可以避免对字符串中已处理的部分进行额外的修剪。
var rgR = /'(('d+)')/g;
var mch,
result = '',
lastAppend = 0;
while ((mch = rgR.exec(str)) !== null) {
result += str.substring(lastAppend, mch.index) + sub[mch[1]];
lastAppend = rgR.lastIndex;
}
result += str.substring(lastAppend);
这个因素不会影响不同浏览器之间的性能差异
看起来性能差异来自于浏览器的实现。由于不熟悉实现,我无法回答差异来自哪里。
在功率方面,exec()
和replace()
具有相同的功率。这包括不使用replace()
返回值的情况。示例1 。示例2 。
replace()
方法比exec()
的while
循环更具可读性(意图更清晰),如果您正在使用函数返回的值(即您正在匿名函数中进行真正的替换)。您也不必自己重建被替换的字符串。这就是replace
优于exec()
的地方。(我希望这回答了问题2的第二部分)。
我认为exec()
用于替代之外的目的(除了非常特殊的情况,例如this)。如果可能的话,应该替换replace()
。
优化只有在实际输入的性能严重下降时才有必要。我没有任何优化要展示,因为两个唯一可能的选项已经分析过了,两个不同浏览器之间的性能矛盾。这在将来可能会改变,但是现在,您可以选择跨浏览器性能最差的浏览器来使用。
- 如何使jQuery插件函数可调用以供独立使用,而不在集合上操作
- D3在一个调用中绘制不同的SVG形状,没有可见性
- 如何从Java/scala调用js美化程序
- 如何调用这个匿名 JavaScript 函数
- 如何从模块链中调用函数.导出到节点中
- 我需要从php调用javascript或jquery
- Chrome开发工具(如何知道我在调用哪个javascript对象)
- 单击按钮后如何逐个调用分区,上一个分区将隐藏
- 另一个ajax调用中的Jquery ajax调用在for循环中没有按预期工作
- Twitter Bootstrap typeahead:使用“this”获取上下文/调用元素
- node.js:setInterval()正在跳过调用
- 如何在单击复选框后调用控制器方法
- 如何在JQuery函数中定义一个值,然后调用另一个函数并使用该值
- 使用Google Visualization动态调用构造函数
- 如何在webView,Android中从@JavascriptInterface方法调用Javascript
- 是否可以将一个函数输入连接到另一个函数调用的文本
- 调用函数内部的函数
- RegExp.exec() 在 Firefox 中反复调用时返回 null
- 为什么使用lambda的String.replace()比重复调用RegExp.exec()的while循环慢?
- 对于具有全局选项的模式,连续调用RegExp测试失败