当前位置: 首页 > news >正文

js 之让人迷惑的闭包 03

文章目录

  • 一、闭包是什么? 🤦‍♂️
  • 二、闭包 😎
  • 三、使用场景 😁
  • 四、使用场景(2) 😁
  • 五、闭包的原理
  • 六、思考
  • 总结
  • 一、 更深层次了解闭包,分析以下代码执行过程
  • 二、闭包
  • 三、闭包定义
  • 四、闭包导致的内存泄漏
  • 面试题



一、闭包是什么? 🤦‍♂️


当一个函数的返回值又是一个函数,作为返回值的那个函数叫做闭包
它利用了 作用域的嵌套 ,将原本的局部变量进化成私有变量的环境,叫闭包

  1. 闭包由两部分组成 :函数+可以访问的自由变量
  2. 一个普通的函数function,如果它可以访问外层作用于的自由变量,那么这个函数就是一个闭包

二、闭包 😎


	 const f = (function() {let a = 0;return function() {a++;console.log(a);};})();f();f();f();

        好兄弟,看!这是一个闭包函数,我们先来分析下这个函数

 	    ()()   他叫做立即运行的匿名函数,当一个匿名函数被括起来,然后再在后面加一个括号,这个匿名函数就能立即运行起来假设我们删去 这个匿名函数,f 是这个最外层的Function,当f()执行时,返回里层的function,f()()才能调用里层的function假设去掉,咱们接下往下走f 其实就是这个函数的返回值  return 出来的函数

        第一段代码执行 f 之后,发现a++了,因为里层的函数执行了,那为什么我再最下面console.log(a) 报错呢??

	......console.log(a);

         我们都知道let 声明的变量有作用域,不会变量提升,而对于内部的function来说,他是可以拿到a的,因为他们同一作用域(外层),a是个局部变量,所以在外层拿不到;

         这也是闭包的 特点将原本局部作用域中的局部变量,临时保存起来,不受外部的作用域的影响,可以被外部,反复使用,刚好说明了我们第二条定义,利用了 “ 作用域的嵌套 ”, 将原本的局部变量进化成私有变量的环境,叫闭包


请添加图片描述


三、使用场景 😁


         举两个小例子,让我们看看闭包的使用场景,看完以下两个例子,好兄弟你能不能想起来对闭包的定义,以及对闭包特点作用

 	  var allLi = document.querySelectorAll("li");for (var i = 0; i < allLi.length; i++) {console.log(i); //这里我每次都能拿到iallLi[i].onclick = (function(i) {return function() {console.log(i);};})(i);}// 当我点击的时候,真正触发的时retrun出来的function// 借助匿名函数帮助我们创建了一个作用域// 不管里层函数什么时候执行,都能拿到匿名函数里的变量for (let i = 0; i < allLi.length; i++) {console.log(i); //这里我每次都能拿到iallLi[i].onclick = function(i) {console.log(i)}}// 这个其实也是一个闭包,因为let 有作用域

函数的返回值又是一个函数
利用 作用域的嵌套 ,将 原本的局部变量 进化成 私有变量的环境 ,叫闭包


四、使用场景(2) 😁


        给不能传参内置函数回调参数 传参

     let a = "hello";setTimeout(function(a) {console.log(a);}, 1000);

三个问题

  1. setTimeout 是什么类型函数?
  2. 内置函数可以传参吗?
  3. 如果可以,怎么传?就像是上面那个案例吗?

直接说明:上个案例是错误写法,首先setTimeout是window内置的定时器函数,其次他不能传参,因为是内置!!!那有没有解决方案呢?yes!yes!yes! ✌

看答案之前先思考两个问题

  1. setTimeout 接收两个参数,第一参数是什么?
  2. 为什么上个案例返回undifine?
setTimeout(fn("hello"), 5000);function fn(a) {return function() {console.log(a);};}

说明:setTimeout是第一个参数是一个函数,因为是内置函数没有接收值,所以不能传参。之所以上个案例打印undifined,是因为上一个函数没有返回值,返回值的标志就是retrun。 既然这样,那我们把这个函数拿出来,而且retrun出去,哎嘿,巧了不是,意外写了个闭包,呐呐,这就是闭包使用场景二:给不能传参内置函数回调参数 传参 ✌ ,这一刻你有没有爱上闭包??

五、闭包的原理


         大篇说了很多闭包,我想好兄弟应该对闭包有很深的印象了吧?如果 `没有`, 那好的,是你出去还是我出去?👉,开个玩笑啦,让好兄弟没能理解是我的 “锅” ,烦请好兄弟评论留言,小妹我加以改正,一起进步,好兄弟如果有收获,还请多多点赞关注,你的大拇指就是小妹更新的动力,爱你 biu biu biu ~ 💕 🥰,言归正传,我们来看看闭包的原理
  1. 从计算机的角度分析:利用了计算机的垃圾回收机制。比如我们电脑桌上的回收站,他是先保存到一个临时空间,如果再使用就可以重复使用,确定不用最终删除
  2. js特性分析:利用了函数的定义作用域,和函数的执行上下文 (作用域链),不管函数在哪里执行,函数都可以操作自身定义域中存在的变量

六、思考


         我们在使用闭包时,可以在作用外部操作作用域内部的数据,有些我们也不知道什么时候调用了影响了结果,使用闭包可以不用定义很多全局变量,但是根据计算机的垃圾回收机制,有些不确定删除的变量会占用内存,所以也比较消耗内存,对于低版本的ie浏览器(ie8),可能会造成内存泄漏就会产生蓝屏卡死等啥的,好在现在浏览器会有内存保护机制,如果满了就会报错,我们修改后刷新下即可

总结


         通篇说了很多次闭包的概念,这里我还是想再强调一下,闭包不是一种固定的写法,它是一种环境,利用`作用域的嵌套`, 得到一种效果:原本的`局部变量`进化成`私有变量`,重复使用, 闭包能少用就少用,像递归,能不用就不用

请添加图片描述
你学废了吗?😂      谢谢阅读,谢谢点赞并关注(没错我就要道德绑架你!)







一、 更深层次了解闭包,分析以下代码执行过程



1.	function foo(){
2.		function bar(){
3.			console.log('吃饭饭')
4.		}
5.		return bar
6.	}
7.	var fn=foo()
8.	fn() 

请添加图片描述
原理:

  1. 调用栈是代码运行环境,在编译前会有个GO对象存放当前环境内置方法类等
  2. 在编译时,会生成一个全局执行上下文 GEC,会被关联到一个变量环境VO,全局执行上下文的VO 对应的是全局变量GO,在编译时,代码中的变量和函数都会被作为属性添加到VO中
  3. 不同的是,编译时变量为undi ,函数对象对应一个地址,同时也会为函数对象生成一个内存空间
  4. 当代码运行时,再执行函数对象时,会创建对应的函数执行上下文 FEC,同样也会被关联到一个变量环境VO,函数执行上下文的VO对应的是AO,当函数执行完毕,这个函数执行上下文也会弹出调用栈

代码:

  1. 开始分析执行过程,代码至上而下执行,先编译后执行
  2. 第一行 发现是个函数,GO 存放一个 foo:地址 ;第七行,发现是个变量, GO 存放一个 fn:undi
  3. 编译完成,开始执行代码
  4. 代码应该是从第7行开始执行的。 foo() ,发现foo对应的是个内存地址,找到内存地址,并创建一个函数执行上下文,因为foo里面包括了定义了一个函数bar,这是这个AO会定义一个bar:内存地址,且开辟一个bar的内存空间,。
  5. 并把函数执行上下文放到调用栈,将bar的内存地址返回给fn
  6. 这是fn 赋值为一个函数,当fn执行时,同样的道理,找到对应bar 的内存地址,并创建 bar 这个函数的函数执行上下文,因为函数里只有console操作,所以对应的AO 为空
  7. 当函数执行完毕,对应的函数执行上下文弹出调用框,

二、闭包

	
1. 	function foo(){
2.		var name='haha'
3		function bar(){
4			console.log('吃饭饭' , name)
5		}
6		return bar
7	}
8	var fn=foo()
9	fn() 
  1. 依旧开始分析执行过程,代码至上而下执行
  2. 第一行,发现是个函数,好的,GO添加一个属性 foo:内存地址
  3. 1-7 函数体就会跳过
  4. 第八行,发现声明了一个fn ,好的,GO添加一个属性 fn:unde
  5. 编译完成,开始执行代码
  6. 他会直接从第八行开始解析,foo()执行
  7. 在GO里找到foo,发现是个地址,找到对应的内存地址,foo() 所以创建了一个函数执行上下文,在foo里发现定义了一个name,这是这个函数执行上下文对应的AO里就会创建一个name:unde,又发现创建了一个bar,是个函数,所以对应创建了内存空间和函数执行上下文
  8. 编译好了以后开始执行,首先name赋值为haha,这时foo对应AO里的namew为haha ,并且把bar retrun出去
  9. 这时 GO里的fn 赋值为一个内存地址
  10. foo执行完毕,被销毁,弹出调用栈。
  11. fn执行,同样的道理,先找到对应的内存地址,创建函数执行上下文,编译bar,发现没有什么变量定义
  12. console.log(name),在bar这个AO里是没有任何值的,因为每个函数内存地址里存放当前作用域和父级作用域,所以根据父级作用域进行查找,此时的name值为Foo里的name
  13. 哎?? foo不是被销毁了吗??
  14. 这就是闭包 闭包由两部分组成 :函数+可以访问的自由变量




请添加图片描述



三、闭包定义

  1. 闭包由两部分组成 :函数+可以访问的自由变量
  2. 一个普通的函数function,如果它可以访问外层作用于的自由变量,那么这个函数就是一个闭包
  3. 从广义的角度来说 : JavaScript中的函数都是闭包
  4. 从狭义的角度来说 : JavaScript中一个函数,如果访问了外层作用于的变量,那么它是一个闭包
  5. 闭包在实现上是一个结构体(对象),它存储了一个函数和一个关联的环境 ( 相当于一个符号查找表 );
  6. 闭包跟函数最大的区别在于,当捕捉闭包的时候,它的 自由变量 会在捕捉时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行
 var name = '夏夏'function my(){console.log(name)}

        根据1234定义来说,当一个函数内部访问到外部的变量就成为闭包,其实上面例子就是一个闭包,我们无意间写了很多闭包,哈哈哈哈

四、闭包导致的内存泄漏

请添加图片描述
        我们经常会说闭包是造成内存泄露的,为什么呢?

  1. 比如在上面的案例中,如果后续我们不再使用 foo 函数了,那么该函数对象应该要被销毁掉,并且其引用着的父
    作用域AO也应该被销毁掉;但是目前因为在全局作用域下 fn 变量对 0xb00 的函数对象有引用,而 0xb00 的作用域中 AO 有用,所以最终会造成这些内存都是无法被释放的;
  2. 所以我们经常说的闭包会造成内存泄露,其实就是刚才的引用链中的所有对象都是无法释放,那么,怎么解决这个问题呢?
  3. 当将 foo 设置为 null 时,就不再对函数对象 0xb00 有引用,那么对应的 AO 对象也就不被销毁了,在 GO 的下一次检测中,它们就会被销毁掉;直接把家连根拔起
foo = null

面试题

为什么会有内存泄漏

相关文章:

js 之让人迷惑的闭包 03

文章目录 一、闭包是什么&#xff1f; &#x1f926;‍♂️二、闭包 &#x1f60e;三、使用场景 &#x1f601;四、使用场景&#xff08;2&#xff09; &#x1f601;五、闭包的原理六、思考总结一、 更深层次了解闭包&#xff0c;分析以下代码执行过程二、闭包三、闭包定义四、…...

10月10日上课内容 Docker--harbor私有仓库部署与管理

Docker--harbor私有仓库部署与管理 ------------------ 1、搭建本地私有仓库 ------------------------------ #首先下载 registry 镜像 docker pull registry #在 daemon.json 文件中添加私有镜像仓库地址 vim /etc/docker/daemon.json { "insecure-registries"…...

Java 序列化和反序列化为什么要实现 Serializable 接口

第一、序列化和反序列化 序列化&#xff1a;把对象转换为字节序列的过程称为对象的序列化. 反序列化&#xff1a;把字节序列恢复为对象的过程称为对象的反序列化. 第二、什么时候需要用序列化和反序列化呢? 当我们只在本地JVM里运行下Java实例, 这个时候是不需要什么序列化和…...

vite+vue3+ts中使用require.context | 报错require is not defined | 获取文件夹中的文件名

vitevue3ts中使用require.context|报错require is not defined|获取文件夹中的文件名 目录 vitevue3ts中使用require.context|报错require is not defined|获取文件夹中的文件名一、问题背景二、报错原因三、解决方法 一、问题背景 如题在vitevue3ts中使用required.context时报…...

C#(Csharp)我的基础教程(四)(我的菜鸟教程笔记)-Windows项目结构分析、UI设计和综合事件应用的探究与学习

目录 windows项目是我们.NET学习一开始必备的内容。 1、窗体类&#xff08;主代码文件窗体设计器后台代码文件&#xff09; 主窗体对象的创建&#xff1a;在Program类里面&#xff1a; Application.Run(new FrmMain());这句代码就决定了&#xff0c;当前窗体是项目的主窗体。…...

Flink: Only supported for operators

Exception in thread "main" java.lang.UnsupportedOperationException: Only supported for operators.at org.apache.flink.streaming.api.scala.DataStream.name(...

NSIDC定义的海冰相关概念

文章目录 相关概念Matlab绘图结果展示 相关概念 NSIDC 表示 “National Snow and Ice Data Center”&#xff0c;即美国国家雪和冰数据中心。NSIDC 是一个位于美国科罗拉多大学波尔得分校的研究中心&#xff0c;致力于收集、管理和分发全球雪和冰的科学数据。 Matlab绘图 cl…...

【码银送书第八期】《Python数据挖掘:入门进阶与实用案例分析》

摘要&#xff1a;本案例将主要结合自动售货机的实际情况&#xff0c;对销售的历史数据进行处理&#xff0c;利用pyecharts库、Matplotlib库进行可视化分析&#xff0c;并对未来4周商品的销售额进行预测&#xff0c;从而为企业制定相应的自动售货机市场需求分析及销售建议提供参…...

微信小程序底部tabBar不显示图标

现场还原 在设置微信小程序底部tabBar导航图标时&#xff0c;无论如何操作均无法显示在界面上 解决思路 问题1 图标类型 一开始以为不支持png类型&#xff0c;但查看官方API仅提示ICON尺寸大小 打开其他项目可以正常展示&#xff0c;排除图标类型问题 iconPath string 否 …...

PostgreSQL基操之角色、表空间、数据库与表

PostgreSQL基操之角色、表空间、数据库与表 角色创建与管理表空间创建与管理数据库创建与管理表创建与管理 角色创建与管理 PostgreSQL数据库里没有User的概念&#xff0c;只有Role的概念。有的Role可以用于登录数据库&#xff0c;这些Role与其他数据库中的用户等价。 --创建…...

【算法|滑动窗口No.1】leetcode209. 长度最小的子数组

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…...

11_博客管理系统_实现过程

项目初始化 创建项目文件夹进入文件夹&#xff0c;执行 npm init -y 命令安装 express 和 mongoose&#xff0c;npm install express mongoose创建项目入口文件&#xff0c;app.js 或 index.js在 app.js 中进行项目搭建配置网站的路由配置网站静态资源目录 配置静态页面 配置…...

安防视频监控平台EasyCVR集成到ios系统不能播放是什么原因?如何解决?

视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#…...

hutool实现文件上传与下载

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version></dependency> 文件上传需要创建一个表 Autowiredprivate SysFileInfoMapper sysFileInfoMapper;Value("${ty.…...

vue3学习源码笔记(小白入门系列)------provide和 inject 跨层级数据传递原理

目录 前言provideinject 总结 前言 需要从父组件向子组件传递数据时&#xff0c;会使用 props。对于层级不深的父子组件可以通过 props 透传数据&#xff0c;但是当父子层级过深时&#xff0c;数据透传将会变得非常麻烦和难以维护。 而依赖注入则是为了解决 prop 逐级透传 的问…...

【Python深度学习】目标检测和语义分割的区别

在计算机视觉领域&#xff0c;语义分割和目标检测是两个关键的任务&#xff0c;它们都是对图像和视频进行分析&#xff0c;但它们之间存在着明显的区别。本文将通过图像示例&#xff0c;详细阐述语义分割和目标检测之间的差异。 一、基本概念 1.1 语义分割&#xff08;Semantic…...

取消加考!自考专业调整,2026年起执行新计划!

就在2023年10月7日&#xff0c;广东省教育考试院发布《关于广东省高等教育自学考试专业调整有关事项的通知》&#xff0c;自学考试迎来新变化&#xff0c;本次专业调整政策性强&#xff0c;涉及面广&#xff0c;持续时间长&#xff0c;一起来看看具体说明~ 关于广东省高等教育自…...

项目串讲(后端)要讲哪些东西?

刚进入一家公司&#xff0c;leader说给你几天时间&#xff0c;对咱们的公司的项目熟悉熟悉&#xff0c;做一个串讲吧。我很慌&#xff0c;没有串讲过&#xff0c;啥也不知道&#xff0c;不知道该怎么写&#xff0c;不知道讲出来leader满不满意&#xff0c;这些都是我在串讲前的…...

区块链技术在供应链管理中的创新应用

区块链技术以其独特的不可篡改和透明性特点&#xff0c;正在逐步改变供应链管理的传统模式。本文将探讨区块链技术在供应链管理中的创新应用及其带来的效益。 区块链技术的出现为许多行业带来了创新的可能&#xff0c;其中之一就是供应链管理。通过区块链技术&#xff0c;企业可…...

tcp/ip协议2实现的插图,数据结构2 (9 - 章)

&#xff08;20&#xff09; 20 九章1 IP选项处理 ip_dooptions &#xff08;21&#xff09;...

嵌入式Linux裸机开发(六)EPIT 定时器

系列文章目录 文章目录 系列文章目录前言介绍配置过程前言 前面学的快崩溃了,这也太底层了,感觉学好至少得坚持一整年,我决定这节先把EPIT学了,下面把常见三种通信大概学一下,直接跳过其他的先学移植了,有些太多了内容。 介绍 EPIT(Enhanced Periodic Interrupt Timer…...

如何批量导出文件名?

如何批量导出文件名&#xff1f;在电商行业从事工作的一些同事可能经常会遇到这样的问题&#xff1a;需要将产品文件夹中的所有图片或产品名称导出到Excel工作表&#xff0c;在工作表中创建这些名称的超链接&#xff0c;并且可能会为每个产名称的后面填写一些相关信息&#xff…...

sort排序

后端返回字段&#xff0c;前端用sort进行正序排序 init() {this.formLoading true;GetAllGalleryTestLevelTypeInfos().then((res) > {res.data.sort((a,b)>{return a.serial_number-b.serial_number})this.data res.data;console.log( this.data," serial_number…...

缓存的力量:提升API性能和可扩展性

缓存是将频繁访问的数据或资源存储在临时存储位置(例如内存或磁盘)的过程&#xff0c;以提高检索速度并减少重复处理的需要。 缓存的好处 提高性能&#xff1a;缓存消除了每次从原始源检索数据的需要&#xff0c;从而提高了响应时间并减少了延迟。减少服务器负载&#xff1a;通…...

部署vSAN相关的名词解释 几句话概括

vSphere vCenter ESXI vSphere 和一些软件的集合 是一个软件的集合。他包括了 vCenter, ESXi 和 vSphere 等。所以&#xff0c;这些软件联合起来就是 vSphere。vSphere 不是一个你可以安装使用的软件。它只是一个包含其它组件的集合。 ESXi 一个装在物理机上的系统管理程序…...

【C++】进阶模板

模板进阶 一、非类型模板参数二、模板的特化1. 函数模板的特化2. 类模板特化3. 模板特化的应用 三、模板的分离编译1. 分离编译2. 模板的分离编译3. 解决方法 四、模板总结 我们在 初识模板 中已经初步接触过模板了&#xff0c;下面我们开始更进一步学习模板。 一、非类型模板…...

易点易动设备管理系统:打通采购管理的智能化设备管理解决方案

在现代企业的运营中&#xff0c;设备管理是一个关键的环节。传统的设备管理方法往往效率低下&#xff0c;导致设备故障频发、巡检和维修工作不协调&#xff0c;备件管理不规范。为了解决这些问题&#xff0c;我们引入了易点易动设备管理系统&#xff0c;它能够全面管理设备的生…...

成集云 | 管家婆ERP集成金蝶云星辰 | 解决方案

源系统成集云目标系统 ​ 编辑 方案介绍 管家婆ERP系统是一个全面而灵活的企业资源计划平台&#xff0c;旨在帮助企业优化和自动化其业务流程&#xff0c;从而提高效率和生产力。该系统集成了从供应链管理、生产管理、财务管理到人力资源管理等所有企业运营方面的功能&#x…...

Django开发之进阶篇

Django进阶篇 一、Django学习之模板二、Django学习之中间件默认中间件自定义中间件 三、Django学习之ORM定义模型类生成数据库表操作数据库添加查询修改删除 一、Django学习之模板 在 Django 中&#xff0c;模板&#xff08;Template&#xff09;是用于生成动态 HTML&#xff…...

【C++】:类和对象(3)

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux的基础知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数…...