天天看点

深入浅出谈闭包

之前一直有归纳的习惯,只是一直未将其形成电子化。
   此次尝试,一来是为了对所学有所总结,二来为了抛砖引玉,以求更大的进步。
   话不多说,开启博客第一篇--《深入浅出谈闭包》
  此篇文章将分别从同步,异步,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以前也不存在块级作用域的概念.
  所以前面说块级作用域好像也不准确,姑且叫它仿作用域吧。
           

继续阅读