什么是闭包
一个函数使用了它外面的变量,这种用法就是闭包。闭包是一个马后炮的总结。
闭包(closure) 指有权访问另一个函数作用域变量的函数.
举例
function fn() {
var num = 10;
function fun() {
console.log(num);
}
fun();
}
fn();
闭包: 我们fun 这个函数作用域 访问了另外一个函数,fn里面的局部变量 num
闭包造成内存泄露?
内存泄露是指你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
闭包里面的变量明明就是我们需要的变量(lives),所以不是内存泄露
为何有人说是?
因为 IE。IE 有 bug,IE 在我们使用完闭包之后,依然回收不了闭包里面引用的变量。
闭包作用
延申了变量的作用范围
function fn() {
var num = 0;
// function fun() {
//console.log(num);
// }
// return fun;
return function() {
console.log(num);
}
}
var f = fn();
f();
// 类似于
// var f = function() {
// console.log(num);
// }
// var f = function fun() {
// console.log(num);
// }
理解闭包、立即执行函数、异步和回调的关系
立即执行函数的作用
只有一个作用:创建一个独立的作用域。
这个作用域里面的变量,外面访问不到(即避免「变量污染」)。
这个作用不就恰恰是闭包所需要的吗!!!
所以之前的函数可以写成
异步+回调
什么是异步
同步:一定要等任务执行完了,得到结果,才执行下一个任务。
异步:不等任务执行完,直接执行下一个任务。相当于给前一个任务加个警报器,任务好了再告诉你去执行。
什么情况下需要用到异步?
如果几个任务互相独立,其中一个执行时间较长,那么一般就用异步地方式做这件事。
什么是回调
callback 就是(传给另一个函数调用的)函数。把括号里面的内容去掉,简化成:callback 就是一种函数。
具体来讲:
当一个函数 A 被作为参数传给另一个函数时 B,那么这个函数 A 就叫做回调(名词)。B 中调用 A 函数的过程,也叫做回调(动词)。
那回调有什么用呢?
回调的作用
回调通常用在获取「异步任务」的结果
之前异步的代码也可写成如下(为理解起见我简化了)
下面我们通过案例来更详细的介绍
<ul class="nav">
<li>你好11</li>
<li>你好22</li>
<li>你好33</li>
<li>你好44</li>
</ul>
<script>
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
// setTimeout(function() {
// console.log(lis[i].innerHTML); for为同步任务,先执行完for再去执行定时器中的异步任务(回调函数)这个时候i = lis.length
// }, 3000)
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(i);
// 这里在循环每个li的时候都同步创造了一个立即执行函数直接得到当时的li,与循环过程中的li达到同步效果,这就是闭包的一个运用
}
</script>
对于这个案例大家可以发现第一种方法会报错(注释部分 )就是因为同步与异步之间的关系,因为定时器,还有其他绑定的点击事件已经属于了异步,这导致了程序出错,我们希望在遍历li的同时,我们给每一个li都添加事件,因此不能直接加上定时器,此刻我们创建一个立即执行函数,这样就不存在异步,在每一个小li遍历的同时,我们也创建列一个立即执行函数,拿到了for作用域下的参数i,然后传递给另外一个定时器里面的函数,这就是闭包的一种体现,我们称为小闭包
我们在通过一个案例来加深理解
<ul class="nav">
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
</ul>
<script>
// 闭包应用- 点击li输出当前li的索引号
// 1.我们可以利用动态添加属性的方式
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
lis[i].index = i; //给每个i一个自定义属性
lis[i].onclick = function() {
console.log(this.index);
}
}
// 2.利用闭包的方式得到当前li的索引号
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建4个立即执行函数
// 立即执行函数也成为小闭包为立即执行函数里面任何一个函数都可以使用i的变量
(function(i) {
lis[i].onclick = function() {
console.log(i);
}
})(i)
}
// 这里我们发现实际上第二种方法存在浪费内存的情况,因为要执行4个函数,而且还是立即执行,只要当点击事件过后才会销毁
这与第一个案例非常相似请大家仔细体会,但是从第二个案例我们可以看出来第一种方法种由于事先已经给每一个小li定义好了index值,所以不影响我们后面的异步执行,而且也发现这种方法比下面闭包这种体现更为简便,所以要深刻理解闭包,