完全关闭的是什么?

时间:2011-09-09 14:21:14

标签: javascript closures

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

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

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

    this.toString = function () {
        x = x + 1;
        return squareBrace();
    };
};

然后我运行了这段代码:

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());

这就是我得到的:

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]

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

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

可以请有人告诉我当时有什么关闭以及为什么这样做?

感谢。

2 个答案:

答案 0 :(得分:6)

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

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

console.log(uniqueInteger()); // returns '1'
console.log(counter); // undefined

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

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

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

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 调用外部函数。因此,相同的函数可以写为:

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 函数仍然返回“1”。虽然计数器变量在uniqueInteger()返回后仍然消失(我们稍后会解决该问题),但嵌套函数可以访问uniqueInteger()的事实使它能够对此变量执行操作,然后返回结果。每当调用counter时,都会存在相同的“范围链”。非常简单地说这被称为词汇范围

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

uniqueInteger()

那发生了什么?因为我们在外部变量中“保存”了嵌套函数的返回值,所以变量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" 没有被垃圾收集,并且下次调用counter时,计数器仍然等于1。也可以说它的状态得救了。我们实现了我们的目标:我们创建了一个函数,记住调用之间的变量,而不是在变量之外定义变量,将它们保密。

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

uniqueInt()

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

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

function () { return ++counter; }

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

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

答案 1 :(得分:3)

闭包是作用域技术。这是将一个范围中定义的参数拉入另一个范围的方法。当你这样做

var x = 1;

var f = function(){
   console.log(x); // you've just 'closed in' the variable x.
};

f();

x将在函数中可用,即使它未在函数中声明。

在您的特定实例中,有两个变量是封闭的:paramx。它们绑定到您定义的函数的范围。当您执行toString时,您正在以x递增关闭。 toString执行squareBrace时,该方法同时使用xparam。所以你在变量x周围有两个闭包,每个方法一个(它也在对象本身的范围内)