代码之家  ›  专栏  ›  技术社区  ›  nonopolarity

实际上什么时候创建了一个闭包?

  •  13
  • nonopolarity  · 技术社区  · 14 年前

    在以下情况下是否真的为 foo 但不是为了 bar ?

    案例1:

    <script type="text/javascript">
    
        function foo() { }
    
    </script>
    

    是一个只有全局作用域的范围链的闭包。

    <script type="text/javascript">
    
        var i = 1;
        function foo() { return i; }
    
    </script>
    

    与案例1相同。

    案例3:

    <script type="text/javascript">
    
        function Circle(r) {
            this.r = r;
        }
        Circle.prototype.foo = function() { return 3.1415 * this.r * this.r }
    
    </script>
    

    在这种情况下, Circle.prototype.foo (返回圆的面积)引用仅具有全局范围的闭包。(创建此闭包)。

    案例4:

    <script type="text/javascript">
    
        function foo() { 
            function bar() { 
            }
        }
    
    </script>
    

    在这里, 是一个只有全局范围的闭包,但是 酒吧 不是闭包,因为函数 在代码中没有调用,因此没有闭包 酒吧 调用,并且 将一直存在到 返回,然后结束 酒吧 将被垃圾回收,因为在任何地方都没有引用它。

    因此,当函数不存在、无法调用、无法引用时,那么闭包还不存在(从未创建过)。只有当函数可以被调用或被引用时,才真正创建了闭包?

    5 回复  |  直到 14 年前
        1
  •  8
  •   Alsciende    13 年前

    闭包是指某些函数代码中的自由变量被函数“context”绑定到某些值(闭包在这里比context更合适)。

    <script type="text/javascript">
        var i = 1;
        function foo() { return i; }
    </script>
    

    在这里, i 函数代码的自由变量 foo

    <script type="text/javascript">
        var i = 1;
        function foo() { return i; }
        foo(); // returns 1
        i = 2;
        foo(); // returns 2
    </script>
    

    现在要创建一个闭包,您必须提供一个值边界上下文:

    <script type="text/javascript">
    
        function bar() {
           var i = 1;
           function foo() { return i; }
           return foo;
        }
        bar(); // returns function foo() { return i; }
        bar()(); // returns 1
        // no way to change the value of the free variable i => bound => closure
    </script>
    

    总之,除非函数返回另一个函数,否则不能有闭包。在这种情况下 函数具有中存在的所有变量值绑定 当它 退出

    <script type="text/javascript">
    
        function bar() {
           var i = 1;
           function foo() { return i; }
           i = 2;
           return foo;
        }
        bar()(); // returns 2
    </script>
    

    关于你的例子:

    1. 案例1不是一个闭包,它只是一个函数
    2. 案例2不是闭包,而是另一个带有自由变量的函数
    3. this . 当函数作为对象的成员调用时,该对象被赋值给 . 否则,值 是全局对象。
    4. 返回 bar ,您将创建一个只包含“bar”及其值的闭包: function bar() {} .
        2
  •  1
  •   Delan Azabani    14 年前

    对。

        3
  •  0
  •   Zorf    14 年前

    在这些例子中,没有一个闭包被创建。

    3+8; ,创建一个数字,然后将其丢弃。

    闭包只是一个函数,它引用了其主体中创建环境中的变量,一个典型的例子是加法器:

    function createAdder(x) { //this is not a closure
        return function(y) { //this function is the closure however, it closes over the x.
            return y + x;
        }
    } //thus createAdder returns a closure, it's closed over the argument we put into createAdder
    
    var addTwo = createAdder(2);
    
    addTwo(3); //3
    
        4
  •  0
  •   Chris Henry    14 年前

    如果我可以提供一个关于何时以及如何创建闭包的模型(这个讨论是理论上的,实际上只要最终结果相同,解释器可以做任何事情):每当在执行过程中对函数求值时,都会创建闭包。然后闭包将指向执行的环境。当站点加载时,Javascript在全局环境中按从上到下的顺序执行。所有事件

    function f(<vars>) {
      <body>
    }
    

    将转换为带有和的闭包,并带有指向全局环境的指针。同时,一个参考 f 是在全局环境中生成的,指向这个闭包。

    f() f . 我们发现它指向一个结束。为了执行闭包,我们创建了一个新环境,它的父环境是闭包所指向的环境 ,即全球环境。在这个新环境中,我们将 真正的价值观。然后是身体的闭合 f f 需求将首先在我们刚刚创建的新环境中得到解决。如果这样的变量不存在,我们递归地在父环境中找到它,直到找到全局环境为止。任何变量 f

    现在,让我们看一个更复杂的例子:

    // At global level
    var i = 10;                  // (1)
    function make_counter(start) {
      return function() {
        var value = start++;
        return value;
      };
    }                            // (2)
    var count = make_counter(10);    // (3)
    count();  // return 10       // (4)
    count();  // return 11       // (5)
    count = 0;                   // (6)
    

    结果是:

    在点(1):来自 i 10 在全球环境部制造(在哪里 var i = 10;

    在点(2):用变量进行闭包 (start) 和身体 return ...; 它指向执行它的环境(全局)。然后就形成了一个联系 make_counter 我们刚刚创造了一个封闭。

    在第(3)点:发生了一些有趣的事情。首先我们找到了什么 制造计数器 与全球环境有关。然后我们执行关闭。因此,一个新的环境,让我们命名它 CE 制造计数器 start 在里面 运行关闭机构 制造计数器 在里面 function f() {} 相当于 var f = function() {}; ). 一个结束,让我们来命名它 count () (空列表)和正文 var ... return value; . 现在,这个闭包将指向它正在执行的环境,即。 总工程师 指出全球环境的新终结(为什么是全球性的?因为 var count ... 总工程师 不是垃圾收集因为我们可以到达 通过关闭 ,我们可以从全球环境的变量 制造计数器 .

    在第(4)点,更有趣的事情发生了。我们首先找到了与 这就是我们刚刚创建的闭包。然后我们创建一个新环境,它的父级是闭包所指向的环境,即 总工程师 ! 我们在这个新环境中执行闭包的主体。什么时候? var value = start++; 执行时,我们搜索变量 开始 从当前环境开始,依次向上移动到全局环境。我们发现了 总工程师 . 我们增加这个值 最初 10 11 开始 在里面 总工程师 . 当我们遇到 var value value 只需在执行变量的环境中创建一个变量。所以一个协会 价值 11 return value; ,我们查找 价值 和我们寻找的一样 开始 . 结果我们在当前环境中找到了它,因此我们不需要查看父环境。然后返回这个值。现在,我们刚刚创建的新环境将被垃圾回收,因为我们再也不能通过任何来自全局的路径到达这个环境。

    开始 ,我们发现值是 11 而不是 10 总工程师 ).

    在第(6)点,我们重新分配 计数 计数 反过来,我们再也找不到通向环境的道路 总工程师 . 因此,这两个都将被垃圾回收。

    P、 对于熟悉LISP或Scheme的用户,上面的模型与LISP/Scheme中的环境模型完全相同。

    P、 哇,一开始我想写一个简短的答案,但结果却是这个庞然大物。我希望我没有犯明显的错误。

        5
  •  0
  •   nonopolarity    9 年前

    事实上,经过几年的JavaScript使用和相当深入的研究,我现在有了一个更好的答案:

    每当一个函数出现时,就会创建一个闭包。

    因为函数只是一个对象,所以我们可以更准确地说,每当函数对象被实例化(函数实例出现),就会创建一个闭包。

    所以,

    function foo() { }
    

    当JS运行完上面的行时,已经有一个闭包,或者

    var fn = function() { };
    

    或者

    return function() { return 1; };
    

    为什么?因为闭包只是一个带有作用域链的函数,所以在上面的每一种情况下,都存在一个函数(它是存在的)。你可以调用它(调用它)。它也有一个范围。所以在我最初的问题中(我是OP),每个案例1到4,都有一个闭包,在每一个案例中。

    案例4是一个有趣的案例。在代码运行之后,由于 foo() 出现了,但是 bar() 还不存在(没有 福() ),所以只创建了一个闭包,而不是两个。