写点什么

简单介绍一下闭包及它的一些应用场景

  • 2022 年 7 月 07 日
  • 本文字数:2170 字

    阅读完需:约 7 分钟

闭包

前言

我是在大一下,一个学姐面试的时候,第一次听闻闭包这个名词。当时听了一些学长的解释(虽然没怎么听懂),但是觉得,似乎很高级的样子。


如今时隔一年,跟闭包也打了不少交道,终于也能谈一谈自己对于它的理解了。

什么是闭包?

闭包是什么? ——可简单理解为:有权访问另一个函数作用域内变量的函数都是闭包。


先来看一个例子:


function drink() {  var beerName = "雪花啤酒";  let beerNum1 = 1;  const beerNum2 = 2;  var innerBeer = {    getBeer:function() {      console.log(beerNum1)      return beerName    },    setBeer:function(newBeer) {      beerName = newBeer    }  }  return innerBeer}var go = drink();go.setbeer("闷倒驴");go.getBeer();console.log(go.getBeer())
复制代码


当我们使用到 getBeer 与 setBeer 这两个函数的时候,它们内部是没有变量 beerNum1 与 beerName 的,那么它们这个时候会怎么办呢,学过作用域链的小伙伴可能会了解,它们这个时候就会到外层去找,也就是去 drink 函数中去找,找到了,于是乎可以使用了。


但是呢,问题又来了,drink 是个函数,它的作用域叫做函数作用域,在 drink 函数执行完,这个作用域是要被销毁的,那么这个时候,innerBeer 又该怎么做呢?


还是因为作用域链的原因,即使 drink 已经执行完了,当 innerBeer 再次调用其下的两个方法时,它用到的变量,是不能被销毁的,也就是说,beerNum1 与 beerName 被迫成为了钉子户


innerBeer:我只要在用,你就不能销毁!


而这,就是闭包。


闭包:这就是我

为什么说闭包容易产生内存泄漏?

首先我们要知道一个概念:什么是内存泄漏?


当内存中产生了不能被回收的变量,就叫内存泄漏


还是看刚刚的那个例子,var go = drink();


go 是一个全局变量,无论在什么时候都可以调用 setBeer 与 getBeer,因为我们将函数的返回值赋值给了它,只要页面没有销毁,它就会一直存在。而 setBeer 跟 getBeer 又会用到 beerNum1 与 beerName,也就是前文中我们提到的,钉子户理论。结合 JavaScript 的垃圾回收机制,我们现在知道,它们两个钉子户是不可能被清除掉的了。


这块存有 beerNum1 与 beerName 的内存,只有 setBeer 与 getBeer 才能访问到,别人是访问不到的。而它也不属于哪一个执行上下文中,所以它也销毁不掉。


所以就说,闭包容易产生内存泄漏的问题


只要页面不关闭,这个变量就一直在,导致存在内存泄漏的问题

为什么还要使用闭包?

张三(👀没错就是法外狂徒本三)就说了:闭包既然会产生内存泄漏的问题,那我们为什么还要去使用它呢?


还不是因为,我们在某些情况下,希望某些函数内的变量在函数执行后不被销毁(我也知道,好像有那么一丝剑意)。


张三又有疑问了:那为什么干脆不定义成全局变量呢?现在搞得你还回收不了。


如果创建全局变量的话,它很容易就会被污染,同名变量,或者被一些函数修改等。为了避免它被篡改,又想让它长时间保存,让他变得形似一个全局变量,可以随时去用,我们就会在这个时候,使用闭包!


请注意:闭包一定要慎用

闭包的应用

查了几个闭包的应用,学习一下 ing


  • 模仿块级作用域

  • 计时器的一个应用


  for (var i = 0; 1 < 10; i++) {        (function (j) {          setTimeout(function () {            console.log(j);          }, 1000 * j)        })(i)      }
复制代码


  • 打印一组 li 的下标


  for(var i = 0; i < lis.length; i++) {    (function(j) {      lis[j].onclick = function() {        alert(j)      }    })(i)  }
复制代码


  • 埋点计数器

  • 产品让做的网站分析的一种常用的数据采集方法


  function count() {    var num = 0;    return function() {      return ++num    }  }  var getNum = count();  // 第一个需要统计的地方  var getNewNum = count(); //第二个需要统计的地方   // 如果我们统计的是两个button的点击次数  document.querySelectorAll('button')[0].onclick = function() {     console.log('点击按钮1的次数:'+getNum());  }  document.querySelectorAll('button')[0].onclick = function() {     console.log('点击按钮2的次数:'+getNewNum());  }
复制代码


  • 柯里化

  • 把一个多参数的函数转化成单参数函数的方法,使得更灵活方便


  // 原函数 用来检验文本是否符合规范  // reg 传入的正则表达式  txt 需要被检测的文本  function check(reg,txt){    return reg.test(txt)  }  console.log(check(电话号码的正则,13923456789));  console.log(check(邮箱的正则,youxiang@163.com));    // 现如今  function nowCheck(reg){    return function(txt){      return reg.test(txt)    }  }  var isPhone = nowCheck(电话号码的正则)  console.log(isPhone('13923456789'))  var isEmail = nowCheck(邮箱的正则)  console.log(isEmail('youxiang@163.com'))
复制代码

总结:简单说一下闭包吧

闭包产生的原因


函数中的局部变量在函数执行完后会被销毁,有时候,我们不希望这个局部变量会被销毁,我们还想在外部进行持续的操作和访问,我们就会用到闭包这种方式。


为什么不创建一个全局变量来代替这个局部变量?


因为全局变量会被污染或者被修改。


闭包能够访问里面的变量,是由于作用域链,用到了函数嵌套中,内部函数能够访问父级函数作用域的变量这个理念。


闭包能够造成内存泄漏


页面不关闭,变量就一直在,不能被垃圾回收机制回收或者手动清除。

发布于: 刚刚阅读数: 3
用户头像

公众号:前端成长日记 2021.08.09 加入

还未添加个人简介

评论

发布
暂无评论
简单介绍一下闭包及它的一些应用场景_7月月更_是乃德也是Ned_InfoQ写作社区