在面试中,我们经常会被问及闭包相关的问题。这也是一个经常使用,但容易被我们忽略的知识点。下面,本篇文章将用最通俗易懂的语言,带领大家揭开闭包的神秘面纱。让大家清楚的知道什么是闭包以及闭包有什么好处、应用在哪里、使用闭包需要注意的地方有哪些......
1. 闭包的特点
- 函数嵌套函数
- 内部函数可以引用外部函数的参数和变量
- 参数和变量不会被垃圾回收机制所收回
例如:
// 第一个函数
function aaa(a){
const b = 5;
return function bbb(){
alert(a)
alert(b)
}
}
aaa(6)
// 第二个函数
function aaa(a){
const b = 5;
}
aaa(6)
第二个函数执行完成后,参数和变量会立即回收,而第一个不会,因为第一个利用了js的闭包,避免了js中垃圾回收机制的触发。
2. js中的垃圾回收机制
function aaa(){
conat a = 5
function bbb(){
alert(a)
}
return bbb;
}
const c = aaa()
c()
3. 闭包有什么好处
- 希望一个变量长期驻扎在内存当中
- 避免全局变量的污染
let a = 1
function aaa(){
a++;
alert(a)
}
aaa()
aaa()
alert(a)
上面的写法,虽然可以弹出a的值,但是因为a是全局变量,会造成全局污染的问题,我们可以利用闭包来改写一下。
我们先把变量放在函数里面:
function aaa(){
let a = 1;
a++;
alert(a)
}
aaa()
aaa()
通过运行代码可以发现,弹窗的值一直是2,因为每一次调用方法,变量的值就被初始化了,下面我们把返回值改成一个函数。
function aaa(){
let a = 1;
return function(){
a++;
alert(a)
}
}
const b = aaa()
b()
b()
运行代码,观察弹窗的值,发现已经实现了我们的要求。不过上述的写法,还是有些繁琐,我们可以再改写一下。
const aaa = (function(){
let a = 1;
return function(){
a++;
alert(a)
}
})()
aaa()
aaa()
4. 闭包的用法
- 私有成员,模块化代码
- 在循环中直接找到元素的索引
const aaa = (function(){
let a = 1;
function bbb(){
a++;
alert(a)
}
function ccc(){
a++;
alert(a)
}
return {
b:bbb,
c:ccc
}
})()
aaa.b()
aaa.c()
下面来看一个很经典的面试题
<body>
<ul>
<li>列表第一项</li>
<li>列表第二项</li>
<li>列表第三项</li>
<ul>
<script>
const aLi = document.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++){
aLi[i].onclick = function(){
alert(i)
}
}
</script>
</body>
当我们点击列表时,会发现,弹窗的值并不是我们想象当中的0、1、2而是3,原因也很简单,for循环给列表增加了点击事件,但是并没有调用,等到调用的时候,i的值是最后的3。
下面是利用闭包特性的改造写法:
const aLi = document.getElementsByTagName('li')
// 做法1
for(var i=0;i<aLi.length;i++){
(function(i){
aLi[i].onclick = function(){
alert(i)
}
})(i)
}
// 做法2
for(var i=0;i<aLi.length;i++){
aLi[i].onClick = (function(i){
return function(){
alert(i)
}
})(i)
}