前端开发中javascript闭包会引发内存泄漏么?

本文主要是和大家一起来讨论下javascript的闭包会造成内存泄漏吗?希望通过本文的分享对大家学习javascript有所帮助。 
在谈内存泄漏这个问题之前先看看JavaScript的垃圾收集机制,JavaScript 具有自动垃圾收集机制,就是找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间)。常用的的方法有两种,即标记清楚和引用计数。 
标记清除 
JavaScript 中最常用的垃圾收集方式是标记清除(mark-and-sweep)。垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。 
引用计数 
引用计数(reference counting)的含义是跟踪记录每个值被引用的次数。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。 
Netscape Navigator 3.0 是最早使用引用计数策略的浏览器,但很快它就遇到了一个严重的问题,请看下面这个例子: 
function problem(){ 
var objectA = new Object(); 
var objectB = new Object(); 
objectA.someOtherObject = objectB; 
objectB.anotherObject = objectA; 

说明:objectA 和objectB 通过各自的属性相互引用,即这两个对象的引用次数都是2,在采用标记清除策略的实现中,由于函数执行之后,这两个对象都离开了作用域,因此这种相互引用不是个问题。但在采用引用计数策略的实现中,当函数执行完毕后,objectA 和objectB 还说明将继续存在,因为它们的引用次数永远不会是0。假如这个函数被重复多次调用,就会导致大量内存得不到回收。 
为此,Netscape 在Navigator 4.0 中放弃了引用计数方式,然而引用计数导致的麻烦并未就此了结。IE9以前中有一部分对象并不是原生JavaScript 对象。例如,其BOM 和DOM 中的对象就是使用C++以COM(Component Object Model,组件对象模型)对象的形式实现的,而COM 对象的垃圾收集机制采用的就是引用计数策略。因此,即使IE 的JavaScript 引擎是使用标记清除策略来实现的,但JavaScript 访问的COM 对象依然是基于引用计数策略的。换句话说,只要在IE 中涉及COM 对象,就会存在循环引用的问题。 比如: 
  var element = document.getElementById("some_element"); 
var myObject = new Object(); 
myObject.element = element; 
element.someObject = myObject; 
DOM 元素(element)与一个原生JavaScript 对象(myObject)之间创建了循环引用。其中,变量myObject 有一个名为element 的属性指向element 对象;而变量element 也有一个属性名叫someObject 回指myObject。由于存在这个循环引用,即使将例子中的DOM 从页面中移除,它也永远不会被回收。 
解决办法:将变量设为null从而切断变量与它此前引用的值之间的连接。 
myObject.element = null; 
element.someObject = null; 
看完上面的内容,我来谈正题。 
闭包不会引起内存泄漏 
由于IE9 之前的版本对JScript 对象和COM 对象使用不同的垃圾收集。因此闭包在IE 的这些版本中会导致一些特殊的问题。具体来说,如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁 
请看例子: 
function assignHandler(){ 
var element = document.getElementById("someElement"); 
element.onclick = function(){ 
alert(element.id); 
}; 

以上代码创建了一个作为element 元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用(事件将在第13 章讨论)。由于匿名函数保存了一个对assignHandler()的活动对象的引用,因此就会导致无法减少element 的引用数。只要匿名函数存在,element 的引用数至少也是1,因此它所占用的内存就永远不会被回收 
解决办法前言已经提到过,把element.id 的一个副本保存在一个变量中,从而消除闭包中该变量的循环引用同时将element变量设为null。 
  function assignHandler(){ 
var element = document.getElementById("someElement"); 
var id = element.id; 
element.onclick = function(){ 
alert(id); 
}; 
element = null; 

总结:闭包并不会引起内存泄漏,只是由于IE9之前的版本对JScript对象和COM对象使用不同的垃圾收集,从而导致内存无法进行回收,这是IE的问题,所以闭包和内存泄漏没半毛钱关系。

来源:全栈开发者

时间: 2024-08-03 07:27:56

前端开发中javascript闭包会引发内存泄漏么?的相关文章

20170917 前端开发周报:JavaScript函数式编程、作用域和闭包

1.用函数式编程对JavaScript进行断舍离 当从业20的JavaScript老司机学会函数式编程时,他扔掉了90%的特性,也不用面向对象了,最后发现了真爱啊!!! https://juejin.im/entry/59b86... 2.JavaScript作用域和闭包 作用域和闭包在JavaScript里非常重要.但是在我最初学习JavaScript的时候,却很难理解.这篇文章会用一些例子帮你理解它们.我们先从作用域开始.作用域 JavaScript的作用域限定了你可以访问哪些变量.有两种作

前端开发:Javascript中的数组,常用方法解析

前端开发:Javascript中的数组,常用方法解析 前言 Array是Javascript构成的一个重要的部分,它可以用来存储字符串.对象.函数.Number,它是非常强大的.因此深入了解Array是前端必修的功课.周五啦,博主的心又开始澎湃了,明儿个周末有木有,又可以愉快的玩耍了. 创建数组 创建数组的基本方式有两种,一种字面量,另一种使用构造函数创建: var arr = [1,2,3]; //字面量的形式创建数组 值与值之间用英文逗号隔开 var arr1 = new Array(1,2

前端开发中务必要转义的三处场景

出于这样或那样的原因,我们在传输.存储.展现字符串时需要进行转义操作,防止不可控的事情发生.下面我将分三处场景描述,有的里边确实有坑,希望大家看完后都有所收获.欢迎积极留言补充. 场景1:使用URL 前端开发中,我们经常会使用到URL,比如博客查询的表单action:"http://eastme.me?q=前端".Ajax发送Get\Post请求.跳转至网址:"http://www.eastme.me/个人简介"等等.这些请求的URL经常会出现汉字,尤其是当表单提交

前端开发中的Error以及异常捕获

本文首发于公众号:符合预期的CoyPan 写在前面 在前端项目中,由于JavaScript本身是一个弱类型语言,加上浏览器环境的复杂性,网络问题等等,很容易发生错误.做好网页错误监控,不断优化代码,提高代码健壮性是一项很重要的工作.本文将从Error开始,讲到如何捕获页面中的异常.文章较长,细节较多,请耐心观看. 前端开发中的Error JavaScript中的Error JavaScript中,Error是一个构造函数,通过它创建一个错误对象.当运行时错误产生时,Error的实例对象会被抛出.

五年干货分享!前端开发中最常用的JS代码片段

很多网友私信我,说学到js就开始卡壳了,甚至初略的看了一下js,就跳过开始学习框架之类的.这里要提醒你,js是前端的重中之重,如果你忽视了,后果不堪设想! 学好,并熟练的运用这门编程语言真的很难吗?本篇文章为大家总结了一些前端开发中最常用的JS代码片段,希望能对大家的学习以及工作上都能有所帮助,有所收获. HTML5 DOM 选择器 javascript 代码 // querySelector() 返回匹配到的第一个元素 var item = document.querySelector('.i

一探前端开发中的JS调试技巧

前言:调试技巧,在任何一项技术研发中都可谓是必不可少的技能.掌握各种调试技巧,必定能在工作中起到事半功倍的效果.譬如,快速定位问题.降低故障概率.帮助分析逻辑错误等等.而在互联网前端开发越来越重要的今天,如何在前端开发中降低开发成本,提升工作效率,掌握前端开发调试技巧尤为重要. 本文将一一讲解各种前端JS调试技巧,也许你已经熟练掌握,那让我们一起来温习,也许有你没见过的方法,不妨一起来学习,也许你尚不知如何调试,赶紧趁此机会填补空白. 骨灰级调试大师Alert 那还是互联网刚刚起步的时代,网页前

关于前端开发中的“收口”思想

什么是收口 所谓条条大路通罗马,但如果让我来设计通向罗马的各种大路,我至少会做两件事情: ① 让罗马只有一个入口 ② 让罗马只有一个出口 这样做的好处是,无论你路从哪来,我可以统一在入口处给你打上各种标志,我也可以在你离开罗马时给你留点纪念.当然罗马自然不只一个出口入口,但是每个出口入口一定有一套相同的规定,否则就会出问题. 具体到当今的工作场景,高速公路又是一个收口的好例子,进入高速公路时候得经过收费站做点标志,离开时候也会做点操作,如果没有这种收口,不论是缴费工作,流量统计或者其他都是无法统

前端开发中的JS调试技巧

前言:调试技巧,在任何一项技术研发中都可谓是必不可少的技能.掌握各种调试技巧,必定能在工作中起到事半功倍的效果.譬如,快速定位问题.降低故障概率.帮助分析逻辑错误等等.而在互联网前端开发越来越重要的今天,如何在前端开发中降低开发成本,提升工作效率,掌握前端开发调试技巧尤为重要. 本文将一一讲解各种前端JS调试技巧,也许你已经熟练掌握,那让我们一起来温习,也许有你没见过的方法,不妨一起来学习,也许你尚不知如何调试,赶紧趁此机会填补空白. 骨灰级调试大师Alert 那还是互联网刚刚起步的时代,网页前

【名词】在前端开发中的“轮子”

最近在随便刷一些论坛,文档,常见一个名词"轮子". 自我理解:已封装完成的库.换个名词,组件.例如:图片轮播组建:swiper等,菜单栏组件. 在前端开发中,我们需要在满足条件下对于平台进行兼容,众所周知的IE系列,那么如果有一份已经写好的能够兼容各个浏览器的代码,进行封装以后直接引入使用显然会事半功倍. 作为练习,对于市场上主流的"轮子"进行仿造可以增加经验,也可以自己造轮子,只有自己动手做了才能够知道其中的知识点.