Javascript:forked函数声明效率有多高?

时间:2011-03-31 22:02:57

标签: javascript function-declaration performance

我刚刚通过this article阅读了有关命名函数表达式及其与IE< = 8的不兼容性。

我特别好奇一个陈述:

  

Web开发中的一个常见模式是基于某种特征测试来“分叉”函数定义,从而实现最佳性能。

从他的页面中摘录的一个例子:

var contains = (function() {
  var docEl = document.documentElement;

  if (typeof docEl.compareDocumentPosition != 'undefined') {
    return function(el, b) {
      return (el.compareDocumentPosition(b) & 16) !== 0;
    };
  }
  else if (typeof docEl.contains != 'undefined') {
    return function(el, b) {
      return el !== b && el.contains(b);
    };
  }
  return function(el, b) {
    if (el === b) return false;
    while (el != b && (b = b.parentNode) != null);
    return el === b;
  };
})();

当我看到这一点时,我的直接反应是维持这一点很糟糕。以这种方式编写的代码并不能真正使其易于理解。

在这种情况下,不是在声明外部函数之后立即调用的另一个函数中有条件地定义函数,而是可以编写嵌套if s的函数。它会更长,但在我看来更容易理解(虽然我来自C / C ++ / Java)。

我希望答案包括一些测试数字或解释这些功能在运行时会有何不同。

4 个答案:

答案 0 :(得分:3)

效率很高。注意最后的();。这会立即执行并将外部函数的结果分配给contains。每次使用函数contains时,它比执行底层逻辑更有效。

不是每次调用contains()时都检查compareDocumentPosition,而是在代码首次执行时执行此操作。 <{1}}存在或不存在的事实不会改变,因此只检查一次是理想的。

答案 1 :(得分:2)

  
    

Javascript:分叉函数声明效率有多高?

  

除非使用JIT /运行时完成任何魔术优化,否则“花费”相同以调用任何函数。 函数只是通常存储在变量(或属性)中的对象

返回专用函数对象的版本的“效率”还取决于多种因素,包括(但不限于):

  1. 执行结果函数的次数(1x =无增益)和
  2. 分支与其他代码(取决于)和
  3. 的“成本”
  4. 创造所述封闭的“成本”(非常便宜)
  5. 对于廉价分支或少量执行计数,“效率”降低。如果有特定用例,则基准,您将获得“答案”。

      
        

    当我看到这一点时,我的直接反应是维持这一点很糟糕。以这种方式编写的代码并不能真正使其易于理解。

      

    这个例子不一定正义,IMOHO并且由于其他原因而变得混乱。我认为给匿名外部函数一个明确的名称 - 即使对于函数表达式也可以这样做 - 例如,有助于更好地澄清意图。编写代码首先要清理。然后运行性能分析(基准测试)并根据需要进行修复。机会是“缓慢的部分”不会是最初的预期。

    其中一些“不易理解”只是缺乏对这种结构的熟悉(并不试图暗示任何负面因素) - 另一方面,我所知道的每种语言都有滥用的特征。哪里有更清洁的解决方案。

      
        

    在这种情况下,不是在声明外部函数之后立即调用的另一个函数中有条件地定义函数,而是可以编写嵌套ifs的函数。它会更长,但在我看来更容易理解(虽然我来自C / C ++ / Java)。

      

    再次,确切的情况有点混乱,IMOHO。然而,JavaScript是的C / C ++ / Java和功能-AS-一流值和封闭件不存在C / C ++ /爪哇(这是一个白色的小谎言,封闭件可以被仿真在Java和最新的C ++支持某种形式的闭包AFAIK - 但我不使用C ++。)

    因此,其他语言不容易支持(或根本没有),因此在其他语言中看不到这种结构 - 它没有提及该方法的可行性(在JavaScript或其他地方) )一般而言。

      
        

    我希望答案包括一些测试数字或解释这些功能在运行时会有何不同。

      

    见上文。


    扩展顶部的粗体部分:

    一个函数是“只是一个对象”,使用(...)运算符“应用”(读取:调用)。

    function x () {
       alert("hi")
    }
    x() // alerts
    window.x() // alerts -- just a property (assumes global scope above)
    a = {hello: x}
    a.hello() // alerts (still property)
    b = a.hello
    b() // alerts (still just a value that can be invoked)
    

    快乐的编码。

答案 2 :(得分:1)

提到的主要优点是速度。使用嵌套if s的单个函数意味着每次调用函数时都需要重新评估条件。但是,我们知道条件的结果永远不会改变。

如果您担心可读性,可以通过更易读的方式实现类似的效果:

var contains = (function () {
    var docEl = document.documentElement;
    if (typeof docEl.compareDocumentPosition != 'undefined') {
        return contains_version1;
    } else if (typeof docEl.contains != 'undefined') {
        return contains_version2;
    } else {
        return contains_version3;
    }

    function contains_version1() {
        ...
    }

    function contains_version2() {
        ...
    }

    function contains_version3() {
        ...
    }
})();

或者:

(function () {
    var docEl = document.documentElement;
    var contains =
        typeof docEl.compareDocumentPosition != 'undefined' ? contains_version1 :
        typeof docEl.contains != 'undefined' ? contains_version2 :
        contains_version3;

    function contains_version1() {
        ...
    }

    function contains_version2() {
        ...
    }

    function contains_version3() {
        ...
    }

})();

答案 3 :(得分:0)

如果您来自纯C背景,这是相对奇怪的构造,但应该很容易映射到C ++ / Java人的已知概念。这个特定的示例本质上是具有抽象函数的实现基类,其中3个派生类为不同的浏览器实现不同的类。对于这种情况使用“if”或“switch”并不是C ++和Java中最好的方法。

这样的一组函数将被打包成一个“类”,在这种情况下,它将紧密映射到具有虚函数的基类和每个浏览器的多个实现......