3

一个问题:

我想处理一个字符串 ( 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%,我上次检查时)

...

三个问题:

  1. 为什么这个过程在 Chrome 中看起来更慢,而在 Firefox 中(例如)更快?
  2. 与给定更大字符串或数组replace()的循环相比,该方法是否有可能更快?while()如果不是,它在Code Golf之外有什么好处?
  3. 没有办法优化这个过程,让它像第二种功能方法一样更高效、更轻松?

我欢迎任何关于这些过程背后发生了什么的见解。

...

[ Fo(u)r 记录:我很高兴在我使用“lambda”和/或“功能”这两个词时被点名。我仍在学习这些概念,所以不要假设我确切地知道我在说什么,如果我在这里误用了这些术语,请随时纠正我。]

4

2 回答 2

3

为什么这个过程在 Chrome 中看起来更慢,而在 Firefox 中(例如)更快?

因为它必须调用一个(非本机)函数,这很昂贵。Firefox 的引擎可以通过识别和内联查找来优化它。

与给定更大字符串或数组的 while() 循环相比,replace() 方法是否有可能更快?

是的,它必须做更少的字符串连接和赋值,并且 - 正如你所说 - 要初始化的变量更少。然而,您只能对其进行测试以证明我的假设(并且还可以查看http://jsperf.com/match-and-substitute/4以获取其他片段 - 例如,您可以看到 Opera 优化了 lambda-replace2 而不是使用arguments)。

如果不是,它在 Code Golf 之外有什么好处?

我不认为代码高尔夫是正确的术语。软件质量与可读性和可理解性有关,用其术语来说,功能代码的简洁性和优雅性(尽管这是主观的)是使用这种方法的原因(实际上我从未见过用 替换execsubstring重新连接)。

有没有办法优化这个过程,让它像第二种功能方法一样更高效、更轻松?

你不需要那个remainder变量。有rgx一个lastIndex属性,它会自动通过 推进匹配str

于 2013-03-17T20:28:43.733 回答
2

你的while循环exec()比它应该的要慢一些,因为你在做额外的工作(substring),因为你exec()在非全局正则表达式上使用。如果您需要遍历所有匹配项,则应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()如果您使用函数返回的值(即您在匿名函数中进行真正的替换),则方法比while循环更具可读性(意图更清晰)。您也不必自己重建替换的字符串。这是首选的地方。(我希望这能回答问题 2 的第二部分)。exec() replaceexec()

我想exec()被用于替代以外的目的(除了非常特殊的情况,例如this)。如果可能,更换应使用replace().

仅当实际输入的性能严重下降时,才需要进行优化。我没有任何优化可以展示,因为已经分析了 2 个唯一可能的选项,并且 2 个不同浏览器之间的性能相互矛盾。这在未来可能会改变,但就目前而言,您可以选择具有更好跨浏览器性能的最差浏览器来使用。

于 2013-03-17T21:10:38.403 回答