13

我正在阅读有关答案的评论并看到此评论

[闭包] 并没有持久化 foo 的状态,而是创建了一个特殊的范围,其中包含 (1) 返回的函数和 (2) 在返回时引用的所有外部变量。这种特殊的作用域称为闭包。

好的,到目前为止一切顺利。现在这是我不知道的有趣部分:

举个例子……如果您在 foo 中定义了另一个未在返回函数中引用的 var ,则它不会存在于闭包范围内。

我想这是有道理的,但是除了内存使用/性能之外,这还有什么影响?

问题——如果范围内的所有变量都包含在闭包中,那将允许我做什么,而我不能用当前模型来做?

4

4 回答 4

11

我认为你太从字面上理解了这个评论。该评论只是说您无法在函数范围之外访问它(它不可公开访问),而不是在函数内根本不可用。无论如何,返回的函数都可以访问所有外部函数范围。如果内部函数不提供访问它的方法,您就无法在外部函数之外访问该范围。

例如,此表达式的计算结果为 4:

function testClosure(){
 var x = 2;
    return function(y){
        alert(eval(y));
    }

}

var closure = testClosure();

closure("x+2");  //4

http://jsfiddle.net/dmRcH/

尽管没有被直接引用,所以x仍然可用

进一步的研究

似乎 chrome 和 firefox 至少确实尝试优化这一点,如果您不提供任何引用x变量的方法,它不会显示为在调试器中可用。在闭包内使用断点运行此x命令在 Chrome 26 和 Firefox 18 上显示为不可用。

http://jsfiddle.net/FgekX/1/

但这只是内存管理细节,而不是语言的相关属性。如果有任何可能的方式可以引用该变量,它就会被传递,我怀疑其他浏览器可能不会以同样的方式优化它。编写规范总是比编写实现更好。在这种情况下,虽然规则确实是:“如果有任何可能的方式让您访问它,它将可用”。而且,不要使用 eval ,因为它确实会让你的代码无法优化任何东西

于 2013-04-04T02:28:43.210 回答
3

如果您在 foo 中定义了另一个未在返回函数中引用的 var,则它不会存在于闭包范围内。

这并不完全准确;该变量是闭包范围的一部分,即使它可能不会在函数本身内部直接引用(通过查看函数代码)。不同之处在于引擎如何优化未使用的变量。

例如,当您使用 DOM 元素时,已知闭包范围内未使用的变量会导致内存泄漏(在某些引擎上)。以这个经典的例子为例:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
}

来源

在上面的代码中,内存被泄漏(至少在 IE 和 Mozilla 中),因为el它是 click 处理函数的闭包范围的一部分,即使它没有被引用;这会导致无法删除的循环引用,因为它们的垃圾收集是基于引用计数的。

另一方面,Chrome 使用不同的垃圾收集器

在 V8 中,对象堆被分成两部分:创建对象的新空间,以及在垃圾回收周期中幸存的对象被提升到的旧空间。如果在垃圾回收周期中移动了一个对象,V8 会更新所有指向该对象的指针。

这也称为分代或临时垃圾收集器。尽管更复杂,但这种类型的垃圾收集器可以更准确地确定变量是否被使用。

于 2013-04-04T02:43:52.863 回答
1

JavaScript 没有固有的隐私感,所以我们使用函数范围(闭包)来模拟这个功能。

您引用的 SO 答案是模块模式的一个示例,Addy Osmani 很好地解释了学习 JavaScript 设计模式的重要性:

模块模式使用闭包封装“隐私”、状态和组织。它提供了一种包装公共和私有方法和变量的混合方式,防止碎片泄漏到全局范围内并意外地与另一个开发人员的接口发生冲突。使用这种模式,只返回一个公共 API,将闭包中的所有其他内容保持为私有。

于 2013-04-04T02:36:35.127 回答
1
    Please have a look below code:

    for(var i=0; i< 5; i++){            
                setTimeout(function(){
                    console.log(i);
                },1000);                        
            }

   Here what will be output? 0,1,2,3,4 not that will be 5,5,5,5,5 because of closure

   So how it will solve? Answer is below:

   for(var i=0; i< 5; i++){
            (function(j){     //using IIFE           
                setTimeout(function(){
                    console.log(j);
                },1000);
            })(i);          
        }

    Let me simple explain, when a function created nothing happen until it called so for loop in 1st code called 5 times but not called immediately so when it called i.e after 1 second and also this is asynchronous so before this for loop finished and store value 5 in var i and finally execute setTimeout function five time and print 5,5,5,5,5

Here how it solve using IIFE i.e Immediate Invoking Function Expression

   (function(j){  //i is passed here           
                setTimeout(function(){
                    console.log(j);
                },1000);
            })(i);  //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4

For more, please understand execution context to understand closure.

- There is one more solution to solve this using let (ES6 feature) but under the hood above function is worked

 for(let i=0; i< 5; i++){           
                setTimeout(function(){
                    console.log(i);
                },1000);                        
            }

Output: 0,1,2,3,4
于 2018-12-14T12:28:24.617 回答