【问题标题】:JavaScript style/optimization: String.indexOf() v. Regex.test()JavaScript 风格/优化:String.indexOf() v. Regex.test()
【发布时间】:2010-09-16 01:23:57
【问题描述】:

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

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

我很感兴趣,因为要编写这个测试我会做:

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

这只是风格上的差异,还是其他代码的作者知道一些我不知道的优化?或者也许有不同的充分理由这样做,或者不使用正则表达式......?

在我看来,使用 String.indexOf() 来读起来有点困难(但是,我对正则表达式很满意),但是在某些情况下,它可能比写一个等效的“更好”正则表达式?

“更好”可能更快或更有效,(尽管显然这取决于浏览器的 JavaScript 引擎),或者我不知道的其他一些原因。谁能赐教?

【问题讨论】:

  • 这对我来说似乎很主观。我们谈论的是 JavaScript——这里的速度差异(如果有的话)将非常小以至于没有实际意义,除非您在一个巨大的循环中执行此操作。话虽如此,它看起来确实是一个 hack,可以写得更清晰,有或没有正则表达式。

标签: javascript regex optimization coding-style


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    我怀疑这是性能或优化的问题。我怀疑该代码的作者根本不熟悉或不熟悉正则表达式。还要注意逗号分隔的字符串是如何不被拆分以利用对象属性的——这也可能是对语言不熟悉的情况。

    例如,在逗号分隔的允许运算符列表中测试运算符的另一种方法是拆分逗号分隔的允许运算符列表,并使用运算符作为属性对对象进行一次性初始化:

    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};函数 OpTest(op) { return !!allowedOperators[op]; ...它在 FF3 中的速度大约是其他两种方法的两倍,但在 IE6 中速度较慢(已经比 FF3 慢 10 倍)。
    • 虽然它更干净。我喜欢。而且由于它可以兼作调度表,因此无论浏览器如何,它都可能是一个整体改进。
    【解决方案3】:

    我进行了一些测试。第一种方法稍微快一点,但即使在大量使用的情况下也不足以产生任何真正的影响...... 除了 sCompOp 可能是一个很长的字符串。因为第一种方法搜索的是固定长度的字符串,所以无论sCompOp有多长,它的执行时间都很稳定,而第二种方法可能会遍历sCompOp的整个长度。

    另外,第二种方法可能会匹配无效字符串 - “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】:

      从前可能存在明显的速度差异,但现在不再如此。我认为这是:

      1. 来自(无神)正则表达式之前的土地的遗留代码。
      2. 由不了解或害怕 REGEX 的人撰写。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-12-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多