文章目录
- 1.引入闭包
- 2. 理解闭包
- 3.常见的闭包
- 4.闭包的作用
- 5.闭包的生命周期
- 6.闭包的应用 :定义js模块
- 7.闭包的缺点及解决
- 8. 扩散概念(内存溢出、内存泄露)
1.引入闭包
js中有两大神兽,一个是原型对象,一个是闭包
进行一个小练习:点击某个按钮,提示“点击的是第几个按钮”;
<button>测试1</button>
<button>测试2</button>
<button>测试3</button>
<script type="text/javascript">
var btns=document.getElementsByTagName("button"); //获取到的元素是放在一个伪数组中;
//第一种:遍历加监听;
for(var i=0; i<btns.length;i++)
//这样写,则需要每次去获取btns.length,下面的写法那就只需要获取一次btns.length;
for(var i=0,length=btns.length; i<length; i++){
var btn =btns[i];
btn.onclick =function (){
alert('第'+(i+1)+'个');
}
}
//for循环执行完后,i=4了。而单击响应函数还没执行;
//必须要单击才会执行,而此时i=4,则无法实现功能
//第二种:
for(var i=0,length=btns.length; i<length; i++){
btns[i].index=i;
//将数组btns的对应元素中分别保存一个下标index;
btns[i].onclick =function (){
alert('第'+(this.index+1)+'个');
}
}
//给数组中每个元素一个对应索引值,依次给数组中每个元素绑定事件,
//this.index+1为元素的对应索引值+1,就是对应的第几个
//遍历数组中的元素,分别给元素绑定事件;
//第三种:利用闭包
for(var i=0,length=btns.length; i<length; i++){
(function (i){
btns[i].onclick=function(){
alert('第'+(i+1)+'个')
}
})(i); //立即执行函数
}
//内部使用了外部的i,产生了对应的闭包,保存了对应的i值
//每次循环产生对应的闭包,闭包中保存了对应的值,单击触发用的是对应的i值。
</script>
2. 理解闭包
1.如何产生闭包?
:当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包。
2.产生闭包的条件?
*函数嵌套
*内部函数引用了外部函数的数据(变量/函数)
*要执行外部函数;
3.闭包到底是什么?
*使用chrome调试查看
*理解一:闭包是嵌套的内部函数(绝大部分人) ==》内部函数
*理解二:闭包是嵌套的内部函数中的对象(极少数人) ==》内部函数中有一个对象Closure,引用的变量放在对象Closure中;
*注意:若为理解二,则闭包存在于嵌套的内部函数中;
<script type="text/javascript">
function fn1(){
var a=2;
function fn2(){
console.log(a); //引用了外部变量
}
}
fn1();
//执行外部函数,就会产生函数执行上下文对象,则会定义函数中的变量。
//定义了变量(内部函数fn2),就产生闭包,不用调用内部函数。
</script>
3.常见的闭包
1.将一个函数作为另一个函数的返回值;
2.将函数作为实参传递给另一个函数调用;
<script type="text/javascript">
//1.将函数作为另一个函数的返回值;
function fn1(){
var a =2;
function fn2(){
a++;
console.log(a);
}
return fn2;
//将一个内部函数,作为他的外部函数的返回值;
}
var f=fn1();
//调用函数fn1,并将fn1()赋给f;
//f()相当于fn1()(),而fn1()执行返回的是fn2,所以fn1()()就相当于fn2();
//只调用了一次fn1,所以产生一个闭包;
//外部函数调用几次就产生几个闭包,就能产生几个内部函数对象;
f(); //3
f(); //4
//闭包Closure中存储着引用变量a,a的值会在里面保存;
//函数执行完a不会自动释放;
//2.将函数作为实参传递给另一个函数调用;
function showDelay(msg,time){
setTimeout(function(){
alert(msg); //引用了外部函数的数据,产生闭包。
},time);
}
showDelay('liufunan',2000);
</script>
4.闭包的作用
1.使函数内部的变量在函数执行完后,仍然存活在内部中(延长了局部变量的生命周期)。
(正常情况函数调用结束时上下文对象就会被自动释放,局部变量也会释放掉)
2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)。
<script type="text/javascript">
function fn1(){
var a =2;
function fn2(){
a++;
console.log(a);
}
function fn3(){
a--;
console.log(a);
}
return fn2;
}
var f=fn1();
f(); //3
f(); //4 则执行完后,a没有释放还是存活在闭包中的,
//这样调用,则是在函数外部去操作函数内部的数据。
//根据返回的函数不同,可以就进行不同的操作。
</script>
问题1:函数执行完后,函数内部声明的局部变量是否还存在 ??
答:一般不存在,存在于闭包中的变量才可能存在;但如果让包含闭包的函数成为了一个垃圾对象,则会被释放,闭包中的变量也会不存在了;
如上题中,局部变量fn2()、fn3()被自动释放了,而局部变量a还存在;n2()产生闭包,a被保存在闭包Closure里 ,不会释放;
问题2.在函数外部能直接访问函数内部的局部变量吗??
答:不能,但是我们可以通过闭包让外部操作它;
5.闭包的生命周期
1.产生:调用外部函数,进行预处理,定义内部函数时就产生了闭包。
(执行上下文对象开始定义嵌套内部函数时,不是在调用内部函数时)
2.死亡:在嵌套的内部函数成为垃圾对象时。
(没有变量指向它、函数=null时,成为垃圾对象)
<script type="text/javascript">
function fn1(){
var a =2;
function fn2(){ //此时闭包语句产生了(函数提升,定义函数时)
a++;
console.log(a);
}
return fn2;
}
var f=fn1();
f();
f();
f=null;
//包含闭包的内部函数成为垃圾对象,闭包死亡;
</script>
6.闭包的应用 :定义js模块
定义js模块:
*具有特定功能的js文件(操作数据)
*将所有的数据和功能都封装在一个函数内部(私有的)
*只向外暴露一个对象(里面包含n个方法的)或函数;
*模块的使用者,只需要通过模块暴露的对象,调用对象中的方法来实现对应的功能;
封装的js模块:
function my (){
//私有数据;
var str="MY dog";
//操作数据的函数;
function some(){
console.log("yes"+str);
}
function other(){
console.log("no"+str);
}
//向外暴露对象(将方法提供给外部使用)
return {
some:some,
other:other
}
}
<script type="text/javascript" src="../js/bibao.js"></script>
//引入js模块
<script type="text/javascript">
var a=my(); //返回的内容赋给a,是一个对象
a.other(); //使用js模块中的方法
</script>
//封装的js模块:
(function my (){
var str="MY dog"; //私有数据;
function some(){
console.log("yes"+str);
}
function other(){
console.log("no"+str);
}
window.bibao2={ //把要暴露的东西添加为window的属性;
some:some, //在使用时,直接调用
other:other
}
})(); //匿名函数自调用(立即执行函数)
//引入js模块
<script type="text/javascript" src="../js/bibao2.js"></script>
<script type="text/javascript">
bibao2.some(); //window的属性可以直接使用;
//这种方法更加直接,引入js文件后,就直接可以使用window的属性;
</script>
7.闭包的缺点及解决
1.缺点: (延长局部变量的生命周期,既优点,也是缺点)
*函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
*容易造成内存泄露
2.解决
*能不用闭包就不用
*用的时候要及时释放
<script type="text/javascript">
function fn1(){
var arr=new Array[]; //数组中放1000个位置
function fn2(){
console.log(arr.length);
}
return fn2;
}
var f=fn1();
f();
f=null; //让内部函数成为垃圾对象--》回收闭包 (及时释放)
</script>
8. 扩散概念(内存溢出、内存泄露)
1.内存溢出
*一种程序运行出现的错误
*当程序运行需要的内存超过剩余的内存时,就抛出内存溢出的错误;
2.内存泄露 (不是程序报错)
*占用的内存没有被及时的释放
*内存泄露积累多了就容易导致内存溢出;
*常见的内存泄露:
1.意外的全局变量
2.没有及时清理的计时器或回调函数
3.闭包
<script type="text/javascript">
//1.内存溢出
var obj={ };
for(var i=0;i<10000;i++){
obj[i]=new Array[100000]; //给每个元素10000个位置
console.log("---");
}
//内存溢出,程序运行出现的错误
//2.内存泄露
//意外的全局变量
function fn(){
a=3;
//没有var定义的变量a,虽然是在函数里面,但是一个全局变量
//这样就容易忽略释放,意外的全局变量
console.log(a);
}
fn();
//没有及时清理的计时器或回调函数
var timer=setInterval(function(){
console.log("---")
},500); //启动循环定时器,会不断的执行;
clearInterval(timer); //所以不用的时候应该及时的清理;
//闭包
function fn1(){
var a=10;
function fn2(){
console.log(a);
}
return fn2;
}
var f=fn1(); //产生闭包
f();
//f=null;
//如果不去释放的话,则闭包中的a会一直都在,则内存泄露
</script>