我正在尽最大努力理解JavaScript闭包。
通过返回一个内部函数,它可以访问直接父函数中定义的任何变量。
这对我有什么用?也许我还没完全搞清楚。我在网上看到的大多数示例都没有提供任何真实的代码,只是一些模糊的示例。
有人能告诉我一个闭包的真实用法吗?
比如这个吗?
var warnUser = function (msg) {
var calledCount = 0;
return function() {
calledCount++;
alert(msg + '\nYou have been warned ' + calledCount + ' times.');
};
};
var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();
假设,你想统计用户在网页上点击按钮的次数。
为此,您将在按钮的onclick事件上触发一个函数来更新变量的计数
<button onclick="updateClickCount()">click me</button>
现在有很多方法,比如:
你可以使用一个全局变量和一个函数来增加计数器:
Var计数器= 0;
函数updatecickcount () {
+ +计数器;
//用counter做一些事情
}
但是,问题在于页面上的任何脚本都可以更改计数器,而不需要调用updatecickcount()。
现在,你可能在考虑在函数内部声明变量:
函数updatecickcount () {
Var计数器= 0;
+ +计数器;
//用counter做一些事情
}
但是,嘿!每次调用updateClickCount()函数时,计数器再次被设置为1。
Thinking about nested functions?
Nested functions have access to the scope "above" them.
In this example, the inner function updateClickCount() has access to the counter variable in the parent function countWrapper():
function countWrapper() {
var counter = 0;
function updateClickCount() {
++counter;
// Do something with counter
}
updateClickCount();
return counter;
}
This could have solved the counter dilemma, if you could reach the updateClickCount() function from the outside and you also need to find a way to execute counter = 0 only once not everytime.
Closure to the rescue! (self-invoking function):
var updateClickCount = (function(){
var counter = 0;
return function(){
++counter;
// Do something with counter
}
})();
The self-invoking function only runs once. It sets the counter to zero (0), and returns a function expression.
This way updateClickCount becomes a function. The "wonderful" part is that it can access the counter in the parent scope.
This is called a JavaScript closure. It makes it possible for a function to have "private" variables.
The counter is protected by the scope of the anonymous function, and can only be changed using the updateClickCount() function!
一个关于闭包的更生动的例子
< >脚本
var updatecickcount =(函数(){
Var计数器= 0;
返回函数(){
+ +计数器;
. getelementbyid(“spnCount”)。innerHTML =计数器;
}
})();
> < /脚本
< html >
<按钮onclick = " updateClickCount ()“>点击我> < /按钮
<div>你点击了
<span id="spnCount"> 0 </span> times!
< / div >
< / html >
参考:JavaScript闭包
如果你熟悉面向对象意义上的实例化类的概念(即创建该类的对象),那么你就接近理解闭包了。
这样想:当你实例化两个Person对象时,你知道类成员变量“Name”在实例之间是不共享的;每个对象都有自己的“副本”。类似地,当你创建一个闭包时,自由变量(上面例子中的'calledCount')被绑定到函数的'实例'。
我认为你的概念上的突破有点受到这样一个事实的阻碍,即由warnUser函数(另外:这是一个高阶函数)返回的每个函数/闭包都绑定'calledCount'与相同的初始值(0),而通常在创建闭包时,将不同的初始化式传递到高阶函数中更有用,就像将不同的值传递给类的构造函数一样。
所以,假设当'calledCount'达到某个值时,你想结束用户的会话;您可能需要不同的值,这取决于请求是来自本地网络还是来自大的坏Internet(是的,这是一个人为的例子)。要实现这一点,您可以将不同的calledCount初始值传递给warnUser(即-3或0?)
文献的部分问题是用来描述它们的命名法(“词汇范围”,“自由变量”)。不要让它欺骗你,闭包比看起来要简单得多…初步证据;-)
是的,这是一个有用闭包的好例子。对warnUser的调用在其作用域中创建calledCount变量,并返回一个匿名函数,该函数存储在warnForTamper变量中。因为仍然有一个使用calledCount变量的闭包,所以在函数退出时不会删除它,所以每次调用warnForTamper()都会增加作用域变量并提醒该值。
我在Stack Overflow上看到的最常见的问题是,有人想要“延迟”使用在每次循环中增加的变量,但因为变量是有作用域的,所以对变量的每次引用都是在循环结束后,导致变量的结束状态:
for (var i = 0; i < someVar.length; i++)
window.setTimeout(function () {
alert("Value of i was "+i+" when this timer was set" )
}, 10000);
这将导致每个警报都显示相同的i值,即循环结束时它增加到的值。解决方案是创建一个新的闭包,一个变量的独立作用域。这可以使用一个立即执行的匿名函数来完成,该函数接收变量并将其状态存储为参数:
for (var i = 0; i < someVar.length; i++)
(function (i) {
window.setTimeout(function () {
alert("Value of i was " + i + " when this timer was set")
}, 10000);
})(i);
闭包的使用:
闭包是JavaScript最强大的特性之一。JavaScript允许函数嵌套,并授予内部函数对外部函数中定义的所有变量和函数的完全访问权(以及外部函数可以访问的所有其他变量和函数)。但是,外部函数不能访问内部函数内部定义的变量和函数。
这为内部函数的变量提供了一种安全性。此外,由于内部函数可以访问外部函数的作用域,如果内部函数能够在外部函数的生命周期之后继续存在,那么外部函数中定义的变量和函数将比外部函数本身的生命周期更长。当内部函数以某种方式对外部函数之外的任何作用域可用时,就会创建闭包。
例子:
<script>
var createPet = function(name) {
var sex;
return {
setName: function(newName) {
name = newName;
},
getName: function() {
return name;
},
getSex: function() {
return sex;
},
setSex: function(newSex) {
if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
sex = newSex;
}
}
}
}
var pet = createPet("Vivie");
console.log(pet.getName()); // Vivie
console.log(pet.setName("Oliver"));
console.log(pet.setSex("male"));
console.log(pet.getSex()); // male
console.log(pet.getName()); // Oliver
</script>
在上面的代码中,外部函数的name变量可以被内部函数访问,除了通过内部函数,没有其他方法访问内部变量。内部函数的内部变量充当内部函数的安全存储。它们保存“持久的”但安全的数据,供内部函数使用。这些函数甚至不需要赋值给变量,也不需要有名称。
详情请阅读此处。
我正在尝试学习闭包,我认为我创建的示例是一个实际的用例。您可以运行一个代码片段并在控制台中查看结果。
我们有两个不同的用户,他们拥有不同的数据。它们中的每一个都可以看到实际的状态并进行更新。
function createUserWarningData(user) {
const data = {
name: user,
numberOfWarnings: 0,
};
function addWarning() {
data.numberOfWarnings = data.numberOfWarnings + 1;
}
function getUserData() {
console.log(data);
return data;
}
return {
getUserData: getUserData,
addWarning: addWarning,
};
}
const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");
//USER 1
user1.getUserData(); // Returning data user object
user1.addWarning(); // Add one warning to specific user
user1.getUserData(); // Returning data user object
//USER2
user2.getUserData(); // Returning data user object
user2.addWarning(); // Add one warning to specific user
user2.addWarning(); // Add one warning to specific user
user2.getUserData(); // Returning data user object