4

我刚刚阅读了这篇关于命名函数表达式及其与 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;
  };
})();

当我看到这一点时,我的第一反应是,这将是可怕的维护。以这种方式编写的代码实际上并不易于理解。

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

我更喜欢包含一些测试数字或解释这些功能在运行时有何不同的答案。

4

4 回答 4

3

这是非常有效的。注意();最后的。这将立即执行并将外部函数的结果分配给contains。它比每次使用函数时都执行底层逻辑要高效得多contains

不是每次都检查contains()是否compareDocumentPosition存在,而是在代码第一次执行时完成一次。compareDocumentPosition存在或不存在的事实不会改变,因此只检查一次是理想的。

于 2011-03-31T22:06:13.980 回答
2

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

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

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

  1. 结果函数执行的次数(1x = 无增益)和
  2. 分支的“成本”与其他代码(取决于)和
  3. 创建所述闭包的“成本”(非常便宜)

对于廉价的分支或少量的执行计数,“效率”会降低。如果有特定的用例,则对其进行基准测试,您将获得“答案”。

当我看到这一点时,我的第一反应是,这将是可怕的维护。以这种方式编写的代码实际上并不易于理解。

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

其中一些“不容易理解”只是对这种结构缺乏熟悉(不是试图在这里暗示任何负面的东西)——另一方面,我所知道的每一种语言都有在存在的情况下被滥用的特性更清洁的解决方案。

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

同样,确切的情况有点混乱,IMOHO。但是,JavaScript不是C/C++/Java,函数作为第一类值和闭包在 C/C++/Java 中不存在(这是一个小小的谎言,闭包可以在 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)

快乐编码。

于 2011-03-31T22:37:06.170 回答
1

如上所述的主要优势是速度。拥有一个带有嵌套ifs 的函数意味着每次调用该函数时都需要重新评估条件。但是,我们知道条件的结果永远不会改变。

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

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() {
        ...
    }

})();
于 2011-03-31T22:13:00.323 回答
0

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

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

于 2011-03-31T22:19:07.520 回答