95
function foo(a) {
    if (/* Some condition */) {
        // perform task 1
        // perform task 3
    }
    else {
        // perform task 2
        // perform task 3
    }
}

我有一个结构类似于上面的函数。我想将任务 3抽象为一个函数,bar()但我希望将此函数的访问限制在foo(a).

为了达到我想要的,改成下面这样对吗?

function foo(a) {
    function bar() {
        // Perform task 3
    }

    if (/* Some condition */) {
        // Perform task 1
        bar();
    }
    else {
        // Perform task 2
        bar();
    }
}

如果以上是正确的,bar()每次foo(a)被调用时都会重新定义吗?(我担心这里会浪费 CPU 资源。)

4

5 回答 5

136

是的,你所拥有的是正确的。一些注意事项:

  • bar在每次调用函数时创建foo,但是:
    • 在现代浏览器上,这是一个非常快的过程。(有些引擎很可能只为它编译一次代码,然后每次都在不同的上下文中重用该代码;谷歌的 V8 引擎 [在 Chrome 和其他地方] 在大多数情况下都会这样做。)
    • 根据具体bar情况,某些引擎可能会确定它们可以“内联”它,从而完全消除函数调用。V8 做到了这一点,我敢肯定它不是唯一这样做的引擎。当然,只有在不改变代码行为的情况下,他们才能这样做。
  • bar每次创建的性能影响(如果有的话)在 JavaScript 引擎之间会有很大差异。如果bar是微不足道的,它将从无法检测到相当小。如果您没有foo连续调用数千次(例如,从mousemove处理程序),我不会担心。即使您是,我也只会在看到速度较慢的引擎出现问题时才会担心。这是一个涉及 DOM 操作的测试用例,这表明存在影响,但影响不大(可能被 DOM 的东西冲淡了)。这是一个进行纯计算的测试用例,它显示出更高的影响,但坦率地说,即使是微秒的差异,因为即使是 92% 的增长也需要微秒几秒钟的发生还是非常非常快的。直到/除非您看到现实世界的影响,否则无需担心。
  • bar只能从函数内部访问,并且它可以访问该函数调用的所有变量和参数。这使得这是一个非常方便的模式。
  • 请注意,因为您使用了函数声明,所以将声明放在哪里(顶部、底部或中间)并不重要——只要它位于函数的顶层,而不是流控制语句内,即语法错误),它是在运行第一行逐步代码之前定义的。
于 2012-04-18T07:00:11.080 回答
15

这就是闭包的用途。

var foo = (function () {
  function bar() {
    // perform task 3
  };

  function innerfoo (a) { 
    if (/* some cond */ ) {
      // perform task 1
      bar();
    }
    else {
      // perform task 2
      bar();
    }
  }
  return innerfoo;
})();

Innerfoo(一个闭包)持有对 bar 的引用,并且只有对 innerfoo 的引用从匿名函数返回,该匿名函数只调用一次以创建闭包。

酒吧不能通过这种方式从外面进入。

于 2012-04-18T07:30:57.780 回答
9
var foo = (function () {
    var bar = function () {
        // perform task 3
    }
    return function (a) {

        if (/*some condition*/) {
            // perform task 1
            bar();
        }
        else {
            // perform task 2
            bar();
        }
    };
}());

闭包保持包含的范围,bar()从自执行匿名函数返回新函数将更可见的范围设置为foo()。匿名自执行函数只运行一次,所以只有一个bar()实例,每次执行foo()都会用到它。

于 2012-04-18T07:13:21.673 回答
5

是的,这很好用。

每次输入外部函数时都不会重新创建内部函数,而是重新分配它。

如果您测试此代码:

function test() {

    function demo() { alert('1'); }

    demo();
    demo = function() { alert('2'); };
    demo();

}

test();
test();

它将显示1, 2, 1, 2, 不是1, 2, 2, 2.

于 2012-04-18T07:06:17.340 回答
0

我创建了一个 jsperf 来测试嵌套与非嵌套以及函数表达式与函数声明,我惊讶地发现嵌套测试用例的执行速度比非嵌套快 20 倍。(我预计相反或可以忽略不计的差异)。

https://jsperf.com/nested-functions-vs-not-nested-2/1

这是在 Chrome 76、macOS 上。

于 2019-08-08T17:25:39.620 回答