闭包:指有权访问另外一个函数做用域中的变量的函数。
闭包的本质是将函数内部和函数外部链接起来的一座桥梁。javascript
例1:java
function outer(){ var a=1; function inner(){ a++; alert(a); } return inner; } var f1=outer();//建立了一个闭包,f1能访问outer函数中的变量 f1();//弹出2.
这段代码的特色在于:
1.函数inner嵌套在函数outer内部
2.函数outer返回函数inner,并将值赋给了f1
例2:算法
// 实现累加:方式1 var a = 0; var add = function(){ a++; console.log(a) } add(); add(); //方式2 :闭包 var add = (function(){ var a = 0; return function(){ a++; console.log(a); } })(); console.log(a); //undefined add(); add(); //方式2的优势:减小全局变量,将变量私有化
闭包只能取得包含函数中任何变量的最后一个值。
例:chrome
function f1() { var res = new Array(); for(var i=0;i<10;i++){ res[i] = function() { alert(i); }; } return res; } var f2 = f1(); var f2 = f1(); f2[0]();//alert 10 //并不会返回一次弹出0-9的函数数组,而是弹出10个10的函数数组,由于res中每一个函数的做用域中都保存着f1()的活动对象,引用的是同一个变量i,当f1()返回后i的值为10
解决方法:数组
function f1() { var res = new Array(); for(var i=0;i<10;i++){ res[i] = (function(num) { return function (){ alert(num); } })(i);//函数参数按值传递 } return res; } var f2 = f1(); var f2 = f1(); f2[0]();//alert 0
垃圾回收机制闭包
说到内存管理,天然离不开JS中的垃圾回收机制,有两种策略来实现垃圾回收:标记清除 和 引用计数;函数
标记清除:垃圾收集器在运行的时候会给存储在内存中的全部变量都加上标记,而后,它会去掉环境中的变量的标记和被环境中的变量引用的变量的标记,此后,若是变量再被标记则表示此变量准备被删除。 2008年为止,IE,Firefox,opera,chrome,Safari的javascript都用使用了该方式;设计
引用计数:跟踪记录每一个值被引用的次数,当声明一个变量并将一个引用类型的值赋给该变量时,这个值的引用次数就是1,若是这个值再被赋值给另外一个变量,则引用次数加1。相反,若是一个变量脱离了该值的引用,则该值引用次数减1,当次数为0时,就会等待垃圾收集器的回收。指针
这个方式存在一个比较大的问题就是循环引用,就是说A对象包含一个指向B的指针,对象B也包含一个指向A的引用。 这就可能形成大量内存得不到回收(内存泄露),由于它们的引用次数永远不多是 0 。早期的IE版本里(ie4-ie6)采用是计数的垃圾回收机制,闭包致使内存泄露的一个缘由就是这个算法的一个缺陷。code
咱们知道,IE中有一部分对象并非原生额javascript对象,例如,BOM和DOM中的对象就是以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数。所以,虽然IE的javascript引擎采用的是标记清除策略,可是访问COM对象依然是基于引用计数的,所以只要在IE中设计COM对象就会存在循环引用的问题!
例子:
window.onload = function(){ var ele = document.getElementById("id"); ele.onclick = function(){ alert(ele.id); } }
这段代码为何会形成内存泄露?
ele.onclick = function(){ alert(ele.id); }
执行这段代码的时候,将匿名函数对象赋值给ele的onclick属性;而后匿名函数内部又引用了ele对象,存在循环引用,因此不能被回收。
解决方法:
window.onload = function(){ var ele = document.getElementById("id"); var id = ele.id; //解除循环引用 ele.onclick = function(){ alert(id); } ele = null; // 将闭包引用的外部函数中活动对象清除 }
优势:
当须要一个变量常驻内存时,闭包能够实现一个变量常驻内存 (若是多了就占用内存了)
避免全局变量的污染
私有化变量
缺点:
由于闭包会携带包含它的函数的做用域,所以会比其余函数占用更多的内存
引发内存泄露