• 279 闭包 :理解闭包,常见的闭包,闭包的作用,闭包的生命周期,闭包应用(模块化、循环遍历加监听、JS框架),闭包的缺点及解决方案,练习题


    1、理解

    • 当嵌套的内部函数引用了外部函数的变量时,就产生了闭包
    • 通过chrome工具得知: 闭包本质是内部函数中的一个对象, 这个对象中包含引用的变量属性

    01_理解闭包

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>01_理解闭包</title>
    </head>
    
    <body>
        <!--
    1. 如何产生闭包?
      * 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
    2. 闭包到底是什么?
      * 使用chrome调试查看
      * 理解一: 闭包是嵌套的内部函数(绝大部分人)
      * 理解二: 包含被引用变量(函数)的对象(极少数人)
      * 注意: 闭包存在于嵌套的内部函数中
    3. 产生闭包的条件?
      * 函数嵌套
      * 内部函数引用了外部函数的数据(变量/函数)
    -->
        <script type="text/javascript">
            function fn1() {
                var a = 2
                var b = 'abc'
    
                function fn2() { // 执行函数定义就会产生闭包(不用调用内部函数)
                    console.log(a)
                }
                // fn2()  // 经测试,这里要执行fn2(),才会在断点调试中看到闭包
            }
            fn1()
    
            function fun1() {
                var a = 3;
                // 这种函数表达式不需要调用fun2,就会在断点调试中看到闭包
                var fun2 = function() {
                    console.log(a)
                }
            }
            fun1()
        </script>
    </body>
    
    </html>
    

    02_常见的闭包

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>02_常见的闭包</title>
    
    </head>
    
    <body>
        <!--
    1. 将函数作为另一个函数的返回值
    2. 将函数作为实参传递给另一个函数调用
    -->
        <script type="text/javascript">
            // 1. 将函数作为另一个函数的返回值
            function fn1() {
                var a = 2
    
                function fn2() {
                    a++
                    console.log(a)
                }
                return fn2
            }
    
            // 每次调用fn1,都会返回fn2,也就是每次调用fn1,都会创建一个闭包,因为只有执行外部函数的时候,才会创建内部函数对象
            // 之所以执行完外层函数后,外层函数的局部变量没有消失,就是因为返回的内层函数还在引用它
            var f = fn1()
            var f2 = fn1()
            f() // 3
            f() // 4
            f2() // 3
            f2() // 4
    
    
            // 2. 将函数作为实参传递给另一个函数调用
            function showDelay(msg, time) {
                setTimeout(function() {
                    alert(msg)
                }, time)
            }
            showDelay('哈哈', 2000)
        </script>
    </body>
    
    </html>
    

    2、作用

    • 延长局部变量的生命周期
    • 让函数外部能操作内部的局部变量

    03_闭包的作用

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>03_闭包的作用</title>
    
    </head>
    
    <body>
        <!--
    1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
    2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
    
    问题:
      1. 函数执行完后, 函数内部声明的局部变量是否还存在?  一般是不存在, 存在于闭中的变量才可能存在
      2. 在函数外部能直接访问函数内部的局部变量吗? 不能, 但我们可以通过闭包让外部操作它
    -->
        <script type="text/javascript">
            function fn1() {
                var a = 2
    
                function fn2() {
                    a++
                    console.log(a);
                    // return a
                }
    
                function fn3() {
                    a--
                    console.log(a)
                }
                return fn3
            }
            var f = fn1()
            f() // 1
            f() // 0
        </script>
    
    </body>
    
    </html>
    

    3、闭包的生命周期

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>04_闭包的生命周期</title>
    
    </head>
    
    <body>
        <!--
    1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
    2. 死亡: 在嵌套的内部函数成为垃圾对象时
    -->
        <script type="text/javascript">
            function fn1() {
                // 此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
                var a = 2
                function fn2() {
                    a++
                    console.log(a)
                }
                return fn2
            }
            var f = fn1()
            f() // 3
            f() // 4
            f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
        </script>
    </body>
    
    </html>
    

    4、写一个闭包程序

    function fn1() {
      var a = 2;
      function fn2() {
        a++;
        console.log(a);
      }
      return fn2;
    }
    var f = fn1();
    f();
    f();
    

    5、闭包应用

    • 循环遍历加监听
    • 模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为
    • JS框架(jQuery)大量使用了闭包

    循环遍历加监听

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>00_引入</title>
    </head>
    
    <body>
    
        <button>测试1</button>
        <button>测试2</button>
        <button>测试3</button>
        <!--
    需求: 点击某个按钮, 提示"点击的是第n个按钮"
    -->
        <script type="text/javascript">
            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所对应的下标保存在btn上
                  btn.index = i
                  btn.onclick = function () {
                    alert('第'+(this.index+1)+'个')
                  }
                }*/
    
            //利用闭包
            for (var i = 0, length = btns.length; i < length; i++) {
                (function(j) {
                    var btn = btns[j]
                    btn.onclick = function() {
                        alert('第' + (j + 1) + '个')
                    }
                })(i)
            }
        </script>
    </body>
    
    </html>
    

    05_闭包的应用_自定义JS模块

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>05_闭包的应用_自定义JS模块</title>
    </head>
    
    <body>
        <!--
    闭包的应用2 : 定义JS模块
      * 具有特定功能的js文件
      * 将所有的数据和功能都封装在一个函数内部(私有的)
      * 只向外暴露一个包含n个方法的对象或函数
      * 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
    -->
        <script type="text/javascript" src="myModule.js"></script>
        <script type="text/javascript">
            var module = myModule()
            module.doSomething() // doSomething() HAHA
            module.doOtherthing() // doSomething() haha
        </script>
    </body>
    
    </html>
    

    myModule.js

    function myModule() {
        //私有数据
        var msg = 'haha'
            //操作数据的函数
        function doSomething() {
            console.log('doSomething() ' + msg.toUpperCase())
        }
    
        function doOtherthing() {
            console.log('doOtherthing() ' + msg.toLowerCase())
        }
    
        //向外暴露对象(给外部使用的方法)
        return {
            doSomething: doSomething,
            doOtherthing: doOtherthing
        }
    }
    

    05_闭包的应用_自定义JS模块2

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>05_闭包的应用_自定义JS模块2</title>
    </head>
    
    <body>
        <!--
    闭包的应用2 : 定义JS模块
      * 具有特定功能的js文件
      * 将所有的数据和功能都封装在一个函数内部(私有的)
      * 只向外暴露一个包信n个方法的对象或函数
      * 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
    -->
        <script type="text/javascript" src="myModule2.js"></script>
        <script type="text/javascript">
            myModule2.doSomething() // doSomething() HAHA
            myModule2.doOtherthing() // doSomething() haha
        </script>
    </body>
    
    </html>
    

    myModule2.js

    (function() {
        //私有数据
        var msg = 'haha'
            //操作数据的函数
        function doSomething() {
            console.log('doSomething() ' + msg.toUpperCase())
        }
    
        function doOtherthing() {
            console.log('doOtherthing() ' + msg.toLowerCase())
        }
    
        //向外暴露对象(给外部使用的方法)
        window.myModule2 = {
            doSomething: doSomething,
            doOtherthing: doOtherthing
        }
    })()
    

    6、闭包的缺点及解决方案

    • 变量占用内存的时间可能会过长
    • 可能导致内存泄露 【白白占用内存】
    • 解决:
      • 及时释放 : f = null; //让内部函数对象成为垃圾对象

    06_闭包的缺点及解决

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>06_闭包的缺点及解决</title>
    </head>
    
    <body>
        <!--
    1. 缺点
      * 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
      * 容易造成内存泄露
    2. 解决
      * 能不用闭包就不用
      * 及时释放
    -->
        <script type="text/javascript">
            function fn1() {
                var arr = new Array[100000]
    
                function fn2() {
                    console.log(arr.length)
                }
                return fn2
            }
            var f = fn1()
            f()
    
            f = null // 让内部函数成为垃圾对象-->回收闭包
        </script>
    </body>
    
    </html>
    

    闭包练习题

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>07_练习题1</title>
    </head>
    
    <body>
    
        <script type="text/javascript">
            //代码片段一
            var name = "The Window";
            var object = {
                name: "My Object",
                // 没有闭包,内层函数没有引用外层函数的变量
                getNameFunc: function() {
                    return function() {
                        return this.name;
                    };
                }
            };
            // object.getNameFunc()得到内层函数,然后直接执行内层函数,this指向window
            alert(object.getNameFunc()()); //?  the window
    
    
            //代码片段二
            var name2 = "The Window";
            var object2 = {
                name2: "My Object",
                // 有闭包,内层函数有引用外层函数的变量
                getNameFunc: function() {
                    // 这个this就是getNameFunc的调用者
                    var that = this;
                    return function() {
                        return that.name2;
                    };
                }
            };
            alert(object2.getNameFunc()()); //?  my object
        </script>
    </body>
    
    </html>
    
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>07_练习题2</title>
    </head>
    
    <body>
    
        <script type="text/javascript">
            function fun(n, o) {
                console.log(o)
                return {
                    // 对象方法的fun名字和函数fun无关就行
                    fun: function(m) {
                        return fun(m, n)
                    }
                }
            } 
            var a = fun(0)
            a.fun(1)
            a.fun(2)
            a.fun(3) //undefined,0,0,0
    
            var b = fun(0).fun(1).fun(2).fun(3) //undefined,0,1,2
    
            var c = fun(0).fun(1)
            c.fun(2)
            c.fun(3) //undefined,0,1,1
        </script>
    </body>
    
    </html>
    
  • 相关阅读:
    Java中接口对象实现回调
    推荐算法之计算相似度
    mahout入门实例2-Mahout单机开发环境介绍(参考粉丝日志)
    mahout入门实例-基于 Apache Mahout 构建社会化推荐引擎-实战(参考IBM)
    windows下gvim使用及常见命令
    一道C语言的问题(转)
    android开发手记一
    数据结构之有关图的算法(图的邻接表示法)
    Week of Code:GG
    HDU 5587:Array
  • 原文地址:https://www.cnblogs.com/jianjie/p/12249790.html
Copyright © 2020-2023  润新知