JS闭包总结
目录给这呢
- JS闭包总结
- 定义
- 用debuuger观察闭包
- 闭包的作用
- 延长变量的生命周期
- 闭包的经典应用
- 经典应用1实现节流函数
- 让函数只能被调用指定次数
- 经典应用3
- 闭包与内存泄漏
定义
定义
在A函数中定义一个B函数(函数的嵌套定义),在B函数中使用了A函数中的变量,就会产生闭包。具体来说,就是B就是一个闭包。
闭包的三大特点为:
1函数嵌套函数
内部函数可以访问外部函数的变量
参数和变量不会被回收。
注意:
1.嵌套定义
2.引用变量
用debuuger观察闭包
下面这个简单的js函数里就构成啦闭包
我们来利用调试器看看闭包的产生
如果没有变量引用,则也不会看到闭包。
闭包的作用
何时使用
变量既想反复使用,又想避免全局污染
如何使用?
定义外层函数,封装被保护的局部变量。
定义内层函数,执行对外部函数变量的操作。
外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。
1 延长变量生命周期。
上面的代码中,闭包B会延长变量i的生命周期,它有能力可以让i活的更久一些。这里的i 是A的局部变量,它正常的生命周期是函数A的调用过程。
在调用A的过程中,由于i是它的局部变量,所以A会向内存申请一个空间来放i,但当A调用结束后,这个空间会回收,即i就死了。
闭包B的能力就是可以让函数A()执行完成之后,i仍然活着!!!!实现这个能力还需要有一个帮手:return !
延长变量的生命周期
可以看到,每次调用r(),都可以对i的值进行++,再输出来。这就说明,A()调用之后,i 并没有死掉了,它还活着。
原因如下
还没有开始调用A()
接下来,开始调用A()A函数内部,有两个局部变量:B 它还是引用类型的。要用到堆区所以A会去申请空间
接下来,执行return B
结果如下:
由于在B的函数体中用到变量i ,所以i的空间不能被回收。即i的生命周期被延长了。
再次观察调试面板:
由于在函数B的内部它用到它外部的变量i,在这个函数B没有死掉之前,i是不会消失的。
闭包的经典应用
经典应用1实现节流函数
在mui的buffer()函数中,我们提到了节流函数。节流函数就是一个函数在指定的时间间隔内,只能被调用一次。
例如,f函数,在1000ms内,只能调用一次。换句话,此时你调用f,则下一次再调用要等1000ms。本质上就降低函数执行的频率。
要求:1000ms内只能执行一次f.
改进如下:
上面的代码起到了降频的效果。
分析:
Var f = A():执行完成,
下面执行
Div.addEventListenter(“mousemove” ,f)
这一句执行之后,则如果鼠标在div上移动就会去调用f,
分析一下调用f的过程。
下面的代码就是f的函数体:
比较当前的时间与第一次保存的now的值之间是否>500( 表示时间过了500ms ).如果成立,则执行核心语句: Console.info(“鼠标移动中…”)
同时更新最近一次执行f的时间。Now = Date.now(); 由于now没有回收,所以它还可以正常访问的。
如果不成立(说明时间没有过500ms)
下面是一个节流函数
让函数只能被调用指定次数
经典应用3
上面点击每一个li都会输出3
解决办法 1:使用自定义属性
解决办法 2:使用闭包
(1)iife。在function的前面有=号。
lis[i].onclick = function(index){}(i)
(2)闭包结构。
a) function的嵌套
b) 在子函数中,用到了index,而index是父函数的形参,就相当于是父数的局部变量。所以 ,也就是在子函数中使用了父函数中定义的变量。
c) Return 子函数。
闭包与内存泄漏
一般来说,在函数内部定义的变量,会随着函数的调用结束而被系统回收,如下:
Function f(){
Var i ;
}
F();
在F()执行完成后,i就不能再访问到了 。
由于闭包结构的存在,如下:
Function f(){
Var i ;
Return function(){
Console.info( i):
}
}
Var r = f();
R();
上面的代码中,由于r()在执行时,还需要用到变量i,这个变量i并不会被系统回收。