我在w3schools上阅读这个关于应用于“反困境”的Javascript闭包的例子:
https://www.w3schools.com/js/js_function_closures.asp
在实际使用中,这个例子似乎几乎是无稽之谈。当我无法保护包含函数本身的变量“add”时,为什么要将变量“counter”包装到闭包中,以防止意外修改全局范围?
更具体一点......我有这个代码:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// the counter is now 3
W3schools在开始时说:“问题是,页面上的任何脚本[在全局范围内]都可以更改计数器,而无需调用add()。”......上面提到的闭包是一个解决方案。好。但是如何保护“添加”免于订阅?似乎这个解决方案只能解决问题,使这个例子实际上毫无用处。你能告诉我为什么这种副作用不成问题吗?
在w3schools给出的例子中,他们用上述闭包解决的问题是保护变量counter
不被直接修改。如果counter
在全局范围内,然后你定义了一个函数,比如add
来将计数器递增一些值,那么你所做的就是为代码的其他部分定义一个与counter
变量交互的接口。
当您在某些数据和代码库的其他部分之间定义接口时,您可能希望强制执行该接口。换句话说,如果你将counter
放在全局范围内,那么就没有办法强制使用你的add
函数,因为我可以这样做:counter += 7
就在全局范围内。
将counter
变量包装在闭包中允许您从全局范围隐藏counter
变量,这意味着修改计数器的唯一方法是通过add
函数。这通过防止直接修改add
变量的值来强制使用counter
。
所以这是一个简单但很好的例子,说明了如何使用闭包来创建私有变量。
它并没有用,因为它将counter
作为add
的财产。如果你需要保护add
,你可以将它包装在另一个封闭中。
var add = "something";
(()=>{
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
console.log(add());
console.log(add());
})();
console.log(add);
当然,在现实生活中,你应该首先避免使用像add
这样的超级通用名称,而在现实生活中,如果你需要你的方法来获得属性,你可能会使用OOP代替......
class counter{
constructor(){
this.counter = 0;
}
add(){
this.counter++;
}
}
var c = new counter();
c.add();
c.add();
console.log(c.counter);
如果您只是想确保永远不会重新分配变量名,您可以使用const
(JS“常量”)代替。
const add = (function(){
var add = 0;
return function(){ return ++add };
})();
try{
// this won't work
add = "something";
}catch(e){
console.log(e.message);
}
// Add is still a function here
console.log(add());
console.log(add());
console.log(add());
出于教育目的,通常将示例简单化。让他们减少分心。
该示例试图显示的是,您只能使用此闭包增加计数器。你不能减少,你不能到达计数器变量做任何其他事情。你当然可以抛弃整个功能,但这不是你偶然会做的事情。
意外地改变不在闭包中的变量的值很容易。
这是一个“问题较少”和“更有用”的例子。
var fibonacci = (function() {
var memo = {};
return function f(n) {
if (!(n in memo)) {
memo[n] = n === 0 || n === 1 ? n : f(n - 1) + f(n - 2);
}
return memo[n];
}
})();
Closure允许访问“memo”对象,该对象存储其以前的所有结果。