在javascript中定义另一个函数内的函数

tam*_*are 73 javascript

function foo(a) {
    if (/*some condition*/) {
        // perform task 1
        // perform task 3
    }
    else {
        // perform task 2
        // perform task 3
    }
}
Run Code Online (Sandbox Code Playgroud)

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

为了达到我想要的目的,改为以下是否正确?

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

    if (/*some condition*/) {
        // perform task 1
        bar();
    }
    else {
        // perform task 2
        bar();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

T.J*_*der 108

是的,你拥有的是对的.一些说明:

  • bar是在每次调用函数时创建的foo,但是:
    • 在现代浏览器上,这是一个非常快速的过程.(有些引擎可能只编译一次代码,然后每次使用不同的上下文重用该代码; Google的V8引擎[在Chrome和其他地方]在大多数情况下都会这样做.)
    • 根据具体bar情况,某些引擎可能会确定它们可以"内联"它,完全取消函数调用.V8这样做,我确信它不是唯一的引擎.当然,如果它不改变代码的行为,它们只能这样做.
  • bar每次创建的性能影响(如果有的话)在JavaScript引擎之间会有很大差异.如果bar是微不足道的,它将从不可检测到相当小的变化.如果你没有foo连续多次调用(例如,从mousemove处理程序),我不会担心它.即使你是,如果我在较慢的发动机上看到问题,我只会担心它.这是一个涉及DOM操作的测试用例,它表明有一个影响,但是一个微不足道的影响(可能被DOM的东西淘汰).这是一个测试案例进行纯计算,显示出更高的影响,但坦率地说,我们正在谈论微秒的差异,因为即使在微秒发生的事情上增加92%仍然非常非常快.直到/除非你看到现实世界的影响,否则不必担心.
  • bar只能从函数中访问,并且它可以访问该函数调用的所有变量和参数.这使得这个非常方便.
  • 请注意,因为您已经使用了函数声明,所以放置声明的位置无关紧要(顶部,底部或中间 - 只要它位于函数的顶层,而不是在流控制语句中,这是语法错误),它在第一行逐步代码运行之前定义.

  • @rkw:创建函数一次,就像robrich的回答一样,是一种有效的方法,可以避免在每次调用时创建它的成本.你失去了这样一个事实:`bar`可以访问调用`foo`的变量和参数(任何你想要它操作的东西,你必须通过它),这可能会使事情变得复杂,但在性能方面 - 在你看到实际问题的危急情况下,你可能会像这样重构,看看它是否解决了问题.不,使用`foo.prototype`不会真正有用(一方面,'bar`将不再是私有的). (4认同)
  • @ahmoo:对于JavaScript性能,答案几乎总是:它取决于.:-)这取决于运行它的引擎以及你调用`foo`的频率.如果你没有连续几次调用`foo`(例如,不是在`mousemove`处理程序中),那么我根本不会担心它.请注意,某些引擎(例如V8)无论如何都会内联代码,完全取消函数调用,前提是这样做不会以可从外部检测到的方式改变正在发生的事情. (2认同)

Mic*_*ent 14

这就是闭包的用途.

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;
})();
Run Code Online (Sandbox Code Playgroud)

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

这种方式无法从外面进入酒吧.


rob*_*ich 8

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

        if (/*some condition*/) {
            // perform task 1
            bar();
        }
        else {
            // perform task 2
            bar();
        }
    };
}());
Run Code Online (Sandbox Code Playgroud)

闭包保持bar()包含的范围,从自执行匿名函数返回新函数设置更明显的范围foo().匿名自执行函数只运行一次,因此只有一个bar()实例,每次执行foo()都会使用它.


Guf*_*ffa 5

是的,那很好。

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

如果您测试此代码:

function test() {

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

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

}

test();
test();
Run Code Online (Sandbox Code Playgroud)

它会显示1212,不1222