什么是闭包
有权访问另一个函数作用域中的变量的函数。通俗地讲,一个函数有权去访问另一个函数作用域中的变量,这个函数就称为闭包。闭包函数在执行完以后,这个函数中还存在一部分在内存当中,没有被垃圾回收机制回收。
来个例子:
var test = function father() {
var data = 'from father';
return function son() {
return data;
}
}();
console.log(test()); //输出 'from father'
解析一下这段代码: 首先这里定义了一个 test 变量,这里 father 后面加了个(),说明他自执行了,return 了一个函数 son 赋值给到 test,那么现在 test 被赋的值是 son 这个函数,接下来执行 test 函数,也就是执行 son 函数,son 函数又将外层的在 father 函数内部的 data return 了出去,于是输出了 form father。也就是说,son 函数访问到了 father 函数内部的变量 data,形成了闭包。
为什么要用闭包
因为js的函数作用域的关系,一般情况下外部是访问不了函数内部的变量的,设计闭包结构就可以访问到函数内部的变量,还有就是闭包可以让变量长期保存在内存里,生命周期较长
闭包应用
for循环异步问题,函数防抖及函数节流问题,封装私有变量等等。
for循环异步问题:
先看一个例子(来自牛客网):
<ul>
<li>click me</li>
<li>click me</li>
<li>click me</li>
<li>click me</li>
</ul>
var elements = document.getElementsByTagName('li');
var length = elements.length;
for (var i = 0; i < length; i++) {
elements[i].onclick = function () {
alert(i);
}
}
点击之后,输出是 4,为什么不是分别的0,1,2,3 呢?
解析一下这段代码: 首先 for 循环是同步机制,onclick 是异步机制,这里 for 循环执行的时候,遇到了 onclick 事件,因为 onclick 是异步,于是把它放在异步队列中,继续执行 for 循环,直到 i = 4 了,开始依次执行异步队列的 onclick 函数,onclick 函数中 alert 了 i 这个变量,这个 i 变量呢,就是来自 for (var i = 0; i < length; i++) 中的 i,注意看,这个 i 是 全局变量,也就是说,onclick 函数 alert 的 i 是来自全局的变量 i,前面说到 i 已经是 4 了,所以实际上 alert 的 i 就是全局变量的 i 也就是 4 了。
使用闭包解决:
var elements = document.getElementsByTagName('li');
var length = elements.length;
for (var i = 0; i < length; i++) {
elements[i].onclick = function father (num) {
return function son () {
alert (num)
}
}(i)
}
输出是 0,1,2,3,这次达到目的了。
解析一下这段代码: 修改过后,按照之前的逻辑,for 循环先执行完,开始依次执行异步队列中的 onclick 函数,这里可以开始看 onclick 被赋值了什么,先看 father 函数,看到它后面有个 (i),说明这里它自执行了并且将那一次循环中的 i 作为参数传进了 father 函数中,father 函数执行后,return 了一个 son 函数,最终这个 son 函数赋值到 onclick,现在开始点击页面中的 li 标签,就会执行刚才赋值的 son 函数,son 函数会 alert num 这个变量,再看看 num 这个变量从哪儿来,num 这个变量来自于 函数 father 从外界接收的参数,也就是在 for 循环时传给 father 函数的参数 i,而这个 i 参数从哪来,是分别从 i = 0、1、2、3 的时候传进去的,最后输出也就是 0,1,2,3。
缺点:如果闭包滥用会造成内存泄露,影响页面性能,每次用完之后要立刻释放资源,把引用指针指向null