JS 闭包

255 阅读2分钟

1.什么是闭包(closure)?

闭包是一种特殊的函数。

2.如何生成一个闭包?

当一个内部函数引用了外部函数的数据(变量/函数)时, 那么内部的函数就是闭包 所以只要满足"是函数嵌套"、"内部函数引用外部函数数据"

3.闭包特点:

只要闭包还在使用外部函数的数据, 那么外部的数据就一直不会被释放 也就是说可以延长外部函数数据的生命周期

4.闭包注意点:

当后续不需要使用闭包时候, 一定要手动将闭包设置为null, 否则会出现内存泄漏


5.场景问题一:

tab切换使用DOM循环绑定事件,循环内使用let而使用var无法实现

5.1 原因以及解决方法

默认情况下是顺序结构, 代码会从上至下的执行, 前面的没执行完后面的不能执行 默认情况下通过var定义的变量, 只要不是定义在函数中都是全局变量

    for(var i = 0; i < 3; i++){ // 0 1 2 3
        function test() {
            console.log(i); // 3
        }
    }
    // console.log(i); // 3
    test();

因为从上至下执行,所以会先把for循环内执行完毕,for循环执行三次,function三个,但由于同名最后一次会覆盖之前的,所以test()只会执行最后一次


使用立即执行实现索引同步

    for(var i = 0; i < 3; i++){ // 0 1 2 3
        function test() {
            console.log(i); // 0 1 2
        }
        test();
    }

test()写在循环内容,每循环一个function便立即执行了test()


    for(var i = 0; i < 3; i++){ // 0 1 2 3
        (function test() {
            console.log(i); // 0 1 2
        })();
    }

立即执行函数与上面差不多


    for(var i = 0; i < 3; i++){ // 0 1 2 3
        // function test(index) { // var index = i;
        //     console.log(index); // 0 1 2
        // }
        // test(i);
        (function test(index) {
            console.log(index); // 0 1 2
        })(i);
    }

image.png

let oBtns = document.querySelectorAll("button");
    for(var i = 0; i < oBtns.length; i++) {
        let btn = oBtns[i];
        (function test(index) { // var index = i;
            // console.log(index); // 0 1 2
            // 注意点: onclick对应的方法由于满足了闭包的条件, 所以onclick对应的方法也是一个闭包
            btn.onclick = function () {
                console.log(index);
            }
        })(i);
    }

只要闭包还在使用外部函数的数据, 那么外部的数据就一直不会被释放 所以只要不执行内部的闭包,外面的被使用的数据不销毁


在ES6中如果在循环中通过let定义的变量, 那么这个变量是一个局部变量

全局变量

    var list = [];
    // 这里的i是全局变量
    for(var i = 0; i < 3; i++){ // 0 1 2 3
        var fn = function test() {
            console.log(i); // 3
        }
        list.push(fn);
    }
    // console.log(i);
    // console.log(list);
    list[0]();
    list[1]();
    list[2]();

image.png

根据顺序执行,for循环结束之后,去执行list[0]、list[1]、list[2],(i是全局变量,循环结束i为2)

局部变量

    let list = [];
    // 这里的i是局部变量
    // 注意点: 由于i是局部变量, 所以每次执行完循环体都会重新定义一个i变量
    for(let i = 0; i < 3; i++){ // 0 1 2 3
        let fn = function test() {
            console.log(i); // 0 1 2
        }
        list.push(fn);
    }
    // console.log(i); // i is not defined
    // console.log(list);
    list[0]();
    list[1]();
    list[2]();

image.png

let局部变量,每次执行循环体都是新定义一个变量,每个作用域就有自己的变量

    for(let i = 0; i < 3; i++){ // 0 1 2 3
        // 注意点: 在ES6中由于{}是块级作用域, 所以只要在块级作用域中定义了一个函数
        //         并且这个函数中用到了块级作用域中的数据, 那么这个函数就是闭包
        function test() {
            console.log(i);
        }
    }
    test(); // 2,同名覆盖,只执行最后一个
    // 在ES6中
    // 1.for循环中通过let定义的变量是一个局部变量
    // 2.for循环中通过let定义的变量每次执行循环体都会重新定义一个新的
    //   也就是每个循环体都有一个属于自己的变量
    // 3.for循环中如果定义了函数, 这个函数用到了通过let定义的变量, 那么这个函数是一个闭包
    for(let i = 0; i < 3; i++){
        function test() {
            console.log(i);
        }
    }

学习笔记❥(^_-)