zoukankan      html  css  js  c++  java
  • JS高级:闭包

    1 如何产生闭包?

    当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包(closure)

    2 闭包到底是什么?

    使用chrome调试查看

    理解一: 闭包是嵌套的内部函数

    理解二: 包含被引用变量(函数)的对象

    注意: 闭包存在于嵌套的内部函数中

    3 产生闭包的条件?

    函数嵌套

    内部函数引用了外部函数的数据(变量/函数)

    4 常见的闭包使用形式?

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

       // 1. 将函数作为另一个函数的返回值
        function fn1() {
            var num = 10;
            function fn2() {
                num++;
                console.log(num);
            }
            return fn2;
        }
    
    	// 通过全局变量引用, 保住了内部函数fn2的命
        var f = fn1();
        f(); // 11 在外部函数执行完成后, 还可以执行内部函数
    
        f(); //
    

    4.2 将函数的形参作为实参传递给另一个函数调用

    // 2. 将函数的形参作为实参传递给另一个函数调用    
         function logMsgDelay(msg, time) {
           setTimeout(function () {
             console.log(msg);
           }, time)
         }
    

    5 闭包的作用分析

    • 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
    • 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

    6 理解闭包解决同步和异步

    封闭作用域又称值为封闭空间,还有一个昵称叫小闭包,以及匿名函数自调。

    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    
        /*
        封闭作用域又称值为封闭空间,还有一个昵称叫小闭包,以及匿名函数自调。
        写法:
        (function(){})();
        ;(function(){})();
        +(function(){})();
        -(function(){})();
      */
    
    	var btns = document.getElementsByTagName('button');
        /*
            借助小闭包, 把每次循环的i值都封闭起来
        */
        for (var i = 0; i < btns.length; i++) {
            console.log('全局的i:' + i);
            (function (i) {
                console.log('局部的i:' + i);
                var btn = btns[i];
                btn.onclick = function () {
                    alert('第' + (i + 1) + '个')
                }
            })(i);
        }
    

    7 模块封装(封装全局变量)

    作用域链条
    JS中有很多作用域, 比如: 全局作用域 和 局部作用域

    1. 凡是存在作用域的地方一定有作用域链条, 变量的查找都是沿着这条链条自内而外的;
    2. 寻找变量都是递归遍历寻找, 当前作用域找不到, 就跳到上一个作用域遍历寻找, 直至顶层;
    3. 作用域链条太长, 会影响程序运行效率

    把一些不需要暴露在全局的变量封装成"私有变量"

    7.1 私有模块封装

    MyTool1.js

    function myTool() {
        // 1.私有数据
        var money =  1000;
        // 2. 操作数据的函数
        function get() {
            money++;
            console.log('赚了一笔钱, 总资产: ' + money + '元');
        }
        function send() {
            money--;
            console.log('花了一笔钱, 总资产: '+ money + '元');
        }
        //向外暴露对象(给外部使用的方法)
        return {
            'get': get,
            'send': send
        }
    }
    

    调用

        <script type="text/javascript" src="js/MyTool1.js"></script>
        <script type="text/javascript">
            var tool = myTool();
            tool.get();
            tool.send();
        </script>
    

    7.2 全局模块(window)封装

    MyTool2.js

    ;(function (window) {
        // 1.私有数据
        var money =  1000;
        // 2. 操作数据的函数
        function get() {
            money++;
            console.log('赚了一笔钱, 总资产: ' + money + '元');
        }
        function send() {
            money--;
            console.log('花了一笔钱, 总资产: '+ money + '元');
        }
    
        //向外暴露对象(给外部使用的方法)
        window.myTool = {
            get: get,
            send: send
        }
    })(window);
    
    /*
       性能考虑, 作用域链条是递归查找对象的
       压缩考虑, a,b,c,...
    */
    

    调用

    <script type="text/javascript" src="js/MyTool2.js"></script>
    <script type="text/javascript">
        myTool.get();
        myTool.send();
    </script>
    

    8 场景应用

    8.1 高级排他

    2个for循环,改为设置1个,根据下标清除

        // window.onload = function () {
        //     var allLis = document.getElementsByTagName('li');
        //     for(var i=0; i<allLis.length; i++){
        //         var li = allLis[i];
        //         li.onmouseover = function () {
        //             for(var j=0; j<allLis.length; j++){
        //                  allLis[j].className = '';
        //             }
        //             this.className = 'current';
        //         }
        //     }
        // }
    
    
        window.onload = function () {
            var allLis = document.getElementsByTagName('li');
            // 记录移动前选中li对应的索引
            var preSelectLiIndex = 0;
            for(var i=0; i<allLis.length; i++){
                (function (i) {
                    var li = allLis[i];
                    li.onmouseover = function () {
                        // 清除
                        allLis[preSelectLiIndex].className = '';
                        // 设置
                        this.className = 'current';
                        // 赋值
                        preSelectLiIndex = i;
                    }
                })(i);
            }
        }
    

    8.2 函数节流

    前面的timer作为全局变量,window指针指向它,如果有很多,影响性能。

       /*
        var timer = null;
        window.onresize = function () {
            clearTimeout(timer);
            timer = setTimeout(function () {
                console.log('输出的内容!!!!');
            }, 200);
        }
        */
    
    
       window.onresize = throttle(function () {
           console.log('大家好!!!');
       }, 200);
    
        function throttle(fn, delay) {
            var timer = null;
            return function () {
                clearTimeout(timer);
                timer = setTimeout(fn, delay);
            }
        }
    
    

    9 闭包的缺点

    1. 缺点
      函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
      容易造成内存泄露
    2. 解决
      及时释放
      function fn1() {
        var arr = new Array[999999999];
        function fn2() {
          console.log(arr.length)
        }
        return fn2
      }
      var f = fn1();
      f();
    
      f = null //让内部函数成为垃圾对象-->回收闭包
    

    10 内存管理

    10.1 内存溢出

    一种程序运行出现的错误
    当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误

        var arrObj = {};
        for (var i = 0; i < 10000; i++) {
            arrObj[i] = new Array(9999999999999);
            console.log(arrObj);
        }
    

    10.2 内存泄露

    占用的内存没有及时释放
    内存泄露积累多了就容易导致内存溢出
    常见的内存泄露:
    1. 占用内存很大的全局变量
    2. 没有及时清理的计时器/定时器
    3. 闭包

    	// 2. 内存泄露
        // 2.1 占用内存很大的全局变量
        /*
        var num = new Array(9999999999999);
        console.log(num);
        */
    
        // 2.2 没有及时清理的计时器或回调函数
        /*
        var intervalId = setInterval(function () { //启动循环定时器后不清理
           console.log('----')
         }, 1000);
         clearInterval(intervalId);
         */
    
        // 2.3 闭包
        /*function fn1() {
          var num = 111;
          function fn2() {
            console.log(num--);
          }
          return fn2
        }
        var f = fn1();
        f();*/
    
        // f = null
    
  • 相关阅读:
    linq to sql 扩展方法
    跨线程的安全更新控件
    WinForm程序启动控制台窗口Console
    Winfrom巧用Using设置鼠标为WaitCursor
    在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。
    基于Token的身份验证——JWT(转)
    jwt算法
    session问题总既然(深入理解)&Token问题理解&sso单点登陆理解实现
    1.spring boot要求最低jdk1.8,平安默认1.6问题,-》安装JDK1.8 2.maven 3.3.3要求最低jdk1.7->安装jdk 1.8
    批量插入删除
  • 原文地址:https://www.cnblogs.com/tangge/p/11623830.html
Copyright © 2011-2022 走看看