之前有整理过一版关于闭包的概念,但感觉思路不是很清晰,是临时想起一些例子来讲的,今天再次来讲一下闭包。
闭包:
函数嵌套函数,内部函数可以引用外部函数的参数和变量
function aaa(a){ var b=5; function bbb(){ alert(a); alert(b); } }
在上面的例子当中,bbb函数是可以访问到aaa函数中的a和b的,同时,JS中的垃圾回收机制也不会回收a,b。
function aaa(){ var a =5; function bbb(){ alert(a); } return bbb; } var c = aaa(); c();
可以看到此时调用c()是可以弹出5的,即a在调用aaa()之后是没有被回收的。即常驻内存。
闭包的好处:
1 希望一个变量可以长期驻扎在内存,之前已经说明。
2 避免全局变量污染
var a=1; function aaa(){ a++; alert(a); } aaa(); //2
aaa(); //3
此时函数aaa访问的是全局变量a,那么就很容易被其他函数或程序修改。那么可以使用一下代码来避免这个问题
function aaa(){ var a=5; return function(){ a++; alert(a); } }
var b=aaa();
b(); //6
b(); //7
alert(a) //error
以上的例子中可以看出已经把a放进函数里作为一个局部变量被引用,a也会常驻内存,不会被垃圾回收清理。
还可以将其改写成函数声明表达式:将function用括号包围起来做到即时调用,并且减少全局变量的污染,实现代码模块化(即该代码不会因外界条件而改变结果)
var b=(function() { var a=5; return function() { a++; alert(a); } })(); b(); //6 b(); //7 alert(a) //error
3 私有成员
var aaa=(function(){ var a =1; function bbb(){alert(++a);} function ccc(){alert(++a);} return{ b:bbb, c:ccc } })() ; alert(aaa.b); //2 alert(aaa.c); //3 alert(a); //error alert(bbb); //error alert(ccc); //error
aaa内的函数和变量只能通过aaa来访问,外部是访问不到的,由此实现了私有成员的创建。
4 循环 索引 作用域延伸
//有一个3个<li>标签的页面,需要绑定点击事件,弹出对应的序号。 windows.onload=function(){ var aLi=document.getElementByTagName('li'); for(var i=0;i<aLi.length;i++){ aLi[i].onclick = (function(i){ return function(){alert(i);} })(i); } }
当然在ES6中使用let替代var也可以解决这个问题。所以在支持ES6的浏览器中能用let的地方就不要用var。
还有一个需要注意的地方:
IE下回引发内存泄漏
//假设有个id为div1的div window.οnlοad=function(){ var oDiv=document.getElementById('div1'); oDIv.onclick=function(){ alert(oDiv.id); }; }
在上述情况中,DOM树中的元素被更深层级的调用,会导致关闭页面后无法释放内存的问题,最终会导致内存泄漏。
要想解决这个问题也是十分的简单,只需要在关闭界面的时候强制解除对元素的引用。即:
//假设有个id为div1的div window.οnlοad=function(){ var oDiv=document.getElementById('div1'); oDIv.onclick=function(){ alert(oDiv.id); }; //以下为添加解除程序 window.οnunlοad=function(){ oDiv.click=null;//或者oDiv=null也可以。 } }
以上便是闭包的所有相关知识。总结一下:
1 闭包的含义:函数中嵌套函数,嵌套的内部函数可以访问外部函数的参数和变量。
2 闭包的作用:
1 避免全局变量的污染。
2 创建私有成员(函数和变量)。实现代码模块化
3 作用域的延伸,也是变量常驻内存的一种体现。