在前端面试中时常会被问到闭包这个概念,那么闭包到底是什么呢?怎么去解读这个概念呢?
其实在别的编程语言里面有代码块级作用域这么一个概念,但是在JS中,不在一个代码块内的变量是可以被访问跟修改的,为了保护这些私有变量不受外界的改变,所以就有了闭包这个概念。
<ul id="myUl">
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
<script>
var myUl=document.getElementById("myUl");
var myLi=myUl.getElementsByTagName("li");
for(var i=0;i<myLi.length;i++){
myLi[i].onclick=function(){
alert(i+1);
}
}
这里的alert就会直接弹出4,因为for循环里面的那个匿名函数,是个异步事件,也就是说代码,顺序执行时,不会执行这个函数,只有当触发事件产生,才会执行这个函数,而此时的循环早已结束,此时的i已经为3,所以弹出4。此时应用闭包就可以解决问题,其实严格的说,只要是定义了一个函数,这个函数执行时就产生了闭包,闭包只是一种运行机制。
var myUl=document.getElementById("myUl");
var myLi=myUl.getElementsByTagName("li");
for(var i=0;i<myLi.length;i++){
;(function(i){
myLi[i].onclick=function(){
alert(i+1);
}
})(i)
}
var myUl=document.getElementById("myUl");
var myLi=myUl.getElementsByTagName("li");
for(var i=0;i<myLi.length;i++){
myLi[i].onclick=(function(i){
return function(){
alert(i+1);
}
})(i)
}
修改成上述两种方式都可以,函数在执行的时候,就会形成一个私有作用域,而这个作用域里面定义的参数,是不受外界干扰的。解决异步,即保护作用域里面的参数不受外界的干扰。
关于定时器以setTimeout为例
window.setTimeout(fn(),1000);
此时,会直接执行fn(),并将fn的返回值,加入到setTimeout的队列中,
而且setTimeout天生就是一个异步的。可看一下代码,可见 闭包是处理异步的非常好用的方法。
for(var i=0;i<myLi.length;i++){
window.setTimeout((function(i){
return function(){myLi[i].style.left=100*(i+1)+"px";
}
})(i),(i+1)*1000)
}
关于函数作用域的释放问题,一般情况下当一个函数执行完之后,这个函数所创建的作用域,将会消失,当这个函数再次执行时,会再 创建一个作用域,前后两个作用域不干扰。若这个函数中产生了闭包,且这个闭包被一个时间绑定,或者返回给了一个变量,那么这个函数创建的作用域将不会消失。
function fn(){
var i=0;
return function(){
console.log(i++);
}
}
var a=fn();
a();
a();
两次打印分别为0和1。是因为 返回的函数赋给了变量,在var a=fn()这一句中函数所创建的作用域就不会被释放。