JavaScript的闭包和内存泄漏问题

闭包

JavaScript中必须提到的功能最强大的抽象概念之一:闭包。它究竟是做什么的呢?

1 function makeAdder(a) {
2     return function(b) {
3         return a + b;
4     }
5 }
6 var x = makeAdder(5);
7 var y = makeAdder(20);
8 x(6); // 11
9 y(7); // 27

makeAdder这个名字本身应该能说明函数是用来做什么的:他创建了一个新的adder函数,这个函数自身带有一个参数,它被调用的时候这个参数会被加载在外层函数传进来的参数上。

这里发生的事情和前面介绍过的内嵌函数十分相似:一个函数被定义在了另外一个函数的内部,内部函数可以访问外部函数的变量。唯一的不同是,外部函数被返回了,那么常识告诉我们局部变量“应该”不再存在。但是它们却仍然存在——否则 adder 函数将不能工作。也就是说,这里存在makeAdder 的局部变量的两个不同的“副本”——一个是 a 等于5,另一个是 a 等于20。运行结果就是一个是11一个是27。

下面来说说到底发生了什么。当JavaScript执行一个函数时,都会创建一个作用域对象(scope object),用来保存在这个函数中创建的局部变量。他和被传入函数的变量一起被初始化。这与那些保存的所有全部变量和函数的全局变量(global object)类似,但仍有很重要的区别,第一,每次函数被执行的时候,就会创建一个新的,特定的作用域对象;第二,与全局对象(在浏览器里面是当做window对象来访问的)不同的是,你不能从JavaScript代码中直接访问作用与对象,也没有可以遍历当前作用与对象里面属性的方法。

所以当调用 makeAdder 时,解释器创建了一个作用域对象,它带有一个属性:a,这个属性被当作参数传入 makeAdder 函数。然后 makeAdder 返回一个新创建的函数。通常 JavaScript 的垃圾回收器会在这时回收 makeAdder 创建的作用域对象,但是返回的函数却保留一个指向那个作用域对象的引用。结果是这个作用域对象不会被垃圾回收器回收,直到指向 makeAdder 返回的那个函数对象的引用计数为零。

作用域对象组成了一个名为作用域链(scope chain)的链。它类似于原形(prototype)链一样,被 JavaScript 的对象系统使用。

一个闭包就是一个函数和被创建的函数中的作用域对象的组合。

闭包允许你保存状态——所以它们通常可以代替对象来使用。

内存泄漏

使用闭包的一个坏处是,在 IE 浏览器中它会很容易导致内存泄露。JavaScript是一种具有垃圾回收机制的语言——对象在创建时分配内存,然后当值想这个对象的引用计数为零时,浏览器会回收内存。宿主环境提供的对象都是按照这种方法被处理的。

浏览器主机需要处理大量的对象来描绘一个正在被展现的HTML页面——DOM对象。浏览器负责管理他们的内存分配和回收。

IE浏览器有自己一套垃圾回收机制,这套机制与JavaScript提供的垃圾回收机制交互时,可能发生内存泄漏。

在IE中,每当在一个JavaScript对象和一个本地对象之间形成循环引用时就会发生内存泄漏。如下所示:

1 function leakMemory() {
2     var el = document.getElementById(‘el‘);
3     var o = { ‘el‘: el };
4     el.o = o;
5 }

这段代码的循环引用会导致内存泄露:IE 不会释放被 el 和 o 使用的内存,直到浏览器被彻底关闭并重启后。

这个例子往往无法引起人们的重视:一般只会在长时间运行的应用程序中,或者因为巨大的数据量和循环中导致内存泄露发生时,内存泄露才会引起注意。

不过一般也很少发生如此明显的内存泄露现象——通常泄露的数据结构有多层的引用(references),往往掩盖了循环引用的情况。

闭包很容易发生无意识的内存泄露。如下所示:

1 function addHandler() {
2     var el = document.getElementById(‘el‘);
3     el.onclick = function() {
4         el.style.backgroundColor = ‘red‘;
5     }
6 }

这段代码创建了一个元素,当它被点击的时候变红,但同时它也会发生内存泄露。为什么?因为对el 的引用不小心被放在一个匿名内部函数中。这就在 JavaScript 对象(这个内部函数)和本地对象之间(el)创建了一个循环引用。

这个问题有很多种解决方法,最简单的一种是不要使用 el 变量:

1 function addHandler(){
2     document.getElementById(‘el‘).onclick = function(){
3         this.style.backgroundColor = ‘red‘;
4     };
5 }

有趣的是,有一种窍门解决因闭包而引入的循环引用,是添加另外一个闭包:

1 function addHandler() {
2     var clickHandler = function() {
3         this.style.backgroundColor = ‘red‘;
4     };
5     (function() {
6         var el = document.getElementById(‘el‘);
7         el.onclick = clickHandler;
8     })();
9 }

内部函数被直接执行,并在 clickHandler 创建的闭包中隐藏了它的内容。

另外一种避免闭包的好方法是在 window.onunload 事件发生期间破坏循环引用。很多事件库都能完成这项工作。注意这样做将使 Firefox 中的 bfcache 无法工作。所以除非有其他必要的原因,最好不要在 Firefox 中注册一个unload 的监听器。

时间: 2024-12-29 07:27:02

JavaScript的闭包和内存泄漏问题的相关文章

闭包和内存泄漏

由于IE9之前的版本对JScript对象和COM对象使用不同的垃圾收集例程,因此闭包在IE的这些版本中会导致一些特殊的问题.具体来说,如果闭包的作用域链中保存着一个HTML元素,那么久意味着该元素将无法被销毁.来看下面的例子 function assignHandler(){ var element=document.getElementById("someElement"); element.onclick=function(){ alert(element.id); }; } 以上

转《js闭包与内存泄漏》

首先,能导致内存泄漏的一定是引用类型的变量,比如函数和其他自定义对象.而值类型的变量是不存在内存泄漏的,比如字符串.数字.布尔值等.因为值类型是靠复制来传递的,而引用类型是靠类似c语言中的指针来传递的.可以认为一个引用类型的变量就是一个指向某个具体的内存地址的指针. 当我们用js代码创建一个引用类型的时候(以下简称对象),js引擎会在内存中开辟一块空间来存放数据,并把指针引用交给那个变量.内存是有限的,js引擎必须保证当开辟的对象没用的时候,把所分配的内存空间释放出来,这个过程叫做垃圾回收,负责

js性能优化之-闭包(内存泄漏)

在闭包中的局布变量会自动回收有些情况不会被 回收一直占用空间 如异步,计时器,事件绑定 不会被 回收最好手动处理一下 obj = new Fname(); obj=null;  //清空对象 clearInterval(timer)  //清除计时器 dom.onclick=null  //指定为null dom.addEventListener('click',fn,!1)  //绑定方法 dom.removeEventListener('click',fn,!1) //尽量不要用匿名函数绑定

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

本文主要是和大家一起来讨论下javascript的闭包会造成内存泄漏吗?希望通过本文的分享对大家学习javascript有所帮助.  在谈内存泄漏这个问题之前先看看JavaScript的垃圾收集机制,JavaScript 具有自动垃圾收集机制,就是找出那些不再继续使用的变量,然后释放其占用的内存.为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间).常用的的方法有两种,即标记清楚和引用计数.  标记清除  JavaScript 中最常用的垃圾收集方式是标记清除(mark-and-s

闭包会造成内存泄漏吗?

前言 在谈内存泄漏这个问题之前先看看JavaScript的垃圾收集机制,JavaScript 具有自动垃圾收集机制,就是找出那些不再继续使用的变量,然后释放其占用的内存.为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间).常用的的方法有两种,即标记清楚和引用计数. 1. 标记清除 JavaScript 中最常用的垃圾收集方式是标记清除(mark-and-sweep).垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式).然后,它会去掉环境中的变量以及

闭包会造成内存泄漏吗

闭包会造成内存泄漏吗? 来源:yan 前言 在谈内存泄漏这个问题之前先看看JavaScript的垃圾收集机制,JavaScript 具有自动垃圾收集机制,就是找出那些不再继续使用的变量,然后释放其占用的内存.为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间).常用的的方法有两种,即标记清楚和引用计数. 标记清除 JavaScript 中最常用的垃圾收集方式是标记清除(mark-and-sweep).垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)

浅析c#内存泄漏

一直以来都对内存泄露和内存溢出理解的不是很深刻.在网上看到了几篇文章,于是整理了一下自己对内存泄露和内存溢出的理解. 一.概念 内存溢出:指程序在运行的过程中,程序对内存的需求超过了超过了计算机分配给程序的内存,从而造成“Out of memory”之类的错误,使程序不能正常运行. 造成内存溢出有几种情况: 1.计算机本身的内存小,当同时运行多个软件时,计算机得内存不够用从而造成内存溢出.对于这种情况,只能增加计算机内存来解决. 2.软件程序的问题,程序在运行时没能及时释放不用的内存,造成使用的

前端知识体系:JavaScript基础-作用域和闭包-闭包的实现原理和作用以及堆栈溢出和内存泄漏原理和相应解决办法

闭包的实现原理和作用 闭包: 有权访问另一个函数作用域中的变量的函数. 创建闭包的常见方式就是,在一个函数中创建另一个函数. 闭包的作用: 访问函数内部变量.保持函数在环境中一直存在,不会被垃圾回收机制处理 因为函数内部声明 的变量是局部的,只能在函数内部访问到,但是函数外部的变量是对函数内部可见的,这就是作用域链的特点了. 子级可以向父级查找变量,逐级查找,找到为止 因此我们可以在函数内部再创建一个函数,这样对内部的函数来说,外层函数的变量都是可见的,然后我们就可以访问到他的变量了. <scr

JavaScript 中的内存泄漏

JavaScript 是一种垃圾收集式语言,这就是说,内存是根据对象的创建分配给该对象的,并会在没有对该对象的引用时由浏览器收回.JavaScript 的垃圾收集机制本身并没有问题,但浏览器在为 DOM 对象分配和恢复内存的方式上却有些出入. Internet Explorer 和 Mozilla Firefox 均使用引用计数来为 DOM 对象处理内存.在引用计数系统,每个所引用的对象都会保留一个计数,以获悉有多少对象正在引用它.如果计数为零,该对象就会被销毁,其占用的内存也会返回 给堆.虽然