完全关闭的是什么?

vto*_*ola 0 javascript closures

我一直在阅读有关闭包和javascript的内容,我认为我得到了它,直到我尝试了这个:

var Object5 = function (param) {
    var x = 0;

    var squareBrace = function () {
        return '[' + param + x + ']';
    };

    this.toString = function () {
        x = x + 1;
        return squareBrace();
    };
};
Run Code Online (Sandbox Code Playgroud)

然后我运行了这段代码:

var counter = new Object5("Counter: ");
print("Has x:" + ('x' in counter));
print("Has f:" + ('f' in counter)); 
print("Can access x:" + (!!counter.x));   
print("Can Invoke f:" + (!!counter.f));   
print(counter.toString());
print(counter.toString());
print(counter.toString());
print(counter.toString());
print(counter.toString());
print(counter.toString());
Run Code Online (Sandbox Code Playgroud)

这就是我得到的:

Has x:false
Has f:false
Can access x:false
Can Invoke f:false
[Counter: 1]
[Counter: 2]
[Counter: 3]
[Counter: 4]
[Counter: 5]
[Counter: 6]
Run Code Online (Sandbox Code Playgroud)

我以为我会得到一个'TypeError',因为'x'和'f'将是'undefined',但后来我开始工作了.我认为闭包是为了启用这种行为,'x'和'y'是'私有'而没有闭包这些成员会被遗忘.

显然我弄错了,或者我错过了一些重要的事情.

可以请有人告诉我当时有什么关闭以及为什么这个工作?

谢谢.

dku*_*ppi 6

为了解决闭包的问题,​​我们必须讨论变量作用域在JavaScript中的工作原理.让我们从查看基本功能和相关问题开始:

function uniqueInteger(){     
     var counter = 0;
     return ++counter;
}

console.log(uniqueInteger()); // returns '1'
console.log(counter); // undefined
Run Code Online (Sandbox Code Playgroud)

此函数声明并为变量赋值0 counter,然后返回该值递增.我们可以看到,在函数执行时,可以在函数中访问变量计数器.但是,一旦函数返回,就不再定义变量,因为它是uniqueInteger()函数范围的一部分.我们也可以说counter变量是私有的 ; 只有该功能uniqueInteger()才能访问它.

但是如果我们想要"记住"变量的值以便下次调用此函数时,counter从1开始,而不是0,那么.一种方法是counter在函数外部声明变量,但之后它将不再是私有,然后其他函数可以更改计数器变量.

为了解决这个问题,我们需要看看函数是如何工作的.当调用一个函数时(如上所示),它的变量在返回后就不再存在了.但是在该函数的范围内,任何嵌套函数仍然可以访问其父函数的"私有"变量:

function uniqueInteger(){     
     var counter = 0; 

     // an example of nested function accessing the counter variable:
     return (function() { return ++counter })(); 
}

console.log(uniqueInteger()); // returns '1'
console.log(counter); // undefined
Run Code Online (Sandbox Code Playgroud)

*请注意,括号()的调用次数与定义的函数数量一致.嵌套函数是自调用的,外部函数用行调用console.log(uniqueInteger());因此相同的函数可以写成:

function uniqueInteger(){     
     var counter = 0; 

     // an example of nested function accessing the counter variable:
     return function() { return ++counter }; 
}

console.log(uniqueInteger()()); // returns '1'
console.log(counter); // undefined
Run Code Online (Sandbox Code Playgroud)

我们可以看到,嵌套函数仍然可以访问变量计数器,uniqueInteger()函数仍然返回"1".虽然uniqueInteger()返回后计数器变量仍然消失(我们稍后会解决这个问题),嵌套函数有权访问的事实counter使它能够对此变量执行操作,然后返回结果.无论什么时候uniqueInteger()被调用,都会存在相同的"范围链".非常简单地说这被称为词汇范围.

现在让我们谈谈在函数返回后变量计数器消失的问题.这里发生的是JavaScript的一个称为垃圾收集的功能.当函数及其变量不再使用时,它们会被"抛弃".但是如果存在对该函数的返回值的引用,(例如,如果该函数被分配给外部变量),则根据我们所讨论的"范围链",它会被"记住"以及其中的任何变量.以上:

function uniqueInteger(){    
     var counter = 0;
     return function() { return ++counter };
}

var uniqueInt = uniqueInteger();

console.log(uniqueInt()); // returns '1'
console.log(uniqueInt()); // returns '2'

console.log(counter) // still "undefined"
Run Code Online (Sandbox Code Playgroud)

所以发生了什么事?因为我们在外部变量中"保存"了嵌套函数的返回值,所以变量counter没有被垃圾收集,并且下次uniqueInt()调用时,counter仍然等于1.我们也可以说它的状态已经保存了.我们实现了我们的目标:我们创建了一个函数,它记住调用之间的变量,而不是在变量之外定义变量,将它们保密.

我们可以将上面的函数重写为函数定义表达式:

var uniqueInteger = (function(){     
     var counter = 0;
     return function() { return ++counter };
})();

console.log(uniqueInteger()); // returns '1'
console.log(uniqueInteger()); // returns '2'

console.log(counter) // still "undefined"
Run Code Online (Sandbox Code Playgroud)

请注意,仍然有两个函数,因此有两个相应的调用.只有这次外部函数是自调用的,才能将其返回值(而不仅仅是函数本身)保存到变量中uniqueInteger.否则变量的返回值uniqueInteger将是function () { return ++counter; }.上面描述的技术本质上就是闭包:使用函数内部的函数(*或对象)来操作内部值,这些值在调用之间保存它们的状态,同时保持它们的私有性.

*您还可以返回具有对外部函数值进行操作的函数的对象:

var uniqueInteger = (function(){     
     var counter = 0;
     return {
          value: function() { return counter },
          count: function() { return ++counter },
          reset: function() { counter = 0; return counter;}
     };

})();

console.log(uniqueInteger.value()); // 0
uniqueInteger.count();
console.log(uniqueInteger.value()); // 1
uniqueInteger.reset()
console.log(uniqueInteger.value()); // again 0
Run Code Online (Sandbox Code Playgroud)

此模式允许您拥有一个自包含的函数对象,该对象在其自己的私有变量上运行,不存在名称冲突或从外部恶意篡改的风险.

我也很难抓住闭包,但如果你继续阅读文献和SO答案,你最终会得到它.继续玩你的代码:)