谈谈JS中的闭包
一、什么是闭包?
看概念总是迷迷糊糊,好像懂了,却又说不清。在此引用别的博主的话:
通俗地讲就是别人家有某个东西,你想拿到但是因为权限不够(不打死你才怪),但是你可以跟家里的孩子套近乎,通过他拿到!
这个家就是局部作用域,外部无法访问内部变量,孩子是返回对象,对家里的东西有访问权限,借助返回对象间接访问内部变量!
总结下有三个特点:
-
函数嵌套函数
-
内部的函数可以引用外部函数的参数或者变量
-
参数和变量不会被垃圾回收机制回收,因为内部函数还在引用
function aaa(){
var a = 5;
function bbb(){
console.log(a);
}
return bbb;
}
var c = aaa(); //此时c是aaa内部return的bbb函数体,外部函数aaa已运行完毕,但是变量仍被内部函数引用,故不会释放。
c(); //打印结果是5
二、闭包的好处
-
变量可以长期驻扎在内存之中
-
避免全局变量的污染,有私有成员
下面看一下例子:
1、普通函数调用:aaa执行完毕,就回收a变量,再次执行,重新赋值计算。
function aaa(){
var a = 1;
a++;
alert(a);
}
aaa(); //2
aaa(); //2
2、闭包方式调用:aaa执行后,由于a变量还在被内部函数引用,故不会被回收,再次计算,在上一次的结果上进行累加。
function aaa(){
var a = 1;
return function(){
a++;
alert(a);
}
}
var c = aaa();
c(); //2
c(); //3
3、上面还可以改写成:
var aaa = (function(){
var a = 1;
return function(){
a++;
alert(a);
}
})() //函数表达式自执行,结果是内部函数体
aaa(); //2
aaa(); //3
三、闭包的用途
-
模块化代码
var aaa = (function(){ //aaa是一个模块,私有变量是a,私有函数是bbb和ccc var a = 1; function bbb(){ a++; alert(a); } function ccc(){ a++; alert(a); } return { b:bbb, c:ccc } })() //函数表达式自执行,结果是return后面的对象 aaa.b(); //2 aaa.c(); //3
- 在循环中找到索引
<body>
<ul>
<li>111111</li>
<li>111111</li>
<li>111111</li>
</ul>
<script>
window.onload = function(){
var aLi = document.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++){
aLi[i].onclick = function(){
alert(i); //顺序点击三个li,分别弹出3 3 3。因为点击的时候for循环已经执行完毕
}
}
}
</script>
</body>
可使用闭包改为:
<body>
<ul>
<li>111111</li>
<li>111111</li>
<li>111111</li>
</ul>
<script>
window.onload = function(){
var aLi = document.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++){
(function(i){
aLi[i].onclick = function(){
alert(i); //0 1 2
}
})(i) //将i作为参数传递给内部函数,i 在for循环执行完毕不会被释放
}
}
</script>
</body>
或者另一种写法:
<body>
<ul>
<li>111111</li>
<li>111111</li>
<li>111111</li>
</ul>
<script>
window.onload = function(){
var aLi = document.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++){
aLi[i].onclick = (function(i){
return function(){
alert(i);
}
})(i) //循环的时候,这个函数表达式自执行,i也是驻扎在内存当中。
}
}
</script>
</body>
四、闭包需要注意的问题
-
在IE下会引发内存泄露
<body>
<div id="div1">my div</div>
<script>
window.onload = function(){
var oDiv = document.getElementById('div1');
oDiv.onclick = function(){
alert(oDiv.id);
}
}
</script>
</body>
当一个变量为一个dom节点获取或者宿主对象(此例子中为oDiv),它的一个属性(此例中为onclick)引用一个内部函数,而内部函数又在引用外部对象(此例中外部对象是oDiv)。会造成互相引用,引起内存泄露。
- 如何解决:
方法一:
<body>
<div id="div1">my div</div>
<script>
window.onload = function(){
var oDiv = document.getElementById('div1');
oDiv.onclick = function(){
alert(oDiv.id);
}
//卸载文档时候执行
window.onunload=function(){
oDiv.onclick = null;
}
}
</script>
</body>
方法二:
<body>
<div id="div1">my div</div>
<script>
window.onload = function(){
var oDiv = document.getElementById('div1');
//赋值给一个变量
var oId = oDiv.id;
oDiv.onclick = function(){
alert(oId);
}
//还要让对象对空
oDiv = null;
}
</script>
</body>