一、闭包
1、引入:实现一个小案例,有三个按钮,点击哪一个就提示点击的是哪个按钮
①先要拿到这三个按钮,然后遍历它们,给它们添加上监听函数
(这里得到的btns其实不是一个数组,而是一个伪数组来的)
==要初始化一下length才行,不然放在for的第二个参数中的话,会计算length次的btns.length
<body> <button>测试1button> <button>测试2button> <button>测试3button> <script> var btns = document.getElementsByTagName('button'); for(var i=0,length=btns.length;i<length;i++){ var btn = btns[i]; btn.onclick = function(){ alert('第'+(i+1)+'个'); } } script> body>
通过这样的话,理论上可以,但是运行的话,点击每一个按钮,都是显示:第4个
所以说,i==3,也就是给btn点击事件定义的函数是在i==3的时候才执行了,也就是说当这个函数执行的时候,循环已经执行完了
也就是说这种方法是不行的了
解决方法就是,我们把btn和i对应起来,也就是每次循环到了之后,就把btn的index设置为这个i,然后最后函数执行的时候,就可以直接用这个btn的index了
<body> <button>测试1button> <button>测试2button> <button>测试3button> <script> var btns = document.getElementsByTagName('button'); // for(var i=0,length=btns.length;i// var btn = btns[i]; // btn.onclick = function(){ // alert('第'+(i+1)+'个'); // } // } for(var i=0,length=btns.length;i<length;i++){ var btn = btns[i]; btn.index = i; btn.onclick = function(){ alert('第'+(this.index+1)+'个'); } } script> body>
即可实现了
方法三:把刚刚失败的方法放在一个匿名函数自调用中了(用到了闭包)
for(var i=0,length=btns.length;i<length;i++){ (function(i){ //这个i和for里面的i是不冲突的,这里是局部变量,for的是全局变量 var btn = btns[i]; btn.onclick = function(){ alert('第'+(i+1)+'个'); } })(i)//而这个i就是一个全局的变量 }
也可以实现(循环遍历+监听、用闭包来实现)
这个就是一个闭包,嵌套在fn2函数里面,因为内部函数中使用了外部函数中的a
做一个测试,我们在外部函数中定义一个b,但是在内部函数中不适用的话,这个闭包里面就没有b,只是a了
所以就是我在内部函数中引用了外部函数中的什么变量,这个闭包里面就有他了
1、如何产生闭包:当一个嵌套的内部函数引用了嵌套的 外部函数的变量,这个变量不一定是var,也可以是一个函数
(还有一个条件,就是要执行外部函数,才会产生的),因为闭包是在内部函数的对象里面,所以要通过调用外部函数,来产生内部函数的变量才行的
二、下面就要利用闭包来做一些可以产生实效果的东西了(常见的闭包)
1、将函数作为另一个函数的返回值
通过这个代码可以发现,两次调用f的结果可以看到a的累加,也就是a没消失,但是a是一个局部变量,为什么会没消失呢?
请问:整个过程中产生了几个闭包?
因为产生闭包就是要建立函数对象,因为只是建立了一个fn2函数对象,所以就只产生了一个闭包,并不是执行了两次f()函数就产生两个闭包的
但是如果我们想要产生两个闭包对象的话(看外部函数执行几次来判断闭包创建了一个)
因为只有在执行外部函数的时候才会去创建内部函数对象的,才会创建闭包了,
也就是说在反复执行内部函数的过程中,闭包里面的数据是不变的,也就没消失了(除非又执行一次外部函数新建一个内部函数对象)
==注意,不要认为在第22行的时候,给f赋值,如何跳转到15行的时候,闭包没有产生,因为有函数提升,在执行22行之前,其实已经对fn1和fn2都进行了函数的提升然后定义了,所以在到22行然后跳转到15行的时候,其实闭包就已经存在的了
也就是说,我们执行完15行之后就会立马跳转到20行了,因为我们的fn2函数的定义早已经执行过了,所以就可以直接跳过这些代码了
到第二次调用f()的时候,闭包还是在的,也就是在这个过程中产生了一个闭包了
2、第二种情况,将函数作为实参传递给另一个函数调用
因为有外部函数也有内部函数,别内部函数使用了msg这个外部变量,所以产生了闭包
但是加入内部函数没使用这个外部变量的话,就没有产生闭包了
3、闭包的作用
在外部啊可以访问局部变量,但是不希望改变这个局部变量的值,影响到它们的话,就可以用闭包实现了
3、闭包的生命周期
如果是以上代码的话,闭包是在15行执行完了之后就存在了,但是如果是下面这种形式的话,是在17行,也就是赋值语句执行完的时候闭包产生的
4、闭包应用-自定义JS模块
①首先就要新建一个js的文件
就是如果想要调用我们js里面的东西的话,就要在js里面把这些东西暴露出来,就可以在js里面用return然后在html中通过fn来接受这个return
就可以保留主js里面的东西,不变成垃圾变量了,然后就可以调用了,但是这样return的话只能是return一个函数出来,如果我们想要把js
文件里面的两个函数都暴露出来来使用的话,要怎么办呢
也就是要用一个容器,把这两个要用的数据封装起来即可了---===就可以用对象来进行封装,然后再return出去即可了
这里其实是用到了闭包的
除了这种方式的话,其实还有别的方式的,下面就是用别的方法来写的
外部用匿名函数自调用
前面我们使用的是 普通的函数然后用return向外面暴露东西的,但是这里是匿名函数的自调用的话,我们要怎么样像外面暴露呢?
====吧这个要暴露的东西添加为window的属性即可了
直接这样使用即可了
这第二种方式其实也是有闭包的
===比较:
第一种方式的话,是通过return的方式,也就是要用一个var 来借住,但是第二种方式的话就不用这个var了,直接可以调用的了
第二种方法有时候也会写成这样:
也就是在收尾都加了一个window,其实平时都是用这种写法,这种写法的好处就是,在代码压缩的时候,因为会把一些变量变成是 a b c d的形式
5、闭包的缺点及解决
也就是这个数组会被一直的占用着,着就是有写人是不知道这个情况下,也就是在不用这个闭包的时候要通过
f = null 的语句吧这个闭包释放掉的,也就是解决方法就是通过 f = null 来释放掉的
下面就是面试和笔试的时候经常文的东西了:
内存溢出与内存泄露
这里的就是那个内存的占用突然的上升的话,就是因为使用了大量的内存,后面的突然下降就是因为爆掉了,也就是崩溃了,就下降了
上面就是内存溢出了
下面就是内存泄漏
(也就是本来是有那么大的内存可以用的,但是因为内存泄漏了,就没那么大的内粗来使用了
====不光影响性能,当内存泄漏过多超过负荷的话,还会造成内存溢出
也就是更加容易内存溢出了
一下是内存泄漏的情况:
也就是在局部里面定义一个变量的时候没有使用var来定义了,这样的话就会把a变成是一个全局变量了,在函数执行完了之后,这个a也是不会消失的了
2、就是在一些循环定时器里面,比如setInterval
也就是每过一秒钟的话,就会console一些东西的了,但是很有可能会忘记之前定义了这个定时器,以至于这个函数会一直调用了
(也就是启动循环定时器之后没有及时的关闭)
也就是不想用的时候,也是要及时的清理掉才行的,
3、闭包
5、相关面试题:
这个代码运行的结果是 The Window
解释:由于通过 object.getNameFunc() 得到的是一个函数,然后再在外面添加一个括号,就是运行这个函数了
直接执行这个函数的话,函数体内部的this是window,然后在window里面的name属性的话就是My Object 了
(这个里面是没有闭包的)--但是是有函数的嵌套的
但是内部函数没有使用外部函数的变量,所以这里是没有闭包的
这个代码运行的结果是 My Object
因为这里的 that = this 中的this其实是调用getNameFunc的this,也就是obect2,也就是object2是这个this
然后这里object2里面的name2属性的话就是 My Object了
这种事情其实是经常的:就是把一个函数的this用另外一个that把它存起来的,然后在其他函数里面用这个that
(这里就有闭包)
闭包里面就是保存了 that 的,也就是外部函数的变量了
比较难的面试题:
注意:里面的return fun调用的不是上一级的fun,而是最外面定义的fun函数的
并且这里有闭包,就是内部函数使用了外部函数变量的n,然后调用的时候,会把这个n又赋值给外部函数的o了
其中
18行: undefined 0 0 0
19行:undefined 0 1 2
20行:undefined 0 1 1