什么是闭包?
简单点说,闭包就是函数嵌套函数,其中内部函数可以引用外部函数的参数和变量。
function aaa(a){
var b = 5;
function bbb(){
console.log(a);
console.log(b);
}
return bbb;
}
var ccc = aaa(3);
ccc();// 3 5
垃圾回收机制
垃圾回收机制:在闭包的情况下,参数 a
和 变量 b
是不会被垃圾回收机制回收的,因为内部的闭包还在引用着外部的变量。
//js 中的垃圾回收机制
function aaa(){
var a = 1;
}
//当这个函数调用完了之后,函数里面的变量就会被垃圾回收机制回收
aaa();
//垃圾回收机制测试
function aaa(){
var a = 5;
function bbb(){
alert(a);
}
return bbb;
}
// aaa 函数已经执行完毕了,这个时候 ccc 代表的是 bbb 这个函数
var ccc = aaa();
//这里执行的其实是bbb();
ccc();// result : 5
//由上述的结果可知:闭包内的变量并不会被垃圾回收机制回收
闭包有什么作用?
作用一:封装
闭包可以做到私有成员,私有方法,减少全局变量的污染来达到你想要的效果。
var a = 1;
function aaa(){
a++;
alert(a);
}
aaa();//2
aaa();//3
//在这里 a 是全局变量所以我们可以在函数内部使其自增
//如果我们把 a 变成一个局部变量
function aaa(){
var a = 1;
a++;
alert(a);
}
aaa();//2
aaa();//2
//在这里 a 只是函数内部的一个局部变量,当我们调用函数的时候不能实现其自增。
如何做到让 a
既是局部变量同时我们在外部调用的时候让 a
又可以累加?这就是我们的闭包可以做到的。
写法一:
function aaa(){
var a = 1;
return function (){
a++;
alert(a);
}
}
var c = aaa();
c();//2
c();//3
//可以看到当我们在使用闭包的写法的时候,可以做到让 a 既是局
//部变量同时在外部调用函数时,又可以实现其累加
写法二:(改写为函数声明)
//改写为函数声明
(function (){
var a = 1;
return function (){
a++;
alert(a);
}
}
)()();
也可以像下面这样改写:
var aaa = function (){
var a = 1;
return function (){
a++;
alert(a);
}
}();
aaa();//2
aaa();//3
//这就是模块化代码,通过减少全局变量的污染来达到想要的效果
模块化代码模型
var aaa = function(){
//局部变量
var a = 1;
//私有方法
function bbb(){
a++;
alert(a);
}
//私有方法
function ccc(){
a++;
alert(a);
}
return {
b : bbb,
c : ccc
}
}();
aaa.b();//2
aaa.c();//3
作用二:在循环中直接找到对应元素的下标不需要再加索引
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
window.onload = function(){
var aLi = document.getElementsByTagName('li');
for(var i=0; i<aLi.length; i++){
aLi[i].onclick = function(){
alert(i);//3
//为什么这里会弹出3 因为当循环执行结束的时候,这个点击事件其实还没有执行,只有当我
//们触发的时候,它才会执行,但是当我们去点的时候,这个 for 循环已经结束了,这个
//时候 i 已经变成3了,所以不管你触发哪一个li最终弹出来的都是3
}
}
}
用闭包来解决这个问题:(第一种写法)
window.onload = function(){
var aLi = document.getElementsByTagName('li');
//第一种写法
for(var i=0; i<aLi.length; i++){
(function(i){
aLi[i].onclick = function(){
alert(i);
}
})(i);
}
}
用闭包来解决这个问题:(第二种写法)
window.onload = function(){
var aLi = document.getElementsByTagName('li');
//第二种写法
for(var i=0; i<aLi.length; i++){
//当你点击的时候,我这个里面已经执行完毕了,所以这个 i 已经
//驻扎在内部了,所以点击的时候,你调用的 i 是内部的函数已经
//存在内存中的 i了,而不是外面的 i
aLi[i].onclick = (function(i){
return function(){
alert(i);
}
})(i);
}
}
注意事项:内存泄漏
在 ie
下使用闭包会引发内存泄漏。
引发内存泄漏的条件:当一个变量,这个变量是由获取一个 dom
节点,或者是一个宿主对象的时候,他的一个属性,比如说 onclick
去引用一个内部函数,而这个内部函数又去引用外部的对象,这个时候就会造成内存泄漏。即 ie
下的 js
互相引用会造成内存泄漏。
<body>
<div id="div1"></div>
<script>
window.onload = function(){
var oDiv = document.getElementById('div1');
oDiv.onclick = function(){
alert(oDiv.id);
}
//就是这个简单的程序可能会造成 ie 下的内存泄漏,如果内存泄漏的
//话,当你页面跳转的时候,变量不会释放,一直存在于内存中,使你
//的 cpu 在累加,大大降低了浏览器的性能,只有当你关闭浏览器的
//情况下才能释放内存。
}
</script>
</body>
解决方法如下:
<body>
<div id="div1"></div>
<script type="text/javascript">
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">
</div>
<script type="text/javascript">
window.onload = function(){
var oDiv = document.getElementById('div1');
//第二种解决方法:在外部建一个变量引用一下
var id = oDiv.id;
oDiv.onclick = function(){
alert(id);
}
//调用完之后还要把这个对象置空
oDiv = null;
}
</script>
</body>