JavaScript—闭包

    科技2022-08-04  123

    文章目录

    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>
    Processed: 0.013, SQL: 8