闭包的基本概念

153 阅读4分钟

什么是闭包: (closure)

  1. 闭包实际上就是一种函数,所以闭包技术也是函数技术的一种,闭包能做的事情函数几乎都能做
  2. 闭包技术花式比较多,用法也比较灵活,一般开发人员在学习闭包的时候都会遇到瓶颈,主要是因为闭包技术的分界线并不明显,几乎无法用一个特点去区分
  3. 当一个嵌套的内部函数引用了嵌套的外部函数的变量(函数)时,就产生了闭包
  4. 解决全局变量污染,性能提升等等

如何产生闭包:

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

闭包到底是什么:

使用chrome调试看看

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

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

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

产生闭包的条件:

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

常见的闭包使用形式:

  1. 将函数作为另一个函数的返回值
  2. 将函数的形参作为实参传递给另一个函数调用

闭包的内存管理

  1. 垃圾回收机制处理方式
  2. 针对闭包来做怎么样的处理
function fn1() {
        var num = 10;
        function fn2() {
            console.log(num);
            
        }
    };

    fn1();

常见的闭包使用形式:

  1. 将函数作为另一个函数的返回值
  2. 将函数的形参作为实参传递给另一个函数调用
  3. 通过闭包来保住大的函数里局部函数(和变量)的生命周期

让小的函数作为大的函数的返回值

1.将函数作为另一个函数的返回值:
     function fn1() {
         var num = 10;
         function fn2() {
             num++;
             console.log(num);
         }

         return fn2;
     };

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

        logMsgDelay('啊啊啊',1000);

闭包的应用-定义JS模块

  • 将所有的数据和功能都封装在一个函数内部(私有)
  • 只向外暴露一个包含多个方法的对象或函数
  • 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
var toolObj = myTool();
toolObj.get();
toolObj.send();

闭包应用-封装全局作用域:

作用域链条:全局作用域和局部作用域

  1. 凡是存在作用域的地方一定有作用域链条,变量的查找都是沿着这条作用域链条自内而外的
  2. 寻找变量都是递归遍历寻找,当前作用域找不到,就到上一个作用域遍历寻找,直至顶层
  3. 作用域链条太长,会影响程序运行效率
  4. 把一些不需要暴露在全局的变量封装成“私有变量”
(function () {
        var str = '张三';
        console.log(str);
        
    })();

    (function () {
        var str = '王五';
        console.log(str);
    })();

    (function (document) {
        document.getElementById();
    })(document);

    (function (window) {
        (function (window) {
            (function (window) {
                
            })(window);
        })(window);
    })(window);
    
    (function (w) {
        
    })(window);
</script>
<script src="js/MyTool2.js"></script>
<script>
    // console.log(window);
    myTool.get();
    myTool.send();
</script>

闭包的作用分析

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

问题:

  1. 函数执行完之后,函数内部声明的局部变量是否还存在? 一般是不存在,存在于闭包中的变量才可能存在
  2. 在函数外部能直接访问函数内部的局部变量吗? 不能,但我们可以通过闭包让外部操作它

产生:在嵌套内部函数定义执行完时就产生了(不是在调用)

死亡:在嵌套的内部函数成为垃圾对象时

function fn1() {
        var num = 10;
        function fn2() {
            num++;
            console.log(num);
        }

        return fn2;
    };

    var f = fn1();
    f(); 

    //使用完毕后,让全局变量为null 删除里面的垃圾对象
    f = null;
闭包的高级排他方法:
var allLis = document.getElementsByTagName('li');
    var lastOne = 0;

    for (var i = 0; i < allLis.length; i++) {
        (function (index) {
            allLis[index].onmouseover = function () {
            //清除
            allLis[lastOne].className = '';
            //设置
            this.className = 'current';
            //赋值 每次进来清除上一个被选中的class
            lastOne = index;
        }      
        })(i);  
    }

闭包的缺点

  1. 缺点:函数执行完之后,函数内部的局部变量没有释放,占用内存时间会变长,容易造成内存泄漏
  2. 解决:及时释放
function fn1() {
        var arr = new Array[99999999999999999];
        function fn2() {
            console.log(arr);
            
        }
        return fn2;
    }

    var f = fn1();
    f();

    //释放
    f = null;

1. 内存溢出:

一种程序运行出现的错误

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

2. 内存泄漏:

占用的内存没有及时释放

内存泄漏累积多了就容易导致内存溢出

常见的内存泄漏:

  1. 占用内场很大的全局变量
  2. 没有及时清理的计时器/定时器
  3. 闭包