我正在尽最大努力理解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();
闭包有各种各样的用例。在这里,我将解释闭包概念的最重要的用法。
闭包可以用来创建私有方法和变量,就像面向对象的语言,如java、c++等。一旦你实现了私有方法和变量,你在函数中定义的变量将不能被窗口对象访问。这有助于数据隐藏和数据安全。
const privateClass = () => {
let name = "sundar";
function setName(changeName) {
name = changeName;
}
function getName() {
return name;
}
return {
setName: setName,
getName: getName,
};
};
let javaLikeObject = privateClass(); \\ similar to new Class() in OOPS.
console.log(javaLikeObject.getName()); \\this will give sundar
javaLikeObject.setName("suresh");
console.log(javaLikeObject.getName()); \\this will give suresh
另一个关于闭包的现实例子:
创建index . html:
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Program with Javascript</title>
</head>
<body>
<p id="first"></p>
<p id="second"></p>
<button onclick="applyingConcepts()">Click</button>
<script src="./index.js"></script>
</body>
</html>
2)在index.js:
let count = 0;
return () => {
document.getElementById("first").innerHTML = count++;
};
})();
在本例中,当您单击一个按钮时,计数将在p#id上更新。
注意:您可能想知道这段代码有什么特别之处。检查时,您将注意到不能使用window对象更改count的值。这意味着你已经声明了私有变量count,这样可以防止你的状态被客户端破坏。
解释JavaScript闭包的实际用法
当我们在另一个函数中创建一个函数时,我们是在创建一个闭包。闭包功能强大,因为它们能够读取和操作其外部函数的数据。无论何时调用函数,都会为该调用创建一个新的作用域。在函数内部声明的局部变量属于该作用域,并且只能从该函数访问它们。当函数完成执行时,作用域通常被销毁。
这类函数的一个简单例子是:
function buildName(name) {
const greeting = "Hello, " + name;
return greeting;
}
在上面的例子中,函数buildName()声明了一个局部变量greeting并返回它。每次函数调用都会创建一个新的作用域和一个新的局部变量。在函数执行完成后,我们无法再次引用该作用域,因此它被垃圾收集。
但如果我们有一个指向范围的链接呢?
让我们看看下一个函数:
函数buildName(name) {
const greeting = "Hello, " + name + " Welcome ";
const sayName = function() {
console.log(问候);
};
返回sayName;
}
const sayMyName = buildName("Mandeep");
sayMyName ();//你好,曼迪普
本例中的sayName()函数是一个闭包。sayName()函数有自己的局部作用域(变量welcome),也可以访问外部(封闭)函数的作用域。在本例中,是来自buildName()的greeting变量。
在执行buildName之后,在本例中不会销毁作用域。sayMyName()函数仍然可以访问它,因此它不会被垃圾收集。但是,除了闭包之外,没有其他从外部作用域访问数据的方法。闭包充当全局上下文和外部作用域之间的网关。
在这里,我有一句想说好几遍的问候语。如果我创建一个闭包,我可以简单地调用该函数来记录问候语。如果我不创建闭包,我必须每次都传递我的名字。
没有闭包(https://jsfiddle.net/lukeschlangen/pw61qrow/3/):
function greeting(firstName, lastName) {
var message = "Hello " + firstName + " " + lastName + "!";
console.log(message);
}
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
使用闭包(https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/):
function greeting(firstName, lastName) {
var message = "Hello " + firstName + " " + lastName + "!";
return function() {
console.log(message);
}
}
var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");
greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();
如果你熟悉面向对象意义上的实例化类的概念(即创建该类的对象),那么你就接近理解闭包了。
这样想:当你实例化两个Person对象时,你知道类成员变量“Name”在实例之间是不共享的;每个对象都有自己的“副本”。类似地,当你创建一个闭包时,自由变量(上面例子中的'calledCount')被绑定到函数的'实例'。
我认为你的概念上的突破有点受到这样一个事实的阻碍,即由warnUser函数(另外:这是一个高阶函数)返回的每个函数/闭包都绑定'calledCount'与相同的初始值(0),而通常在创建闭包时,将不同的初始化式传递到高阶函数中更有用,就像将不同的值传递给类的构造函数一样。
所以,假设当'calledCount'达到某个值时,你想结束用户的会话;您可能需要不同的值,这取决于请求是来自本地网络还是来自大的坏Internet(是的,这是一个人为的例子)。要实现这一点,您可以将不同的calledCount初始值传递给warnUser(即-3或0?)
文献的部分问题是用来描述它们的命名法(“词汇范围”,“自由变量”)。不要让它欺骗你,闭包比看起来要简单得多…初步证据;-)
特别是在JavaScript(或任何ECMAScript)语言中,闭包在隐藏功能实现的同时仍然显示接口方面非常有用。
例如,假设您正在编写一个日期实用工具方法类,您希望允许用户通过索引查找工作日名称,但不希望他们能够修改您在底层使用的名称数组。
var dateUtil = {
weekdayShort: (function() {
var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return function(x) {
if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
throw new Error("invalid weekday number");
}
return days[x - 1];
};
}())
};
注意,days数组可以简单地存储为dateUtil对象的属性,但是脚本用户可以看到它,他们甚至可以根据需要更改它,甚至不需要您的源代码。但是,由于它被返回日期查找函数的匿名函数所包围,因此只能由查找函数访问,因此现在它是防篡改的。
假设,你想统计用户在网页上点击按钮的次数。
为此,您将在按钮的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闭包