作者:手机用户2502937497 | 来源:互联网 | 2023-09-25 09:32
①函数作为返回值【1】例子1functiona(){varnamedov;returnfunction(){returnname;}}varba();console.log(b(
①函数作为返回值
【1】例子1
function a() {var name='dov';return function(){return name;}}var b=a();console.log(b());//dov
在这段代码中,a()中的返回值是一个匿名函数,这个函数在a()作用域内部,所以它可以获取a()作用域下变量name的值,将这个值作为返回值赋给全局作用域下的变量b,实现了在全局变量下获取到局部变量中的变量的值
【2】例子2
function fn() {var num=3;return function(){var n=0;console.log(++n);console.log(++num);}}var fn1=fn();fn1();//n=1;num=4fn1();//n=1;num=5
一般情况下,在函数fn执行完后,就应该连同它里面的变量一同被销毁,但是在这个例子中,匿名函数作为fn的返回值被赋值给了fn1,这时候相当于fn1=function(){var n = 0 ... },并且匿名函数内部引用着fn里的变量num,所以变量num无法被销毁,而变量n是每次被调用时新创建的,所以每次fn1执行完后它就把属于自己的变量连同自己一起销毁,于是乎最后就剩下孤零零的num,于是这里就产生了内存消耗的问题
【3】定时器与闭包
写一个for循环,让它按顺序打印出当前循环次数
for(var i&#61;0;i<5;&#43;&#43;i){setTimeout(function(){console.log(i&#43;&#39; &#39;)},100)
}
按照预期它应该依次输出1 2 3 4 5&#xff0c;而结果它输出了五次5&#xff0c;这是为什么呢&#xff1f;原来由于js是单线程的&#xff0c;所以在执行for循环的时候定时器setTimeout被安排到任务队列中排队等待执行&#xff0c;而在等待过程中for循环就已经在执行&#xff0c;等到setTimeout可以执行的时候&#xff0c;for循环已经结束&#xff0c;i的值也已经编程5&#xff0c;所以打印出来五个5&#xff0c;那么我们为了实现预期结果应该怎么改这段代码呢&#xff1f;&#xff08;ps:如果把for循环里面的var变成let&#xff0c;也能实现预期结果&#xff09;
for (var i &#61; 0; i <5; &#43;&#43;i) {(function (i) {setTimeout(function () {console.log(i &#43; &#39; &#39;)}, 100)}(i))}
引入闭包来保存变量i&#xff0c;将setTimeout放入立即执行函数中&#xff0c;将for循环中的循环值i作为参数传递&#xff0c;100毫秒后同时打印出1 2 3 4 5
那如果我们想实现每隔100毫秒分别依次输出数字&#xff0c;又该怎么改呢?
for (var i &#61; 1; i <5; &#43;&#43;i) {(function (i) {setTimeout(function () {console.log(i &#43; &#39; &#39;)}, 100*i)}(i))}
在这段代码中&#xff0c;相当于同时启动3个定时器&#xff0c;i*100是为4个定时器分别设置了不同的时间&#xff0c;同时启动&#xff0c;但是执行时间不同&#xff0c;每个定时器间隔都是100毫秒&#xff0c;实现了每隔100毫秒就执行一次打印的效果。
②闭包作为参数传递
var num&#61;15;var fn1&#61;function(x){if(x>num){console.log(x)}}void function(fn2){var num&#61;100;fn2(30)}(fn1)//30
在这段代码中&#xff0c;函数fn1作为参数传入立即执行函数中&#xff0c;在执行到fn2(30)的时候&#xff0c;30作为参数传入fn1中&#xff0c;这时候if(x>num)中的num取的并不是立即执行函数中的num&#xff0c;而是取创建函数的作用域中的num这里函数创建的作用域是全局作用域下&#xff0c;所以num取的是全局作用域中的值15&#xff0c;即30>15&#xff0c;打印30
最后总结一下闭包的好处与坏处
好处
①保护函数内的变量安全 &#xff0c;实现封装&#xff0c;防止变量流入其他环境发生命名冲突
②在内存中维持一个变量&#xff0c;可以做缓存&#xff08;但使用多了同时也是一项缺点&#xff0c;消耗内存&#xff09;
③匿名自执行函数可以减少内存消耗
坏处
①其中一点上面已经有体现了&#xff0c;就是被引用的私有变量不能被销毁&#xff0c;增大了内存消耗&#xff0c;造成内存泄漏&#xff0c;解决方法是可以在使用完变量后手动为它赋值为null&#xff1b;
②其次由于闭包涉及跨域访问&#xff0c;所以会导致性能损失&#xff0c;我们可以通过把跨作用域变量存储在局部变量中&#xff0c;然后直接访问局部变量&#xff0c;来减轻对执行速度的影响