解析面试常问题之JavaScript中的闭包概念及应用,顺便普及一下大家口中常说的内存泄漏问题

简介: JavaScript中的闭包是一个面试中经常被考到的问题,大家可能都对这个概念多多少少都有一些模糊的概念或者一点都不了解,那么今天就来给大家讲解一下。

01

引言


首先在这里我得说一下,要了解闭包一定要有作用域链的相关概念,这里我放上一篇文章,希望大家花3分钟看一下,了解一下作用域链,否则后面看起来会有点懵。作用域链讲解文章——从零开始讲解JavaScript中作用域链的概念及用途


02

闭包的定义


闭包: 是指有权访问另一个函数作用中的变量的函数,常见的闭包形式就是一个函数的内部再创建另一个函数。


想必这个概念听起来很懵,那我们接下来就来体验一个闭包吧。


03

体验闭包


来用一个小案例,来体验一下闭包是什么


    <!DOCTYPE html><html><head>    <meta charset="UTF-8">    <title></title></head>
    <body>
    <div class="show">0</div><button>增加</button>
    <script>    let show = document.querySelector('.show')    let btn = document.querySelector('button')    function func() {        let n = 0        btn.onclick = function () {            n ++            show.innerHTML = n        }    }    f()</script>
    </body></html>


    接下来是gif动图展示


    5f35ada16753f1d9642aa4bb24e682fb.jpg


    我们可以看到,在函数 fnc 中定义了一个变量 n,而我们在该函数内部,对按钮 btn 绑定了一个点击函数,该点击函数将变量 n + 1,然后展示在页面上。了解过作用域链的小伙伴会知道,当我们点击按钮的时候,处罚了点击的处理函数,此时该函数内部的作用域链是这个样子的


    ef6d6fed0e89e48cf44560c6de064642.png

    引用了变量 n ,首先在作用域链的第一个变量对象中寻找变量 n,没有找到;然后去作用域链的第二个里寻找变量 n,找到了,也就是在函数 func 内部定义的变量 n。所以该点击处理函数每次引用变量 n 时,都是从函数 func 内部去寻找的变量 n ,这也就是我们所说的,一个函数有权访问另一个函数内部的变量


    04

    使用闭包的注意事项


    上面我们了解了闭包的基本使用,那么我们再用一个例子来给大家介绍在使用闭包时容易犯的错误。


      function create() {    var arr = []
          for(var i=0; i<10; i++) {        arr[i] = function () {            return i        }    }
          return arr}
      let result = create()
      result[0]()         //返回10result[1]()         //返回10      ……result[9]()         //返回10


      这个例子就是在函数 create 中通过 for 循环定义10个匿名函数,每个函数都返回变量 i,最终将每个匿名函数保存到数组 arr 中并返回数组 arr,然后我们在收到数组 arr 后依次调用每个匿名函数,发现每个返回的都是数字10,而我们最初的目的是依次返回的是 0~9


      这是因为,我们调用匿名函数的时候需要返回变量 i ,而匿名函数内部没有该变量,所以去往下一个变量对象,也就是定义匿名函数时所处的函数环境 create 中寻找变量 i ,但此时的变量 i 已经通过循环变成了10,所以当我们调用每个匿名函数时,返回的全部都是10.


      为此,我们的代码可以写成这样


        function create() {    var arr = []
            for(var i=0; i<10; i++) {        arr[i] = (function (num) {            return function () {                return num            }        })(i)    }
            return arr}
        let result = create()
        result[0]()         //返回 0result[1]()         //返回 1      ……result[9]()         //返回 9


        这样做就直接在定义最内部的匿名函数时,把当前循环的变量 i 放在了最内部匿名函数外部的那个匿名函数内,这样的话,我们之后调用匿名函数时,寻找变量 i 就会从该匿名函数外部的那个匿名函数的变量对象中找到相应的变量。


        05

        内存泄露


        相信面试过的小伙伴都知道,在面试时,如果面试官问到你闭包,可能会跟你提一下内存泄漏。


        首先我要打假一个说法,很多人都说闭包会引起内存泄漏,这一半真一半假,因为只有在IE9之前才会因为闭包出现内存泄露的问题,所以以后千万别在别人面前说闭包就会引起内存泄露了哈。


        那么内存泄露到底是什么呢?


        简单来说一下,就是当一个闭包的作用域链内有一个HTML元素时,该元素就无法被垃圾回收机制给销毁。


        这里不懂JavaScript垃圾回收机制的小伙伴可以花2分钟看一下这篇文章,下面会讲解到,以防听不懂——JavaScript的垃圾回收机制,清除无用变量,释放多余内存,展现更好的性能


          function handle() {
              let element = document.querySelector('#app')        element.onclick = function () {        console.log(element.id)    }}


          在函数 handle 中,给HTML元素 element 创建了一个点击事件的匿名函数,该函数内部引用了变量 element ,所以变量 element 的引用次数为1,这样的话垃圾回收机制一直都不会清除该元素了,这就是一个内存泄露的情况。


          所以我们可以这样做,来解决内存泄露的问题


            function handle() {        let element = document.querySelector('#app')    let id = element.id        element.onclick = function () {        console.log(id)    }        element = null}


            将元素 elementid 值保存在一个变量 id 内,然后在该元素的点击处理事件中引用变量 id , 并且在最后通过把变量 element设置为 null ,以解除对DOM元素的引用,这样引用次数就变为0,而不再是1了,垃圾回收机制就可以对其进行清除了。


            06

            闭包的私有变量


            顾名思义,私有变量的意思就是说,闭包拥有自己的变量,别人都无法访问,无法使用。

            很明显,了解过作用域链就能清楚得知道,当函数调用后,作用域链是先从最内部开始,然后向外依次排列。所以只有内部访问外部变量的说法,而没有说外部访问内部变量的道理。


            就比如这个简单的例子


              let m = 1let n = 4
              function func() {    let n = 2    alert(m)                    //返回 1    return function () {        let m = 3        alert(n)                //返回 2    }}
              func()


              在该例子中可以看到,函数 func 本意想访问匿名函数中的变量 m 值为3,但却只访问到全局中的变量 m 值为1;而匿名函数就成功访问到了函数 func 内部定义的变量 n 值为2


              这就是通过闭包实现的私有变量的例子


              07

              总结


              • 闭包就是指有权访问另一个函数作用中的变量的函数,常见的闭包形式就是一个函数的内部再创建另一个函数。


              • 闭包就是为了隐藏变量,使外部无法访问到


              • 闭包可以将变量定义在内部,使内部拥有自己的变量,同时可以不污染全局变量
              相关文章
              |
              8天前
              |
              监控 负载均衡 JavaScript
              有哪些有效的方法可以优化Node.js应用的性能?
              有哪些有效的方法可以优化Node.js应用的性能?
              128 69
              |
              8天前
              |
              JavaScript 前端开发
              如何减少Node.js应用中的全局变量?
              如何减少Node.js应用中的全局变量?
              99 43
              |
              15天前
              |
              存储 JavaScript 前端开发
              |
              15天前
              |
              前端开发 搜索推荐 JavaScript
              如何通过DIY.JS快速构建出一个DIY手机壳、T恤的应用?
              DIY.JS 是一款基于原生 Canvas 的业务级图形库,专注于商品定制的图形交互功能,帮助开发者轻松实现个性化设计。适用于 T 恤、手机壳等多种商品场景。它自带丰富功能,无需从零构建,快速集成到项目中。通过创建舞台、添加模型、定义 DIY 区域和添加素材四个步骤即可完成基础用法。支持在线演示体验,文档详细,易上手。
              |
              2月前
              |
              数据采集 前端开发 JavaScript
              金融数据分析:解析JavaScript渲染的隐藏表格
              本文详解了如何使用Python与Selenium结合代理IP技术,从金融网站(如东方财富网)抓取由JavaScript渲染的隐藏表格数据。内容涵盖环境搭建、代理配置、模拟用户行为、数据解析与分析等关键步骤。通过设置Cookie和User-Agent,突破反爬机制;借助Selenium等待页面渲染,精准定位动态数据。同时,提供了常见错误解决方案及延伸练习,帮助读者掌握金融数据采集的核心技能,为投资决策提供支持。注意规避动态加载、代理验证及元素定位等潜在陷阱,确保数据抓取高效稳定。
              78 17
              |
              12月前
              |
              设计模式 JavaScript 前端开发
              js开发:请解释闭包(closure)是什么,以及它的用途。
              闭包是JavaScript中的关键特性,允许函数访问并操作外部作用域的变量,常用于实现私有变量、模块化和高阶函数。私有变量示例展示了如何创建无法外部访问的计数器;模块化示例演示了封装私有变量和函数,防止全局污染;高阶函数示例则说明了如何使用闭包创建能接收或返回函数的函数。
              79 2
              |
              12月前
              |
              自然语言处理 JavaScript 前端开发
              JavaScript基础知识:什么是闭包(Closure)?
              JavaScript基础知识:什么是闭包(Closure)?
              86 0
              |
              12月前
              |
              JavaScript 前端开发 Java
              学习Javascript闭包(Closure)
              学习Javascript闭包(Closure)
              65 0
              |
              JavaScript 前端开发 Java
              学习Javascript闭包(Closure)
              学习Javascript闭包(Closure)
              121 0
              |
              JavaScript 前端开发 Java
              javascript中的闭包closure详解
              javascript中的闭包closure详解

              热门文章

              最新文章

              推荐镜像

              更多