闭包复习

90 阅读2分钟

闭包是由函数以及声明该函数的词法环境组合而成的

  • 一种常见的闭包问题
var t = [];
function getColor(color) {
    console.log(color);
}
function test() {
    var colors = ["red", "white", "black"];
    for (var i = 0; i < colors.length; i++) {
        var nowColor = colors[i];
        // 这里给t[i]赋值采用的是闭包的形式,nowColor使用了var的方式进行声明,故存在变量提升,
        // 所以具有函数作用域。再t[i]中的事件进行调用的时候,循环早已结束,
        // 故对应的color都指向了colors的最后一项
        // 简单的解决方法就是将nowColor使用let方式声明
        t[i] = function () {
            getColor(nowColor);
        };
    }
}
test();

t.forEach((x) => {
    x(); // 控制台输出了三次black,而不是依次输出red,white, black
});
  • 在函数体内声明的变量就是一个天然的闭包。
   var p = 'x'
   var func = function() {
       var x = 'tj'; //闭包中声明的变量(局部变量)在退出函数后会被销毁
       console.log(x); // 'tj'
       console.log(p); // 'x'
   }
   console.log(x) // x is not defined
   func() // 'tj'
  • 函数体外不能访问到函数体内声明的变量,不过函数体内可以拿到函数体外声明的变量。 函数a中声明的变量如果被函数体内的函数引用了,那么在函数a中声明的变量就不一定会随着函数的退出而被销毁。
var a = function() {
    var x = 1;
    return function() {
       x++;
       console.log(x);
    }
}
var f = a();
//可见x的值一直被保存在了内存里,因为x一直被a函数体内的匿名函数所应用
f(); //2
f(); //3
  • 闭包还可以用来延续局部变量的寿命
// 局部变量n用闭包的方式封闭起来
let numbers = (function() {
    let cache = [];
    return function(num) {
        let n = {value: num}
        cache.push(n);
        console.log(cache);
    }
})()
numbers(1) // [{value: 1}]
numbers(2) // [{value: 1},{value: 2}]

在闭包的使用时,常常要和内存管理一起联系起来。是的,在使用闭包的时候,我们会主动把一些变量封装在闭包中,这些变量是在之后的使用中可能再次用到的,它们存放在闭包或者全局变量中,对内存的影响是差不多的。不过在我们确认这些变量不需要时,可以手动将这些变量设置为null。当然,闭包的使用中,一定要避免循环引用,这样能有效的避免内存泄露。