JS技术

JavaScript之条件表达式性能影响

字号+ 作者: 来源: 2014-11-16 22:20 我要评论( )

JavaScript之条件表达式性能影响

       与循环相似,条件表达式决定JavaScript运行流的走向。其他语言使用if-else或者switch表达式的传统观点也适用于JavaScript。由于不同的浏览器针对流程控制进行了不同的优化,使用哪种技术并不总是很清楚。
      一、if-else 与switch 比较
      使用if-else或者switch的流行理论是基于测试条件的数量:条件数量较大,倾向于使用switch而不是if-else。这通常归结到代码的易读性。这种观点认为,如果条件较少时,if-else容易阅读,而条件较多时switch更容易阅读。考虑下面几点:
      if (found){
            //do something
      } else {
            //do something else
      }

      switch(found){
            case true:
            //do something
            break;
            default:
            //do something else
      }
      虽然两个代码块实现同样任务,很多人会认为if-else 表达式比witch 表达式更容易阅读。如果增加条件体的数量,通常会扭转这种观点:
      if (color == "red"){
            //do something
      } else if (color == "blue"){
            //do something
      } else if (color == "brown"){
            //do something
      } else if (color == "black"){
            //do something
      } else {
            //do something
      }
      switch (color){
            case "red":
            //do something
            break;
            case "blue":
            //do something
            break;
            case "brown":
            //do something
            break;
            case "black":
            //do something
            break;
            default:
            //do something
      }
      大多数人会认为这段代码中的switch 表达式比if-else 表达式可读性更好。事实证明,大多数情况下switch表达式比if-else更快,但只有当条件体数量很大时才明显更快。两者间的主要性能区别在于:当条件体增加时,if-else 性能负担增加的程度比switch 更多。因此,我们的自然倾向认为条件体较少时应使用if-else 而条件体较多时应使用switch 表达式,如果从性能方面考虑也是正确的。
      一般来说,if-else 适用于判断两个离散的值或者判断几个不同的值域。如果判断多于两个离散值,switch表达式将是更理想的选择。
      二、优化if-else
      优化if-else 的目标总是最小化找到正确分支之前所判断条件体的数量。最简单的优化方法是将最常见的条件体放在首位。考虑下面的例子:
      if (value < 5) {
            //do something
      } else if (value > 5 && value < 10) {
            //do something
      } else {
            //do something
      }
      这段代码只有当value值经常小于5 时才是最优的。如果value 经常大于等于10,那么在进入正确分支之前,必须两次运算条件体,导致表达式的平均时间提高。if-else中的条件体应当总是按照从最大概率到最小概率的顺序排列,以保证理论运行速度最快。
      另外一种减少条件判断数量的方法是将if-else组织成一系列嵌套的if-else表达式。使用一个单独的一长串的if-else通常导致运行缓慢,因为每个条件体都要被计算。例如:
      if (value == 0){
            return result0;
      } else if (value == 1){
            return result1;
      } else if (value == 2){
            return result2;
      } else if (value == 3){
            return result3;
      } else if (value == 4){
            return result4;
      } else if (value == 5){
            return result5;
      } else if (value == 6){
            return result6;
      } else if (value == 7){
            return result7;
      } else if (value == 8){
            return result8;
      } else if (value == 9){
            return result9;
      } else {
            return result10;
      }
      在这个if-else 表达式中,所计算条件体的最大数目是10。如果假设value 的值在0 到10 之间均匀分布,那么会增加平均运行时间。为了减少条件判断的数量,此代码可重写为一系列嵌套的if-else 表达式,例如:
      if (value < 6){
            if (value < 3){
                  if (value == 0){
                        return result0;
                  } else if (value == 1){
                        return result1;
                  } else {
                        return result2;
                  }
            } else {
                  if (value == 3){
                        return result3;
                  } else if (value == 4){
                        return result4;
                  } else {
                        return result5;
                  }
            }
            } else {
                  if (value < 8){
                        if (value == 6){
                              return result6;
                        } else {
                              return result7;
                        }
                  } else {
                        if (value == 8){
                              return result8;
                        } else if (value == 9){
                              return result9;
                        } else {
                              return result10;
                  }
            }
      }
      在重写的if-else表达式中,每次抵达正确分支时最多通过四个条件判断。它使用二分搜索法将值域分成了一系列区间,然后逐步缩小范围。当数值范围分布在0到10时,此代码的平均运行时间大约是前面那个版本的一半。此方法适用于需要测试大量数值的情况(相对离散值来说switch更合适)。
      三、查表法
      有些情况下要避免使用if-else或switch。当有大量离散值需要测试时,if-else和switch都比使用查表法要慢得多。在JavaScript中查表法可使用数组或者普通对象实现,查表法访问数据比if-else或者switch更快,特别当条件体的数目很大时。
      与if-else和switch相比,查表法不仅非常快,而且当需要测试的离散值数量非常大时,也有助于保持代码的可读性。例如,当switch 表达式很大时就变得很笨重,诸如:
      switch(value){
            case 0:
            return result0;
            case 1:
            return result1;
            case 2:
            return result2;
            case 3:
            return result3;
            case 4:
            return result4;
            case 5:
            return result5;
            case 6:
            return result6;
            case 7:
            return result7;
            case 8:
            return result8;
            case 9:
            return result9;
            default:
            return result10;
      }
      switch 表达式代码所占的空间可能与它的重要性不成比例。整个结构可以用一个数组查表替代:
      //define the array of results
      var results = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10]
      //return the correct result
      return results[value];
      当使用查表法时,必须完全消除所有条件判断。操作转换成一个数组项查询或者一个对象成员查询。使用查表法的一个主要优点是:由于没有条件判断,当候选值数量增加时,很少,甚至没有增加额外的性能开销。
      查表法最常用于一个键和一个值形成逻辑映射的领域(如前面的例子)。一个switch 表达式更适合于每个键需要一个独特的动作,或者一系列动作的场合。
      四、递归
      复杂算法通常比较容易使用递归实现。事实上,有些传统算法正是以递归实现的,诸如阶乘函数:
      function factorial(n){
            if (n == 0){
                  return 1;
            } else {
                  return n * factorial(n-1);
            }
      }
      递归函数的问题是,一个错误定义,或者缺少终结条件可导致长时间运行,冻结用户界面。此外,递归函数还会遇到浏览器调用栈大小的限制。
      五、调用栈限制
      JavaScript 引擎所支持的递归数量与JavaScript 调用栈大小直接相关。只有Internet Explorer 例外,它的调用栈与可用系统内存相关,其他浏览器有固定的调用栈限制。大多数现代浏览器的调用栈尺寸比老式浏览器要大(例如Safari 2 调用栈尺寸是100)。当你使用了太多的递归,超过最大调用栈尺寸时,浏览器会出错并弹出以下信息:
      1、Internet Explorer: “Stack overflow at line x”
      2、Firefox: “Too much recursion”
      3、Safari: “Maximum call stack size exceeded”
      4、Opera: “Abort (control stack overflow)”
      Chrome 是唯一不显示调用栈溢出错误的浏览器。
      关于调用栈溢出错误,最令人感兴趣的部分大概是:在某些浏览器中,他们的确是JavaScript错误,可
以用一个try-catch表达式捕获。异常类型因浏览器而不同。在Firefox中,它是一个InternalError;在Safari
和Chrome中,它是一个RangeError;在Internet Explorer中抛出一个一般性的Error 类型。(Opera不抛出
错误;它终止JavaScript 引擎)。这使得我们能够在JavaScript中正确处理这些错误:
      try {
            recurse();
      } catch (ex){
            alert("Too much recursion!");
      }
      如果不管它,那么这些错误将像其他错误一样冒泡上传(在Firefox中,它结束于Firebug和错误终端;在Safari/Chrome中它显示在JavaScript终端上),只有Internet Explorer例外。IE 不会显示一个JavaScript错误,但是会弹出一个提示堆栈溢出信息的对话框。

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • WEB前端教程-JavaScript里的类和继承

    WEB前端教程-JavaScript里的类和继承

    2016-01-21 15:28

  • 高性能动画“box-shadow”属性 - FedFun - 博客频道 - CSDN.NET FedFun 爱前端,乐分

    高性能动画“box-shadow”属性 - FedFun - 博客频道 - CSDN.NET FedF

    2015-12-14 16:15

  • JS开发者调查 - FedFun - 博客频道 - CSDN.NET FedFun 爱前端,乐分享,前端痴王海庆的博客!

    JS开发者调查 - FedFun - 博客频道 - CSDN.NET FedFun 爱前端,乐分

    2015-12-13 11:08

  • Jquery下编写流行的前端的应用源码_Javascript教程

    Jquery下编写流行的前端的应用源码_Javascript教程

    2015-10-01 09:24

网友点评