在百度百科中,有闭包的解释。
【百度百科】官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
【百度百科】闭包的特点:
1.作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
2.一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
百度百科这么说有点绕,感觉意思也差不多,通俗地理解:
1、闭包就是一个封闭的包,它有对外(环境)变量的引用;
2、闭包一直存在于内存当中。
就这两点的理解就足够了,但由此可以有很多的应用,JavaScript语言的魅力也可以在此展现。
一个简单的例子就很能说明。
每篇CSDN的文章下面都有点赞按钮和踩的按钮,一般的写法是:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript中的闭包</title>
</head>
<body>
<label>点赞:</label><label id="lblPraise"></label>
<label>踩:</label><label id="lblTread"></label>
<br>
<button type="button" onclick="praise()">点赞</button>
<button type="button" onclick="tread()">踩</button>
<script>
praiseCount=0;//点赞的计数
treadCount=0;//踩的计数
function praise(){
praiseCount=praiseCount+1;
document.getElementById("lblPraise").innerHTML=praiseCount;
}
function tread(){
treadCount=treadCount+1;
document.getElementById("lblTread").innerHTML=treadCount;
}
</script>
</body>
</html>
这是可以实现的:
问题是这里声明了全局变量,容易造成变量污染和冲突,通常情况下是要避免的,那么要实现这个功能,应该怎样写呢?
把变量声明写在函数内行不行?如果这样的话,每次点击都会从0开始计数了,肯定不行。
这里就可以利用上面说的闭包的两个特点来实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript中的闭包</title>
</head>
<body>
<label>点赞:</label><label id="lblPraise"></label>
<label>踩:</label><label id="lblTread"></label>
<br>
<button type="button" onclick="btnPraise();console.log(praiseCount);">点赞</button>
<button type="button" onclick="btnTread();console.log(treadCount);">踩</button>
<script>
var btnPraise=(function(){
var praiseCount=0;//点赞的计数
return function praise(){
praiseCount=praiseCount+1;
document.getElementById("lblPraise").innerHTML=praiseCount;
};
})()
var btnTread=(function(){
var treadCount=0;//踩的计数
return function tread(){
treadCount=treadCount+1;
document.getElementById("lblTread").innerHTML=treadCount;
};
})()
</script>
</body>
</html>
实现效果:
这里的praiseCount和treadCount在外面是访问不了的,所以console.log(praiseCount);和console.log(treadCount);会报错。
这个例子就很明显地说明了闭包的作用和特点。
1、里面是可以访问外面的,但是外面访问里面必须通过外露的方法来实现;
2、闭包是驻留内存的,而一般函数如果没有引用执行完毕后就作为垃圾回收了。
这里需要弄清楚的是每个闭包都会牵扯到链式作用域的问题。
所谓的链式作用域就是在这个作用区域或者范围内,包裹在最内层的对象可以一层一层向上(或者说向外)访问父对象,也就是里面的对象可以访问外面的对象,但外面的对象是不能访问里面的对象。
通过例子可以很清楚地表现出这个概念。
<script>
var a=1;
function funA(){
var a=2;
return function funB(){
var a=3;
return function funC(){
return a;
}
}
}
var x=funA;
console.log(x()()());
</script>
上面的输出为3。
如果是:
<script>
var a=1;
function funA(){
var a=2;
return function funB(){
return function funC(){
return a;
}
}
}
var x=funA;
console.log(x()()());
</script>
上面的输出为2。
如果是:
<script>
var a=1;
function funA(){
return function funB(){
return function funC(){
return a;
}
}
}
var x=funA;
console.log(x()()());
</script>
上面的输出为1。
也就是它会一层一层地寻找这个变量所对应的值,找到了就输出,不再继续寻找,如果一直到最外层也找不到则会报错。
利用闭包的这两个特点,在JavaScript面向对象的编程中可以有很多的灵活运用,比如对象的封装与继承、对外接口的定义、任务列表对象的迭代等等。