闭包理解

闭包可以简单理解为本作用域对其他作用域变量的引用,看如下代码:

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

上述代码想每秒打印1~5这5个数字,但是最终的结果会打印5次“6”这个数字。
首先(function () {})();这个IIFE(立即执行函数)在for循环每次迭代的时候都会创建一个作用域,但是这个作用域是空的,在IIFE的作用域中打印的i变量仍然是for中声明定义的i,这就是闭包。据实践证明,setTimeout这个工具函数,是在for循环执行完毕之后才执行的,所以for循环的结束标志是i=6,此时i的取值为6。看如下修改:

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

此时打印的就是1、2、3、4、5。
因为在IIFE内部的作用域声明了一个j变量,这个变量属于当前作用域,在for每次迭代的时候,函数会将i的值赋值给j,由j来保存,这样每次输出的值就是作用域中保存的j的值。  可以对代码改进:

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

当然,这种传统的写法或许已经过时了,块作用域的出现似乎可以代替IIFE这种写法,写法如下:

for (var i = 1; i <= 5; i++) {
    let j = i;//闭包的块作用域
    setTimeout(function timer() {
        console.log(j);
    },j*1000);
}

let声明的j变量就在当前创建了块作用域(也就是{}包含的内容),也即for循环每次迭代会创建块作用域,这个块作用域中有闭包,timer打印的也就是每个块作用域中的j变量的值。
当然,还有更高级的写法:

for (let i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i);
    },i*1000);
}

for循环头部的let声明指出,for循环在let声明过程中不止会声明一次,每一次迭代都会声明。随后每一次迭代都会使用上一次迭代结束时的值来初始化这个变量。

猜你喜欢

转载自blog.csdn.net/weixin_32087115/article/details/81168894