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 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
闭包: 不合理的使用闭包,从而导致某些变量一直被留在内存当中。

相关内容

热门资讯

郭德纲相声论梦台词 郭德纲相声论梦台词  郭德纲出现引起了社会对相声关注,很多人对相声感兴趣,开始思考相声中问题。下面是...
学校元旦经典致辞 学校元旦经典致辞范文(精选5篇)  在我们平凡的日常里,大家一定都接触过致辞吧,致辞的措词造句要考虑...
快板台词 快板台词大全  快板是一种汉族说唱艺术,属于中国曲艺韵诵类曲种。早年称作“数来宝”,也叫称“顺口溜”...
读书活动主持词开场白   读书活动主持词开场白1  尊敬的各位领导、各位来宾、各位参赛选手:  大家下午好!在春意盎然,百...
感谢老师致辞 感谢老师致辞(精选12篇)  在平日的学习、工作和生活里,要用到致辞的地方还是很多的,致辞是指在举行...
结婚典礼感谢致辞 结婚典礼感谢致辞(通用15篇)  在平平淡淡的学习、工作、生活中,要用到致辞的情况还是蛮多的,致辞要...
大鱼海棠配音台词 大鱼海棠配音台词  蛰伏十二年的动画《大鱼海棠》,终于上映了。虽然剧情被很多人吐槽,但美得不像话的画...
小学毕业典礼主持词 小学毕业典礼主持词实用范文  活动对象的不同,主持词的写作风格也会大不一样。在当今中国社会,司仪等是...
篮球赛闭幕词 篮球赛闭幕词(精选15篇)篮球赛闭幕词1各位来宾、同志们:  为期xx天的xxx第二届职工篮球赛,在...
元旦联欢晚会主持词 元旦联欢晚会主持词范文(精选9篇)  主持词分为会议主持词、晚会主持词、活动主持词、婚庆主持词等。在...
大型文艺演出主持词 大型文艺演出主持词  主持词可以采用和历史文化有关的表述方法去写作以提升活动的文化内涵。在当今中国社...
开学典礼致辞 开学典礼致辞15篇  在日常生活或是工作学习中,大家都经常接触到致辞吧,致辞具有“礼仪性”或“仪式化...
农村婚礼司仪主持词 农村婚礼司仪主持词15篇  主持词需要富有情感,充满热情,才能有效地吸引到观众。在当今不断发展的世界...
如何主持会议参考 如何主持会议范文参考  1.宣布会议开始。会议时间到后,若全体与会人员都已到位(至少重要的与会人员已...
韩剧漂亮男人经典台词 韩剧漂亮男人经典台词  1、答案都是在当事人手里的,不知道人们为什么,明明是自己非常了解的问题,但是...
外公七十大寿祝寿词 外公七十大寿祝寿词  外公七十大寿祝寿词  尊敬的外公、外婆、各位长辈、各位来宾:    大家好!今...
《香瓜七兄弟》经典台词 《香瓜七兄弟》经典台词  1. 阿丽呀,我哪好,我改还不行吗  2. 记不记得上回你尿炕 阿姨来了你...
孙子满月酒爷爷致辞 孙子满月酒爷爷致辞(通用6篇)  无论是身处学校还是步入社会,大家都不可避免地会接触到致辞吧,致辞具...
国庆文艺汇演主持词 国庆文艺汇演主持词  主持词要注意活动对象,针对活动对象写相应的主持词。在当今社会中,主持人在活动中...
中秋晚会主持词开场 中秋晚会主持词开场  主持词是主持人在台上表演的灵魂之所在。在现在的社会生活中,主持人的需求越来越高...