在百度百科中,有闭包的解释。
【百度百科】官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
【百度百科】闭包的特点:
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面向对象的编程中可以有很多的灵活运用,比如对象的封装与继承、对外接口的定义、任务列表对象的迭代等等。