之前一直有归纳的习惯,只是一直未将其形成电子化。
此次尝试,一来是为了对所学有所总结,二来为了抛砖引玉,以求更大的进步。
话不多说,开启博客第一篇--《深入浅出谈闭包》
此篇文章将分别从同步,异步,es6块级作用域。自执行函数等方面来阐述。
1.什么是闭包?
理解闭包,从字面上其实就可以看出个究竟.
下面这个案例,我们想实现点击li获取响应的下标效果。
<div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
<script>
var list=document.querySelectorAll("li")
for(let i=0;i<list.length;i++){
list[i].onclick=function(){
console.log(i);
}
}
</script>
结果都是打印3,为什么呢?因为相当于执行了一个异步命令,先执行完同步命令,再执行异步命令,那个时候i都已经变成了3(加上时间可能更清楚),所以我们就要引入闭包,让i的值只在一定的作用域里面活动,并且在里面保存下来,就是闭合地包在一个块级作用域里面,这就是语义化的闭包。提到这里,大家可能会想到自执行函数,因为它具有独立的作用域,然后,我们继续探讨。
2.自执行函数和闭包的关系
不就是要让变量存在一个块级域里面嘛,然后我们就可以用自执行函数来试试
<script>
var list=document.querySelectorAll("li")
for(var i=0;i<list.length;i++){
list[i].onclick=function(i){
console.log(i);
}(i)
}
</script>
结果发现,虽然可以实现闭包的效果,但是也有了自执行的不足,还没等到点击,已经打印出0,1,2了。而且点击事件的存在好像也没了意义。
然后我们继续改进。就想到了return。分任务来执行操作,自执行函数的任务仅仅是为了传参形成闭包,而执行console.log()就交给点击事件。代码如下;
<script>
var list=document.querySelectorAll("li")
for(var i=0;i<list.length;i++){
list[i].onclick=function(i){
return function() {
console.log(i);
}
}(i)
}
</script>
虽然我们可以有多种传参的方式,但是原理都是一样的。
自执行函数和闭包具有一定的关联性,但是也有不同点,比如自执行函数不依赖与外界变量,而闭包依赖。
3. 闭包与垃圾回收机制
闭包里面的变量是局部变量,虽然可以在某种程度上可以避免全局变量污染的效果,
但是因为其生命周期是由外界变量确定的,只要外界有调用,其生命周期就会一直存在,
所以一直不会执行垃圾回收机制回收,会造成内存泄漏。
注:
因为var变量没有块级作用域,es6以前也不存在块级作用域的概念.
所以前面说块级作用域好像也不准确,姑且叫它仿作用域吧。