千呼万唤始出来,我终于来总结javascript(以下都简称js)闭包了。闭包在js中有多重要呢?——很重要。话不多说,开撸。
为什么要用闭包
闭包作为我们js中必不可少的部分,我们究竟是为什么要使用它呢,这里从两个方面来说,什么是闭包和使用闭包的优势。
什么是闭包
闭包就是能够读取其他函数内部变量的函数。js中,由于js作用域的问题,内部函数可以读取外部函数里的变量,但是外部函数不可以读取内部函数的变量,闭包就是为了将函数内部和函数外部联系起来。
闭包的优势
1,一个变量长期驻扎在内存中(一不小心会成为缺点,占内存)。
2,避免全局变量的污染,加强封装性,达到对变量的保护作用。
3,私有成员的存在,有些方法和属性不想让外部修改,就可以使用闭包只提供方法获取。
js的垃圾回收机制
正式讲闭包的使用之前,先理解下js的垃圾回收机制。js有自动垃圾回收机制(Garbage Collecation简称GC),GC在回收内存的时候,会判断该变量是否被其他对象引用,在确定没有其他对象引用就会释放其占有的内存。使用闭包后,函数a被b引用,b又被a外的c引用(闭包特点一个函数嵌套一个函数),所以函数a在返回后不会被GC回收,因为在js垃圾回收机制中,如果两个对象互相引用,不再被第三者引用时,这两个互相引用的对象才会被回收。
如何创建闭包
闭包的代码特点:
1,函数b嵌套在函数a内部。
2,函数a返回函数b。
先看个例子
function a(){
var i=0;
alert(++i);
}
//alert(i);//报异常,i is not defined
a();//1
a();//1
alert(i);//函数外部是无法访问函数内部的值的,所以会报异常,接下来两个函数a执行都弹出1,很简单的函数,就不用说了。
function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c = a();
c();//1
c();//2
上面就创建了一个标准的闭包,在函数a中定义了函数b又return了函数b。var c = a();//执行了a函数,返回函数a的返回值即函数b
c();//执行函数b,i变量自加1,最后弹出1
c();//再执行函数b,弹出2,上面执行两次都弹出1,这里却弹出了2,这说明i的变量一直在内存中保存并没有被回收,这就是闭包。
再举一个例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</body>
<script>
function getLiIndex(){
var li = document.getElementsByTagName('li');
for(var i = 0;i<li.length;i++){
li[i].onclick = function(){
alert(i);//全部为5
};
}
}
getLiIndex();
</script>
</html>
上面是一个获取li索引值的一个功能,在这个例子中,在页面中点击li的时候,全部弹出5,这里因为这里闭包并不是记录的变量的值,只是记录的变量的一个引用,它是最后点击的时候才去获取i的值,所以它最后弹出的都为5.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</body>
<script>
function getLiIndex(){
var li = document.getElementsByTagName('li');
for(var i = 0;i<li.length;i++){
li[i].onclick = (function(j){
return function(){
alert(j);
};
})(i);
}
}
getLiIndex();
</script>
</html>
将这个例子改成如上的闭包,就可以解决全部弹出5这个问题了,这是因为将上面的函数改成了一个立即执行函数,并且捕捉当前的索引值,所以在点击li的时候,当前的li就弹出已经保存好的索引值。
闭包的缺点1,由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除,即将变量值设为null。
2,闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
好像还有很多没总结,逻辑还有点乱,貌似我应该重新再总结一遍,不管了,今天就这样了,下雨任性~~~~(>_<)~~~~