闭包概念及涉及面试

65 阅读3分钟

闭包的基础知识

变量作用域链

  • JS的两种变量:全局变量、局部变量
  • 局部变量作用域一般指函数内部及{},此外视为全局变量
  • JS的链式作用域:理解为函数嵌套,子函数调用父函数变量,会一级一级往上寻找变量,子函数变量对父函数不可见。

闭包概念

闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式,在一个函数内部创建另一个函数。之所以一个内部的函数可以访问其外部的变量,而且在其被返回或是调用时还可以访问,是因为这个内部函数的作用域链中包含外部函数的作用域。

闭包解决了什么

闭包可以缓存上级作用域,那么就使得函数外部打破了“函数作用域”的束缚,可以访问函数内部的变量。以平时使用的Ajax成功回调为例,这里其实就是个闭包,由于上述的特性,回调就拥有了整个上级作用域的访问和操作能力,提高了极大的便利。开发者不用去写钩子函数来操作上级函数作用域内部的变量了

闭包使用注意

  1. 闭包会使得函数中的变量都被保存在内存中不回收,会导致内存爆满,滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,退出函数之前,将不使用的局部变量全部删除。
  2. 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

闭包的使用场景

面试被问时 闭包随处可见,一个Ajax请求的成功回调,一个事件绑定的回调方法,一个setTimeout的延时回调,或者一个函数内部返回另一个匿名函数,这些都是闭包。简而言之,无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都有闭包的身影。

闭包使用Demo

几个 li 中 点击当前 li 返回当前的下标

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>
   
<Script>
    var li = document.querySelectorAll('li');
    for(var i=0;i<li.length;i++){
        li[i].onclick = function(){
            console.log(i)
        }
    }
</Script>

此时点击返回的都是4,因为i时用var定义的全局变量,点击触发时i已经为4,解决方法如下:

var li = document.querySelectorAll('li');
    for(var i=0;i<li.length;i++){
        (function(i) {
            li[i].onclick = function(){
                console.log(i)
            }
        })(i)
    }

在循环时把i传进闭包里面。

函数防抖(debounce):

就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

函数节流(throttle):

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。