闭包

161 阅读2分钟

个人理解的闭包,特此记录下来。

1、封闭内部变量,仅可被该返回函数访问

var counter = function() {
    var count = 0;
    return function() {
        console.log(count++);
    }
}

在上述counter函数中,内部的count无法被外部代码访问,除非用counter();

eg:

counter();     // 1
counter();     // 2
counter();     // 3

2、变量长期存于内存中

还是上面这个例子,因为counter()返回的是函数,在js里面函数即对象,相当于counter()返回了一个对象,这个对象在内存中开辟了内存地址,导致它内部的count一直能被访问,所以这个count变量一直没法被释放,故一直存于内存中。如果有时候想要长期管理一个变量,可以使用闭包的方式。但是切记项目中不能有过多的闭包,否则太多变量无法得到释放会造成内存溢出。

闭包核心点:

var x = "outer";
function father() {
    var x = "father";
    return child;
}
function child() {
    return x;
}

father()();    // father

其实就是自由变量的读取,是从函数定义的地方,而不是函数运行的地方。

知道了这一点,之前的一些闭包经典例子应该都能理解。

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, i*1000);
}
// 5
// 5
// 5
// 5
// 5

如上面例子中的i,读取它是从 函数定义的地方,如上所知,这个i所在的函数作用域是在for循环中,也就是全局作用域。

因为执行setTimeout回调的时候,i已经走完循环了,所以会输出5个5。

现在我们来改良一下上诉代码

for(var i = 0; i < 5; i++) {
    (function(j) {
        setTimeout(function() {
            console.log(j);
        }, j*1000)
    })(i)
}
// 0
// 1
// 2
// 3
// 4

用自执行匿名函数,把i作为实参,j为形参传递给匿名函数。这时候匿名函数中的setTimeout里面的j,读取的是变量在匿名函数定义的地方也就是作为形参的j。此时的j是通过闭包保存在匿名函数内部,所以每1秒执行console.log的时候,能够打印出准备的j值

当然在for循环中用let定义i,也能正确打印i值。

这是因为let能够创造块做作用域。而var不行。我估计let内部应该也是用闭包去实现的。

表达能力有限,暂时只能这么描述了。供以后复盘用吧。