zoukankan      html  css  js  c++  java
  • Day15-JS进阶-闭包

    一、闭包

    1、引入:实现一个小案例,有三个按钮,点击哪一个就提示点击的是哪个按钮

      ①先要拿到这三个按钮,然后遍历它们,给它们添加上监听函数

    (这里得到的btns其实不是一个数组,而是一个伪数组来的)

    ==要初始化一下length才行,不然放在for的第二个参数中的话,会计算length次的btns.length

    <body>
        <button>测试1</button>
        <button>测试2</button>
        <button>测试3</button>
        <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>测试1</button>
        <button>测试2</button>
        <button>测试3</button>
        <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)+'个');
            //     }
            // }
            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 来释放掉的

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

    内存溢出与内存泄露

     

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

    上面就是内存溢出了

    下面就是内存泄漏

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

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

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

    一下是内存泄漏的情况:

          1、

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

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

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

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

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

            3、闭包

     

    5、相关面试题:

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

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

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

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

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

    w

    这个代码运行的结果是 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

  • 相关阅读:
    【SCOI2012】滑雪
    【NOI2008】假面舞会
    ※初赛知识总结※
    【FJSC2012】足球
    【中山市选2011】杀人游戏
    【SDOI2008】洞穴勘测
    【SNOI2017】炸弹
    【LGOJ1606】白银莲花池
    int类型中为什么负数比正数多了一个数?
    utf8、utf16、utf32之间的格式
  • 原文地址:https://www.cnblogs.com/SCAU-gogocj/p/13231319.html
Copyright © 2011-2022 走看看