Day15-JS进阶-闭包

一、闭包

 

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个

Day15-JS进阶-闭包_第1张图片

 

 所以说,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就是一个全局的变量
        }

也可以实现(循环遍历+监听、用闭包来实现)

Day15-JS进阶-闭包_第2张图片

 

 Day15-JS进阶-闭包_第3张图片

 

 这个就是一个闭包,嵌套在fn2函数里面,因为内部函数中使用了外部函数中的a

做一个测试,我们在外部函数中定义一个b,但是在内部函数中不适用的话,这个闭包里面就没有b,只是a了

Day15-JS进阶-闭包_第4张图片

 

所以就是我在内部函数中引用了外部函数中的什么变量,这个闭包里面就有他了

1、如何产生闭包:当一个嵌套的内部函数引用了嵌套的 外部函数的变量,这个变量不一定是var,也可以是一个函数

(还有一个条件,就是要执行外部函数,才会产生的),因为闭包是在内部函数的对象里面,所以要通过调用外部函数,来产生内部函数的变量才行的

 

二、下面就要利用闭包来做一些可以产生实效果的东西了(常见的闭包)

1、将函数作为另一个函数的返回值

Day15-JS进阶-闭包_第5张图片

 

 通过这个代码可以发现,两次调用f的结果可以看到a的累加,也就是a没消失,但是a是一个局部变量,为什么会没消失呢?

请问:整个过程中产生了几个闭包?

因为产生闭包就是要建立函数对象,因为只是建立了一个fn2函数对象,所以就只产生了一个闭包,并不是执行了两次f()函数就产生两个闭包的

但是如果我们想要产生两个闭包对象的话(看外部函数执行几次来判断闭包创建了一个)

Day15-JS进阶-闭包_第6张图片

 

 因为只有在执行外部函数的时候才会去创建内部函数对象的,才会创建闭包了,

也就是说在反复执行内部函数的过程中,闭包里面的数据是不变的,也就没消失了(除非又执行一次外部函数新建一个内部函数对象)

Day15-JS进阶-闭包_第7张图片

 

 ==注意,不要认为在第22行的时候,给f赋值,如何跳转到15行的时候,闭包没有产生,因为有函数提升,在执行22行之前,其实已经对fn1和fn2都进行了函数的提升然后定义了,所以在到22行然后跳转到15行的时候,其实闭包就已经存在的了

也就是说,我们执行完15行之后就会立马跳转到20行了,因为我们的fn2函数的定义早已经执行过了,所以就可以直接跳过这些代码了

到第二次调用f()的时候,闭包还是在的,也就是在这个过程中产生了一个闭包了

 

 2、第二种情况,将函数作为实参传递给另一个函数调用

因为有外部函数也有内部函数,别内部函数使用了msg这个外部变量,所以产生了闭包

Day15-JS进阶-闭包_第8张图片

 

 但是加入内部函数没使用这个外部变量的话,就没有产生闭包了

3、闭包的作用

Day15-JS进阶-闭包_第9张图片

 

 Day15-JS进阶-闭包_第10张图片

 

 在外部啊可以访问局部变量,但是不希望改变这个局部变量的值,影响到它们的话,就可以用闭包实现了

Day15-JS进阶-闭包_第11张图片

 

 3、闭包的生命周期

Day15-JS进阶-闭包_第12张图片

 

 如果是以上代码的话,闭包是在15行执行完了之后就存在了,但是如果是下面这种形式的话,是在17行,也就是赋值语句执行完的时候闭包产生的

Day15-JS进阶-闭包_第13张图片

 

4、闭包应用-自定义JS模块

   ①首先就要新建一个js的文件

 Day15-JS进阶-闭包_第14张图片

 

Day15-JS进阶-闭包_第15张图片

 

 就是如果想要调用我们js里面的东西的话,就要在js里面把这些东西暴露出来,就可以在js里面用return然后在html中通过fn来接受这个return

就可以保留主js里面的东西,不变成垃圾变量了,然后就可以调用了,但是这样return的话只能是return一个函数出来,如果我们想要把js

文件里面的两个函数都暴露出来来使用的话,要怎么办呢

也就是要用一个容器,把这两个要用的数据封装起来即可了---===就可以用对象来进行封装,然后再return出去即可了

 Day15-JS进阶-闭包_第16张图片

 

Day15-JS进阶-闭包_第17张图片

 

 这里其实是用到了闭包的

除了这种方式的话,其实还有别的方式的,下面就是用别的方法来写的

外部用匿名函数自调用

 Day15-JS进阶-闭包_第18张图片

 

 前面我们使用的是 普通的函数然后用return向外面暴露东西的,但是这里是匿名函数的自调用的话,我们要怎么样像外面暴露呢?

====吧这个要暴露的东西添加为window的属性即可了

Day15-JS进阶-闭包_第19张图片

 

 

 

Day15-JS进阶-闭包_第20张图片

 

 直接这样使用即可了

这第二种方式其实也是有闭包的

===比较:

第一种方式的话,是通过return的方式,也就是要用一个var 来借住,但是第二种方式的话就不用这个var了,直接可以调用的了

第二种方法有时候也会写成这样:

Day15-JS进阶-闭包_第21张图片

 

 也就是在收尾都加了一个window,其实平时都是用这种写法,这种写法的好处就是,在代码压缩的时候,因为会把一些变量变成是 a b c d的形式

 

5、闭包的缺点及解决

Day15-JS进阶-闭包_第22张图片

 

 也就是这个数组会被一直的占用着,着就是有写人是不知道这个情况下,也就是在不用这个闭包的时候要通过

f = null 的语句吧这个闭包释放掉的,也就是解决方法就是通过 f = null 来释放掉的

下面就是面试和笔试的时候经常文的东西了:

内存溢出与内存泄露

Day15-JS进阶-闭包_第23张图片

 

 Day15-JS进阶-闭包_第24张图片

 

 这里的就是那个内存的占用突然的上升的话,就是因为使用了大量的内存,后面的突然下降就是因为爆掉了,也就是崩溃了,就下降了

上面就是内存溢出了

下面就是内存泄漏

(也就是本来是有那么大的内存可以用的,但是因为内存泄漏了,就没那么大的内粗来使用了

====不光影响性能,当内存泄漏过多超过负荷的话,还会造成内存溢出

也就是更加容易内存溢出了

一下是内存泄漏的情况:

      1、Day15-JS进阶-闭包_第25张图片

 

 也就是在局部里面定义一个变量的时候没有使用var来定义了,这样的话就会把a变成是一个全局变量了,在函数执行完了之后,这个a也是不会消失的了

      2、就是在一些循环定时器里面,比如setInterval

Day15-JS进阶-闭包_第26张图片

 

 也就是每过一秒钟的话,就会console一些东西的了,但是很有可能会忘记之前定义了这个定时器,以至于这个函数会一直调用了

(也就是启动循环定时器之后没有及时的关闭)

也就是不想用的时候,也是要及时的清理掉才行的,

Day15-JS进阶-闭包_第27张图片

 

        3、闭包

 Day15-JS进阶-闭包_第28张图片

 

 

5、相关面试题:

Day15-JS进阶-闭包_第29张图片

 

 这个代码运行的结果是 The Window

解释:由于通过 object.getNameFunc() 得到的是一个函数,然后再在外面添加一个括号,就是运行这个函数了  

直接执行这个函数的话,函数体内部的this是window,然后在window里面的name属性的话就是My Object 了

(这个里面是没有闭包的)--但是是有函数的嵌套的

Day15-JS进阶-闭包_第30张图片

但是内部函数没有使用外部函数的变量,所以这里是没有闭包的

 

wDay15-JS进阶-闭包_第31张图片

这个代码运行的结果是 My Object

  因为这里的 that = this 中的this其实是调用getNameFunc的this,也就是obect2,也就是object2是这个this

然后这里object2里面的name2属性的话就是 My Object了

这种事情其实是经常的:就是把一个函数的this用另外一个that把它存起来的,然后在其他函数里面用这个that

(这里就有闭包)

 

Day15-JS进阶-闭包_第32张图片

闭包里面就是保存了 that 的,也就是外部函数的变量了

 

比较难的面试题:

Day15-JS进阶-闭包_第33张图片

 

 

 注意:里面的return fun调用的不是上一级的fun,而是最外面定义的fun函数的

并且这里有闭包,就是内部函数使用了外部函数变量的n,然后调用的时候,会把这个n又赋值给外部函数的o了

Day15-JS进阶-闭包_第34张图片

 

 其中

18行: undefined 0 0 0

19行:undefined 0 1 2

20行:undefined 0 1 1

 

你可能感兴趣的:(Day15-JS进阶-闭包)