MDN解释:
闭包是使用被作用域封闭的变量,函数,闭包等执行的一个函数的作用域。通常我们用和其相应的函数来指代这些作用域。(可以独立访问数据的函数)
首先这是个错误的说法:闭包就是 在一个作用域中可以访问另一个作用域的变量。
一个函数嵌套一个函数,在嵌套的函数中访问到了外部函数(作用域)中的变量或者函数,就会发生闭包现象
例子:
function fn(){
var n= 10;
return n;
}
fn();
// 注意我们在看是否产生闭包的时候,最好通过断点调试的方式来查看当前是否产生闭包Closure (fn)。通过调试,没有出现Closure 的字眼。说明没有发生闭包。
当这个函数结束的时候,就会立即释放函数的作用域,作用域中的全局变量n=10也就被销毁了
function fn() {
var n = 10;
return function() {
return n;
};
}
var f = fn()// 因为fn的返回值是一个函数 所以必须用一个函数来接收
console.log(f());
在这个函数中,函数在执行完毕之后,返回了一个函数,而在返回的函数中又访问了父作用域中的一个变量。所以当我们在调用完函数fn()
的时候,函数的作用域并没有被销毁。因为变量n 还有人f()
会去访问。所以闭包延展了函数的作用域
闭包的应用
需求,在点击页面中的所有li
的时候,输出当前li的索引
<ul id="heros">
<li>01</li>
<li>02</li>
<li>03</li>
<li>04</li>
<li>05</li>
</ul>
<script>
var heros = document.getElementById("heros");
var list = heros.children;
// console.log(list.length)//5
for (var i = 0; i < list.length; i++) {
var li = list[i];
li.onclick = function() {
console.log(i);
};
}
</script>
以上的代码结果并不符合需求,不论点击哪一个li 都显示的是5。而不是对应的索引数。
我们知道js的执行机制:代码从上往下执行。遇到定时器或者函数的时候,先把这些复杂类型的数据放到栈中保存,等到其他的代码都执行完毕的时候,才会将栈中的代码push出,一件一件的进行执行。li的点击事件在一开始的时候并没有执行,而是在用户点击的时候才执行。等待执行点击事件的时候,循环早已经执行完了。所以最后i的值是5,i是全局变量。解决方法如下:
//方式一:
var heros = document.getElementById("heros");
var list = heros.children;
for (var i = 0; i < list.length; i++) {
var li = list[i];
li.index = i;
li.onclick = function() {
console.log(this.index);
};
}
// 方式二:
var heros = document.getElementById("heros");
var list = heros.children;
for (var i = 0; i < list.length; i++) {
var li = list[i];
(function(i){ //自调用函数
li.onclick = function() {
console.log(i);
};
})(i)
}
// 方法二用到了闭包。但是闭包延展了函数的作用域。会造成内存泄漏。一些内存不能用完就释放,会造成卡顿现象。所以在使用完闭包的时候要即使的清空因闭包产生的内存
// 再看一个例子
for (var i = 0; i <3; i++) {
(function(i){
setTimeout(function(){
console.log(i)
},0)
})(i)
}
// 每次循环都开启一个作用域,将当前循环的变量i保存在每个作用域中,再次访问的时候就会访问到当前循环的的作用域中,从而就把当前的循环变量i打印出来了。
<ul id="heros">
<li size="12" >01</li>
<li size="14" >02</li>
<li size="16" >03</li>
<li size="18" >04</li>
<li size="20" >05</li>
</ul>
<script>
function makeFun(size ){
return function(){
document.body.style.fontSize = size + 'px';
}
}
var heros = document.getElementById('heros')
var list = heros.children;
for(var i = 0; i< list.length;i++){
var li = list[i]
var size = btn.getAttribute('size')
li.onclick = makeFun(size)
}
</script>