JS - 闭包 垃圾回收与内存泄露
创始人
2024-05-31 12:51:29
0

一、闭包

涉及面试题:什么是闭包?

闭包的定义其实很简单:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包

function A() {let a = 1window.B = function () {console.log(a)}
}
A()
B() // 1

很多人对于闭包的解释可能是函数嵌套了函数,然后返回一个函数。其实这个解释是不完整的,就比如我上面这个例子就可以反驳这个观点。
在 JS 中,闭包存在的意义就是让我们可以间接访问函数内部的变量。

经典面试题,循环中使用闭包解决 var 定义函数的问题

for (var i = 1; i <= 5; i++) {setTimeout(function timer() {console.log(i)}, i * 1000)
}

首先因为 setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆 6。

  • 解决办法有三种,第一种是使用闭包的方式
for (var i = 1; i <= 5; i++) {(function(j) {setTimeout(function timer() {console.log(j)}, j * 1000)})(i)
}

在上述代码中,我们首先使用了立即执行函数将 i 传入函数内部,这个时候值就被固定在了参数 j上面不会改变,当下次执行 timer 这个闭包的时候,就可以使用外部函数的变量 j,从而达到目的。

  • 第二种就是使用 setTimeout 的第三个参数,这个参数会被当成 timer 函数的参数传入。
for (var i = 1; i <= 5; i++) {setTimeout(function timer(j) {console.log(j)},i * 1000,i)
}
  • 第三种就是使用 let 定义 i 了来解决问题了,这个也是最为推荐的方式
for (let i = 1; i <= 5; i++) {setTimeout(function timer() {console.log(i)}, i * 1000)
}

二、垃圾回收与内存泄露

1. 浏览器的垃圾回收机制

(1)垃圾回收的概念
垃圾回收:JavaScript代码运行时,需要分配内存空间来储存变量和值。当变量不在参与运行时,就需要系统收回被占用的内存空间,这就是垃圾回收。
回收机制:
Javascript 具有自动垃圾回收机制,会定期对那些不再使用的变量、对象所占用的内存进行释放,原理就是找到不再使用的变量,然后释放掉其占用的内存。
JavaScript中存在两种变量:局部变量和全局变量。全局变量的生命周期会持续要页面卸载;而局部变量声明在函数中,它的生命周期从函数执行开始,直到函数执行结束,在这个过程中,局部变量会在堆或栈中存储它们的值,当函数执行结束后,这些局部变量不再被使用,它们所占有的空间就会被释放。
不过,当局部变量被外部函数使用时,其中一种情况就是闭包,在函数执行结束后,函数外部的变量依然指向函数内部的局部变量,此时局部变量依然在被使用,所以不会回收。
(2)垃圾回收的方式
浏览器通常使用的垃圾回收方法有两种:标记清除,引用计数。
1)标记清除
标记清除是浏览器常见的垃圾回收方式,当变量进入执行环境时,就标记这个变量“进入环境”,被标记为“进入环境”的变量是不能被回收的,因为他们正在被使用。当变量离开环境时,就会被标记为“离开环境”,被标记为“离开环境”的变量会被内存释放。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。
2)引用计数
另外一种垃圾回收机制就是引用计数,这个用的相对较少。引用计数就是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变为0时,说明这个变量已经没有价值,因此,在在机回收期下次再运行时,这个变量所占有的内存空间就会被释放出来。
这种方法会引起循环引用的问题:例如: obj1和obj2通过属性进行相互引用,两个对象的引用次数都是2。当使用循环计数时,由于函数执行完后,两个对象都离开作用域,函数执行结束,obj1和obj2还将会继续存在,因此它们的引用次数永远不会是0,就会引起循环引用。

function fun() {let obj1 = {};let obj2 = {};obj1.a = obj2; // obj1 引用 obj2obj2.a = obj1; // obj2 引用 obj1
}

这种情况下,就要手动释放变量占用的内存:

obj1.a =  null
obj2.a =  null

(3)减少垃圾回收
虽然浏览器可以进行垃圾自动回收,但是当代码比较复杂时,垃圾回收所带来的代价比较大,所以应该尽量减少垃圾回收。

对数组进行优化: 在清空一个数组时,最简单的方法就是给其赋值为[ ],但是与此同时会创建一个新的空对象,可以将数组的长度设置为0,以此来达到清空数组的目的。

对object进行优化: 对象尽量复用,对于不再使用的对象,就将其设置为null,尽快被回收。

对函数进行优化: 在循环中的函数表达式,如果可以复用,尽量放在函数的外面。

2. 哪些情况会导致内存泄漏

以下四种情况会造成内存的泄漏:
意外的全局变量: 由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
被遗忘的计时器或回调函数: 设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
脱离 DOM 的引用: 获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
闭包: 不合理的使用闭包,从而导致某些变量一直被留在内存当中。

相关内容

热门资讯

绵山游记高一作文【实用3篇】 绵山游记高一作文 篇一绵山游记绵山,位于四川省南充市嘉陵区,是一座著名的山岳旅游胜地。我有幸在暑假期...
优秀的男孩们高二作文【精彩3... 优秀的男孩们高二作文 篇一优秀的男孩们男孩们,你们是社会的未来,是家庭的希望,是校园的风景。作为高二...
高三责任与担当作文800字【... 高三责任与担当作文800字 篇一高三,是每个学生都期待的一年,也是每个学生都感到压力巨大的一年。在这...
记忆中的红薯粉高一作文【精简... 记忆中的红薯粉高一作文 篇一红薯粉是我童年时的美味回忆。每逢夏日,妈妈总是会准备一碗清爽可口的红薯粉...
新的计划本高二作文(优选5篇... 新的计划本高二作文 篇一:为什么制定计划是成功的关键在我们的生活中,计划是非常重要的。无论是在学习、...
那年匆匆高二作文【精选3篇】 那年匆匆高二作文 篇一初次踏入高二,我仿佛感受到时间的流逝之快。仿佛昨天还是迷茫的高一新生,而今天已...
记得高三作文 记得高三作文  在生活、工作和学习中,许多人都有过写作文的经历,对作文都不陌生吧,写作文可以锻炼我们...
我的命运我做主高三作文 我的命运我做主高三作文  在我们平凡的日常里,说到作文,大家肯定都不陌生吧,作文可分为小学作文、中学...
做一个聪明的人高三作文800... 篇一:做一个聪明的人高三作文800字第一篇内容:智慧的力量聪明是每个人都希望拥有的品质之一。在高三这...
高三家长寄语(精彩5篇) 高三家长寄语 篇一高三家长寄语:相信你的孩子,给予他们支持和鼓励亲爱的家长们:您好!我是一位拥有多年...
高三毕业证明范文【优选6篇】 高三毕业证明范文 篇一尊敬的校领导、老师们:我是xxx,现就读于贵校高三年级,即将毕业。在这里,我谨...
高三作文:甘当绿叶【优选3篇... 高三作文:甘当绿叶 篇一甘当绿叶,追寻自我的价值人生是一场长跑,每个人都有自己的起点和终点。有的人天...
高三活出自我作文 高三活出自我作文  繁星闪烁,点缀浩瀚无限的宇宙。众星争艳,释放灿烂之光,只为自己精彩,而在无边的浩...
生活的乐趣高三作文 生活的乐趣高三作文  在平平淡淡的学习、工作、生活中,大家都有写作文的经历,对作文很是熟悉吧,作文是...
梦回高三【实用3篇】 梦回高三 篇一回忆起高三,仿佛一场梦境般在脑海中浮现。那是我人生中最为辛苦却也最为难忘的一年。在这一...
寻求高考之外的人生之路创新作... 寻求高考之外的人生之路创新作文 篇一近年来,高考已经成为了社会上的一种普遍现象,几乎每一个家庭都会面...
高三物理典型例题五(通用3篇... 高三物理典型例题五 篇一在高三物理的学习过程中,典型例题是非常重要的。它们可以帮助我们巩固所学的知识...
我的高三生活作文(优秀6篇) 我的高三生活作文 篇一我的高三生活是充满挑战和压力的,但也是充满成长和回忆的。高三是人生中一个重要的...
高考作文题目预测:多一度热爱... 高考作文题目预测:多一度热爱 篇一在即将来临的高考中,作文题目往往是考生最为关注的部分。对于即将面对...
成人高考专升本《大学语文》作... 成人高考专升本《大学语文》作文写作指导 篇一:掌握作文基本要素在成人高考专升本《大学语文》考试中,作...