面试官为我什么是闭包?句句扎心(面试题)

413 阅读4分钟

前言

    为什么我们需要理解并且掌握闭包,且不说大道理,也且不说你要不要成为JavaScript高手 那你要不要面试
找工作对吧??扎心
     再者,对于任何一个前端或者JavaScript开发者来说,理解闭包可以看做是另一种意义上的重生。闭包是纯函数
 编程语言的一个特性,因为他 大大简化复杂的操作,所以很容易在一些JavaScript库以及其他高级代码中找到闭包
 的使用。
  一言以蔽之,闭包,你就得掌握。

你所理解的闭包?

 ==> 官方一点:函数形成一个私有作用域,保护里面的私有变量不受外界的干扰,这种保护机制称为'闭包'
 ==> 通俗一点:形成一个不销毁的私有作用域(私有栈内存)才是闭包

常见的闭包种类

 ==>闭包:柯理化函数(高大上的名词)
  function fn(){
    return function() 
  }
   }
 var f = fn();
 
 ==>闭包:惰性函数
 var utils = (function(){
     return {
     }
 })();

代码处处有闭包

 在经典的for循环中使用闭包
 for (var i=1; i<=5; i++) {  
 setTimeout( function timer() {
 console.log( i ); 
 }, i*1000 );
 }
  执行上述for循环,大家都知道输出6,因为在这个作用域中,我们只有一个i,所有的回调函数都是在这个for循环
  结束以后才执行的。但是根据作用域工作原理,尽管循环中五个函数是在各个迭代中分别定义,但是他们都被封闭在
  共享的作用域中 所以言归正传,我们需要使用闭包,在每一个循环中每一个迭代都让他产生一个闭包作用域。所以我
  们代码修改如下:
  for (var i=1; i<=5; i++) { (function() {
     setTimeout( function timer() {
         console.log( i ); 
    }, i*1000 ); 
      })();
 }

闭包项目实战应用

 ==>在做项目中为了保证JS的性能(堆栈内存的性能优化),应采取减少闭包的使用(不销毁的堆栈内存是消耗性能的)
 ==>闭包具有保护作用:保护私有变量不是受外界的污染,在做项目的时候可以将自己的代码封装在闭包中让全局变量
    变为私有变量,以防止和别人之间相互冲突造成全局变量污染
   (function(){
      var n = 12;
      function(){
      }
       //...
   })();
 ==>闭包具有'保存'作用:形成不销毁的栈内存,我们需要调用的时候,值是保存在栈内存,方便调用

小案例讲解(选项卡)

先简单写一丢丢样式

 <style>
        * {
            margin: 0;
            padding: 0;
            list-style: none;
        }

        .box {
            width: 400px;
            margin: auto;
        }

        .tabbox {
            width: 100%;
            height: 50px;
            display: flex;
        }

        .tab {
            flex: 1;
            text-align: center;
            line-height: 50px;
            cursor: pointer;
        }

        .tab.current {
            background: saddlebrown;
            color: aqua;
        }

        .body {
            width: 400px;
            height: 400px;
            font-size: 50px;
            text-align: center;
            border: 1px dashed skyblue;
            display:none;
        }

        .body.current {
            display: block;
        }
    </style>
 </head>
 <body>
    <div class="box">
       <ul class="tabbox">
           <li class="tab current">1</li>
           <li class="tab">2</li>
           <li class="tab">3</li>
       </ul>
           <div class="body current">1</div>
           <div class="body">2</div>
           <div class="body">3</div>
    </div>
 </body>
 </html>

JS代码实现

 //获取要操作的元素
    let tabs = document.getElementsByClassName('tab'),
    bodys = document.getElementsByClassName('body');
 // 先清除所有current类名,然后在点击的元素上加上,然后封装成函数
    function clearClass() {
    for( var i=0;i<tabs.length;i++){
        tabs[i].className='tab';
        bodys[i].className='body'
    }
}
 //之前都是采用this的属性来操作当前的元素 现在采用闭包的方式
 //闭包的方式有很多种下面给大家 说两种方式:

闭包方法一

   直接给循环体套一个自执行函数,形成一个私有作用域
    for( var i=0;i<tabs.length;i++){
    (function(n){
        tabs[n].onclick = function(){
        //点击tabs 让当前元素有current类名,其他元素没有
        clearClass();
        this.className='tab current'; 
        // 让body跟着改变,让对应索引的body添加current类名,其他的移除即可
        bodys[n].className='body current'
        }    
    })(i)
}     

闭包方法二

 思想 就是把for循环体的东西分离
 function fn(n){ 
     return function(){
        //点击tabs 让当前元素有current类名,其他元素没有
        // 先清除所有current类名,然后在点击的元素上加上,随之封装函数
        clearClass();
        this.className='tab current'; 
        // 让body跟着改变,让对应索引的body添加current类名,其他的移除即可
        bodys[n].className='body current'
        }    
    }
    for( var i=0;i<tabs.length;i++){
      tabs[i].onclick = fn(i)
    }