1. 闭包的定义

ECMAScript : 闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量

个人理解:函数A内部定义一个函数B,   B引用了A中的变量,这样产生了闭包 (根据官方的定义:B应该称作为闭包)

先给出一个闭包的简单实例:

 1 function A(){
 2     var i = 10;
 3     var B = function(){
 4         alert(i++);
 5     }      
 6     return B;       
 7 }
 8 
 9 var c = A();
10 c();  //10
11 c();  //11

在这个例子中 c --> B   B --> i  而 i 是A的变量, 这样的引用关系造成了垃圾回收不会对A进行操作, 所以A常驻内存中, i也就成为了只能通过B来修改而属于A的常驻内存的变量。

 

2. 闭包的特性及应用场景

# 让函数A中的变量能常驻内存, 而且A中的变量可以根据自己的意愿是否让外界访问或操作加限制。

例如: 我想写一个类似bean的函数, 函数中变量外部不可直接访问, 但函数提供了对变量操作的方法,供外部使用 (这就是封装)。

 1 function A(){
 2     var i = 10;
 3     this.getI = function(){
 4         return i;
 5     }      
 6     this.setI = function(t){
 7         i = t;
 8     }   
 9 }
10 
11 var c = new A();
12 c.setI(15);
13 alert(c.getI());

场景: 这就是js 编写类方式中的一种。

 

#脚本中对页面dom节点操作(这不仅实际中被用到, 面试时候也有类似这个的东东)

例如: 有时我们希望我们在非加载时期对dom进行事件添加或修改

 1 <script>
 2 function doit(){
 3     var ps = document.getElementsByTagName("p");
 4     for(var i=0; i<ps.length; i++){
 5         ps[i].onclick = function(){
 6             alert(i);
 7         };
 8     }
 9 }
10 </script>
11 <style>
12 p{background-color:orange;}
13 </style>
14 <body onload="doit()">
15     <p>123</p>
16     <p>123</p>
17     <p>123</p>
18     <p>123</p>
19     <p>123</p>
20     <p>123</p>
21 </body>

理想情况: 点击某个P时, 弹出当前P所在的索引值。   实际情况:弹出的都是 6 。 我们F12 调试时输出一下p的点击事件是什么,

function(){
    alert(i);
};

所以问题关键在于 i 的取值问题, 按照我们之前的思路: p的onclick事件是doit函数中的一个匿名函数的引用, 这就形成了闭包.  而这个匿名函数引用了外层doit中的变量 i ,函数A因为这种引用关系也就常驻内存了, 所以p的onclick事件每次引用匿名函数时都要取 doit的 i 值, 而 i 因doit常驻内存原因自身值循环达到了6, i的最终值也就是6. 最终你每次点击弹出的都是6.

解决: 只要能让对 i 的引用消失,或者说不存在闭包, 怎么做都行。 例如:在保留原有代码结构的基础上 for循环中可以这么写:

ps[i].onclick = (function(j){
    alert(j);
})(i);

 这类的js代码在网上有很多, 问一些执行的结果顺序, 只要理清他们的作用域或逻辑关系, 问题就简单化了。


# 匿名自执行函数(算是一种特例吧, 不写代码了)

 

3. 存在的问题:

一个技术在某方面突出,那么他一定是放弃了另一方面的能力。如同闭包, 就是放弃了空间, 换取代码的高效及结构简洁。所以在使用闭包时应该着重考虑内存。

最后吐槽一下: js本不复杂,只不过很多人想的太复杂了, 所以经常在网上看到一些不切实际而且代码逻辑很匪夷所思的集合体。代码嘛! 简单明了才最好。