我一直在阅读有关闭包和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'是'私有'而没有闭包这些成员会被遗忘。
显然我错了,或者我错过了一些重要的事情。
可以请有人告诉我当时有什么关闭以及为什么这样做?
感谢。
答案 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
将在函数中可用,即使它未在函数中声明。
在您的特定实例中,有两个变量是封闭的:param
和x
。它们绑定到您定义的函数的范围。当您执行toString
时,您正在以x
递增关闭。 toString
执行squareBrace
时,该方法同时使用x
和param
。所以你在变量x
周围有两个闭包,每个方法一个(它也在对象本身的范围内)