13.1在JavaScript中定义,根据词法作用域的规则,内部函数总是可以访问其外部函数声明的变量。当调用外部函数返回内部函数后,即使外部函数已经执行完毕,内部函数引用外部函数的变量仍然保存在内存中,这些变量的集合称为闭包。13.2闭包实现将一个函数嵌套在另一个函数中或将匿名函数作为值传递给另一个函数。//fun2嵌套在fun1中,fun2作为参数返回,外部调用时仍然可以打印val1,形成一个闭包functionfun1(){constval1=10;functionfun2(){console.log(val1);}returnfun2;}functionfun3(){constval2=20;//定时器是一个匿名函数,作为参数传入。函数fun3执行后,1秒后会执行定时器函数,但此时也可以打印val2形成闭包setTimeout(function(){console.log(val2);},1000);}13.3流程我们按照下面的函数functionmain(){constval1=20;varval2=2functionvalResult(){returnval1*val2;}returnvalResult;}varresult=main();console.log(result());//40上图是各时期的调用栈,需要重点关注以下几点:main函数执行时,从栈顶弹出main函数的执行上下文;返回的方法(valResult)调用main函数中的val1和val2变量,这两个变量会被打包成一个闭包闭包添加到[[scopes]]中;call返回方法时作用域链为:resultfunctionscope——Closure(main)——globalscope13.4优点和缺点优点(1)变量可以重用,不会造成变量污染;(2)可用于定义私有属性和私有方法的缺点(1)会生成未被销毁的上下文,导致栈/堆内存消耗过大(2)会造成内存泄漏。延伸:闭包是如何回收的?如果闭包引入的函数是全局变量,则闭包会一直存在,直到页面关闭;但是如果以后不再使用闭包,就会造成内存泄漏;包的函数是一个局部变量。函数销毁后,JavaScript引擎下次执行垃圾回收时,判断闭包内容不再使用,js引擎的垃圾回收器会对其进行回收。13.5PurposeClosure主要有以下两个目的:创建私有变量functionMyName(name){return{getName(){returnname;}}}constmyName=MyName('lili');//对应的name只能通过getName访问,其他方法无法访问console.log(myName.getName());//lili作为回调函数。当函数作为值传递到某处并在某个点回调时,将创建一个闭包。示例包括计时器、DOM事件侦听器和Ajax请求。functionfun(name){setTimeout(()=>{console.log(name);},1000);}fun('linlin');13.6多个子函数都同时指向Parent的经典闭包问题[[scope]],完全共享。所以当父变量对象被修改时,所有的子函数都会受到影响。for(vari=1;i<5;i++){setTimeout(()=>console.log(i),1000);}上面的代码本意是输出1,2,3,4,但是结果是四5、为了解决这个问题,主要有以下三种方式。变量可以作为函数参数传入,避免使用默认的[[scope]]查找(vari=1;i<5;i++){(function(i){setTimeout(()=>console.log(i),1000);})(i);}用setTimeout包裹,作为第三个参数传入。(注:setTimeout后可以有多个参数,从第三个参数开始,作为回调函数的附加参数)for(vari=1;i<5;i++){setTimeout(value=>console.log(value),1000,i);}使用块级作用域,让变量成为自己上下文的属性,避免共享for(leti=1;i<5;i++){setTimeout(()=>console.log(i),1000);}本文转载自微信公众号“风筝风筝”,可通过以下二维码关注。转载本文请联系风筝持有人公众号。
