1. 经典问题
在面试中会遇到一个经典的问题
// html部分
<ul>
<li>我是第1个li标签</li>
<li>我是第2个li标签</li>
<li>我是第3个li标签</li>
<li>我是第4个li标签</li>
</ul>
// js部分
let lis = document.querySelectorAll("li")
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
console.log(i);
}
}
// 点击每一个li标签浏览器打印
4、4、4、4
- 这四个 li 每一个点击打印的 i 都为4
- 因为使用var声明的变量没有块级作用域的概念,当 i 为 4的时候才结束条件,所以每次点击的时候获取的就是4
- 解决方案
- 使用let,因为let有块级作用域,相当于为每一个方法中存放了对应的 i
- 使用闭包与立即执行函数
// html代码不变 // js代码如下 // 这里面的 i 每循环一次,就立刻把 i 传给 j,这样每个事件中的 j, // 就是每次循环时传入的 i for (var i = 0; i < lis.length; i++) { (function(j) { lis[j].onclick = function () { console.log(j); } })(i) }
2. 什么是闭包
严格来讲,所有JavaScript函数都是闭包(这个不是我说的,犀牛书说的),因为函数外部无法访问内部的区域,利用作用域访问规则的不可逆性,构成一个单向空间
3. 闭包的特点
-
这里在介绍闭包的特点都是使用高级函数,也就是函数嵌套函数
-
在调用闭包函数时,用一个全局变量接收函数的调用,所以只要全局变量没销毁时,闭包函数也不会被销毁,其所在的外部函数也不会被销毁
function fn(){ return function(){} } // 这里demo保存的fn永远不会被销毁,除非用新的值覆盖demo let demo = fn()
-
普通函数在调用完后会被直接销毁
-
闭包函数只执行一次(父函数)
-
保护私有属性
-
在父函数被销毁时,子函数仍然保存着父级的变量和作用域