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
posted @ 2019-10-05 01:36  【唐】三三  阅读(416)  评论(0编辑  收藏  举报