我正在尽最大努力理解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();
这个帖子极大地帮助我更好地理解闭包是如何工作的。
从那以后,我自己做了一些实验,并提出了这个相当简单的代码,它可能会帮助其他人了解如何以实际的方式使用闭包,以及如何在不同的级别上使用闭包来维护类似于静态和/或全局变量的变量,而不会有它们被覆盖或与全局变量混淆的风险。
这将跟踪按钮点击,无论是在本地级别上的每个按钮,还是在全局级别上的每个按钮,计算每个按钮的点击,为单个数字做出贡献。请注意,我没有使用任何全局变量来完成此操作,这是练习的要点—拥有一个可以应用于任何按钮的处理程序,该按钮也对全局具有贡献。
专家们,如果我在这里犯了什么错误,请告诉我!我自己也在学习这些东西。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">
window.addEventListener("load" , function () {
/*
Grab the function from the first closure,
and assign to a temporary variable
this will set the totalButtonCount variable
that is used to count the total of all button clicks
*/
var buttonHandler = buttonsCount();
/*
Using the result from the first closure (a function is returned)
assign and run the sub closure that carries the
individual variable for button count and assign to the click handlers
*/
document.getElementById("button1").addEventListener("click" , buttonHandler() );
document.getElementById("button2").addEventListener("click" , buttonHandler() );
document.getElementById("button3").addEventListener("click" , buttonHandler() );
// Now that buttonHandler has served its purpose it can be deleted if needs be
buttonHandler = null;
});
function buttonsCount() {
/*
First closure level
- totalButtonCount acts as a sort of global counter to count any button presses
*/
var totalButtonCount = 0;
return function () {
// Second closure level
var myButtonCount = 0;
return function (event) {
// Actual function that is called on the button click
event.preventDefault();
/*
Increment the button counts.
myButtonCount only exists in the scope that is
applied to each event handler and therefore acts
to count each button individually, whereas because
of the first closure totalButtonCount exists at
the scope just outside, it maintains a sort
of static or global variable state
*/
totalButtonCount++;
myButtonCount++;
/*
Do something with the values ... fairly pointless
but it shows that each button contributes to both
its own variable and the outer variable in the
first closure
*/
console.log("Total button clicks: "+totalButtonCount);
console.log("This button count: "+myButtonCount);
}
}
}
</script>
</head>
<body>
<a href="#" id="button1">Button 1</a>
<a href="#" id="button2">Button 2</a>
<a href="#" id="button3">Button 3</a>
</body>
</html>
闭包有各种各样的用例。在这里,我将解释闭包概念的最重要的用法。
闭包可以用来创建私有方法和变量,就像面向对象的语言,如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,这样可以防止你的状态被客户端破坏。
这里我有一个闭包概念的简单例子,我们可以在我们的电子商务网站或其他许多网站上使用它。
我正在添加示例的JSFiddle链接。它包含一个由三种商品组成的小产品清单和一个购物车柜台。
JSFiddle
// Counter closure implemented function;
var CartCouter = function(){
var counter = 0;
function changeCounter(val){
counter += val
}
return {
increment: function(){
changeCounter(1);
},
decrement: function(){
changeCounter(-1);
},
value: function(){
return counter;
}
}
}
var cartCount = CartCouter();
function updateCart() {
document.getElementById('cartcount').innerHTML = cartCount.value();
}
var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
productlist[i].addEventListener('click', function(){
if(this.className.indexOf('selected') < 0){
this.className += " selected";
cartCount.increment();
updateCart();
}
else{
this.className = this.className.replace("selected", "");
cartCount.decrement();
updateCart();
}
})
}
.productslist{
padding: 10px;
}
ul li{
display: inline-block;
padding: 5px;
border: 1px solid #DDD;
text-align: center;
width: 25%;
cursor: pointer;
}
.selected{
background-color: #7CFEF0;
color: #333;
}
.cartdiv{
position: relative;
float: right;
padding: 5px;
box-sizing: border-box;
border: 1px solid #F1F1F1;
}
<div>
<h3>
Practical use of a JavaScript closure concept/private variable.
</h3>
<div class="cartdiv">
<span id="cartcount">0</span>
</div>
<div class="productslist">
<ul>
<li class="item">Product 1</li>
<li class="item">Product 2</li>
<li class="item">Product 3</li>
</ul>
</div>
</div>
闭包的另一个常见用途是将方法中的this绑定到特定对象,允许在其他地方调用它(例如作为事件处理程序)。
function bind(obj, method) {
if (typeof method == 'string') {
method = obj[method];
}
return function () {
method.apply(obj, arguments);
}
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);
每当鼠标移动事件触发时,都会调用watch .follow(evt)。
闭包也是高阶函数的重要组成部分,通过参数化不同部分,可以将多个相似函数重写为一个高阶函数,这是非常常见的模式。举个抽象的例子,
foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}
就变成了
fooer = function (x) {
return function (...) {A x B}
}
其中A和B不是语法单位,而是源代码字符串(不是字符串字面量)。
具体示例请参见“用函数简化我的javascript”。
闭包的使用:
闭包是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变量可以被内部函数访问,除了通过内部函数,没有其他方法访问内部变量。内部函数的内部变量充当内部函数的安全存储。它们保存“持久的”但安全的数据,供内部函数使用。这些函数甚至不需要赋值给变量,也不需要有名称。
详情请阅读此处。