02.【JS读书笔记】循环和闭包

作用域闭包

function foo() {
    var a = 2;
    
    function bar () {       //bar()的词法作用域能够访问foo()的内部作用域
        console.log(a);
    }
    
    return bar;  //然后将bar()函数本身当做一个值进行传递
}

var baz = foo();        //foo()执行后,其返回值(即内部的bar()函数)赋值给变量baz
baz();          //2 —— 闭包的效果 

通常来说,foo()函数执行后,其整个内部作用域都会被销毁(垃圾回收机制),而闭包的“神奇”之处正是可以阻止这件事发生。

bar()依然持有对foo()内部作用域的引用,这个引用就叫做闭包。

闭包使得函数可以继续访问定义时的词法作用域。

循环和闭包

预期希望分别输出数字1-5,每秒一次,每次一个;

但实际上,代码运行时会已每秒一次的频率输出5次6(6产生的原因:循环的终止条件是i不再<= 5,条件首次成立时的值是6.故输出显示的是循环结束时i的最终值。)。

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

根据作用域的工作原理,实际情况为:尽管循环中的五个函数是在各个迭代中分别定义的,但他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
故我们需要更多的闭包作用域,特别是在循环过程中每个迭代都需要一个闭包作用域。

方法一:用IIFE产生闭包解决

IIFE(立即执行函数)会通过声明并立即执行一个函数来创建作用域。但若作用域为空,仅将其封闭是不够的,故IIFE需要有自己的变量,用来在每个迭代中储存i的值:

for (var i = 1; i <= 5; i++) {
    (function(){
        var j = i;      //IIFE需要有自己的变量,用来在每个迭代中储存i的值
        setTimeout (function timer (
            console.log(j);
        ),j * 1000);
    })();
}

将这段代码进行改进之后:

for (var i = 1; i <= 5; i++) {
    (function(j){       //IIFE需要有自己的变量,用来在每个迭代中储存i的值
        setTimeout (function timer (   //在迭代内使用IIFE会为每个迭代都生成一个新的作用域
            console.log(j);     //使得setTimeout函数的回调可以将新的作用域封闭在每个迭代的内部
        ),j * 1000);
    })(i);
}

方法二:let

for (var i = 1; i <= 5; i++) {
    let j = i;      // 用let声明可以劫持块作用域,并在块作用域中声明一个变量
    setTimeout(function timer(){
        console.log(j);
    },j * 1000);
}

或者直接在for循环头部用let声明:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容