JavaScript样式/优化:String.indexOf()与Regex.test()的比较

9

我最近遇到了这段JavaScript代码:

if (",>=,<=,<>,".indexOf("," + sCompOp + ",") != -1)

我很感兴趣,因为要编写这个测试,我需要做以下事情:

if (/(>=|<=|<>)/.test(sCompOp))

这只是一种风格上的差异,还是其他代码的作者知道我不知道的优化技巧?或者也许有不同的充分理由去这样做,或者不使用正则表达式...?
在我看来,对于这个问题使用 String.indexOf() 稍微难以阅读一些(但是,我对正则表达式非常熟悉),但是否存在某些情况下它比编写等效的正则表达式更“好”呢?
通过“更好”,这可能更快或更有效(尽管显然这取决于浏览器的 JavaScript 引擎),或者其他我不知道的原因。有人能为我解答吗?

2
这对我来说似乎很主观。我们正在谈论JavaScript-如果有任何速度差异,除非您在一个巨大的循环中执行此操作,否则它将非常微小,以至于无关紧要。话虽如此,它似乎是一种hack,并且可以更清晰地编写,无论是否使用正则表达式。 - Jason Bunting
5个回答

12

我进行了一些测试。第一种方法略微更快,但在重负荷使用下也不足以产生任何真正的差异...... 除非sCompOp可能是一个非常长的字符串时。因为第一种方法搜索的是固定长度的字符串,所以无论sCompOp有多长,其执行时间都非常稳定,而第二种方法将潜在地迭代整个sCompOp的长度。

此外,第二种方法可能匹配无效的字符串——“blah blah blah<= blah blah”能够通过测试......

考虑到你很可能正在其他地方完成解析运算符的工作,我认为两种特殊情况都不会成为问题。但即使不是这种情况,对表达式进行小的修改即可解决这两个问题:

/^(>=|<=|<>)$/

测试代码:


function Time(fn, iter)
{
   var start = new Date();
   for (var i=0; i<iter; ++i)
      fn();
   var end = new Date();
   console.log(fn.toString().replace(/[\r|\n]/g, ' '), "\n : " + (end-start));
}

function IndexMethod(op)
{
   return (",>=,<=,<>,".indexOf("," + op + ",") != -1);
}

function RegexMethod(op)
{
   return /(>=|<=|<>)/.test(op);
}

function timeTests()
{
   var loopCount = 50000;
   
   Time(function(){IndexMethod(">=");}, loopCount);
   Time(function(){IndexMethod("<=");}, loopCount);
   Time(function(){IndexMethod("<>");}, loopCount);
   Time(function(){IndexMethod("!!");}, loopCount);
   Time(function(){IndexMethod("the quick brown foxes jumped over the lazy dogs");}, loopCount);
   Time(function(){IndexMethod("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");}, loopCount);

   Time(function(){RegexMethod(">=");}, loopCount);
   Time(function(){RegexMethod("<=");}, loopCount);
   Time(function(){RegexMethod("<>");}, loopCount);
   Time(function(){RegexMethod("!!");}, loopCount);
   Time(function(){RegexMethod("the quick brown foxes jumped over the lazy dogs");}, loopCount);
   Time(function(){RegexMethod("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");}, loopCount);
}

timeTests();

在IE6、FF3、Chrome 0.2.149.30中测试通过。


4
我怀疑这不是性能或优化的问题。我认为那段代码的作者可能只是对正则表达式不太熟悉或不太舒服。还请注意,逗号分隔的字符串没有被拆分以利用对象属性——这可能也是对该语言不太熟悉的情况之一。
例如,在允许的操作符逗号分隔列表中测试运算符的另一种方法是将逗号分隔的允许操作符列表拆分,并初始化一个具有操作符属性的对象:
var aOps = ">=,<=,<>".split(",");
var allowableOps = {};
for (var iLoop = 0; iLoop < aOps.length; iLoop++) {
  allowableOps[aOps[iLoop]] = true;
} //for

这种小的初始化开销很可能会被快速查找的能力所抵消:
if (allowableOps[sCompOp]) { ... }

当然,这种方式总体来说可能会更慢,但可以说是更加干净的方法。

我按照您的建议实现了以下代码:var allowedOperators = {'>=' : true, '<=' : true, '<>' : true}; function OpTest(op) { return !!allowedOperators[op]; } 在Firefox 3中,它的速度大约是其他两种方法的两倍,但在IE6中速度较慢(IE6本来就比FF3慢10倍)。 - Shog9
虽然这样更加简洁,但我喜欢它。而且由于它可以充当调度表,因此无论在哪个浏览器中使用,它都可能是一个整体改进。 - Shog9

3

曾经可能会有明显的速度差异,但现在不再是这样了。我认为这可能是:

  1. 来自(无神论者)正则表达式之前的时代的遗留代码。
  2. 由不知道正则表达式或害怕使用它的人编写。

3

这让我想起了一些早期基于JavaScript的getElementsByClassName实现。

indexOf比使用正则表达式要快得多,但使用indexOf的代码从假设开发人员将类名用空格(而不是制表符和换行符)分隔开始。公平地说,一些基于正则表达式的实现正在使用\b(单词边界),这与CSS规范不兼容(因为CSS允许在类名中使用连字符)。

在IE中使用indexOf支持getElementsByClassName确实可以产生巨大的差异,因为没有更快的替代方法,并且className属性的底层getter/setter方便地将制表符和换行符替换为空格。


1

这是一段非常老的代码吗?它可能是在正则表达式在JavaScript中被广泛使用之前编写的。总体来看,似乎有人试图过于聪明地“优化”该语句,或者来自C背景并不习惯正则表达式。正则表达式的使用可能很昂贵,但字符串连接也可能如此,如果它不在循环中,我会选择更容易理解的那个(对我来说是正则表达式)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接