JS中的闭包
闭包是JS中的一个重要的概念,在模块封装,保存变量中有着重要的作用,掌握闭包在前端开发中占着非常重要的角色。在了解闭包之前我们需要了解一下JS的垃圾回收机制和作用域。
一、垃圾回收机制
我们知道前端开发中,JS中最好不要存在过多的全局变量,这样可以避免全局变量的污染,和在创建新的变量或者函数中存在冲突的情况。所以有时候我们可能会看到如下所示:
var a = 1;
var b = a;
var a = null;
即将全局中不再使用的变量赋值为null。那大家知道为什么赋值给null,为什么不赋值给undefined?
这个就牵涉到JS的垃圾回收机制了,JS中的垃圾回收机制将会每隔一段时间,按照一定的算法和规则,找出不再使用的变量对象,进行回收,提高页面的性能,而a = null
正是让a失去赋值1的引用,此时a就变成不再使用的变量对象了,将会在下一次的垃圾回收中被销毁。
二、作用域
JS中存在两种作用域,全局作用域和函数作用域(第三种作用域eval因为我们平常很少用到,并且浪费计算机算力,所以我们很少使用,这里不做讨论)。
var a = 1;
function demo(){
var b = 2;
console.log(a);
}
demo(); //1
console.log(b); // b is not defined
上面函数存在两个作用域,一个是全局作用域window,一个是函数demo的作用域,按照规范是函数作用域可以访问全局变量,而全局变量是不能访问函数作用域的变量,所以在执行demo函数的时候,能够打印出1,而在打印b的时候会报错没有找到。
三、闭包
var a = 1;
function demo(){
var b = 2;
function c(){
console.log(b);
}
a = c;
}
demo();
a(); //2
上面代码执行流程为先执行demo函数,在内存中创建函数function(){console.log(b)}
,变量c指向该函数,通过a=c
,a也指向该函数,当demo函数执行完毕后,不再存在c变量,然后执行a()
,由于a已经获得函数c的引用,所以执行结果过打印出2。
闭包经典例子:
点击li标签,打印当前标签的索引。
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.getElementsByTagName("ul")[0];
var i = 0;
var lis = ul.getElementsByTagName("li");
var len = lis.length;
for(;i<len;i++){
(function(j){
lis[i].onclick = function (){
console.log(j);
}
})(i)
}
</script>
为什么需要使用立即执行函数(function(){})()
?
在执行for循环的过程中,点击事件并没有触发,相应的函数也不会执行。如果不使用闭包保存i
的话,当我们在点击li
标签的时候,此时for循环已经全部执行完毕,此时i=2,无论点击任何i
标签将会打印同样的结果。
使用立即执行函数的目的就是将每次for循环的i
值保存在当前函数中,这样我们在每一次的点击li
标签的时候就能获取当前li
的索引值了。