闭包

164 阅读2分钟

看了《你不知道的js》,觉得在作者的带领下重新认识了什么闭包。所以带着我的理解整理了这篇文章。

一、什么是闭包?

在《你不知道的js》上卷中,给出的定义是:1、闭包是基于此法作用域书写代码时所产生的自然结果。 2、当函数可以记住并访问所在的此法作用域时,救产生了闭包。

那么什么是词法作用域呢?

《你不知道的js》书中给出的解释是:词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块级作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)。

二、闭包的应用

1、保持内部数据变量是隐藏且私有的状态

2、当循环中使用setTimeout函数时,我在面试的时候也会遇到过这个问题,所以单独拧出来,刚好它与闭包也有关系。

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

这个的输出结果是以每隔差不多1秒的频率输出4。那么这么改写这个函数,让它以每隔差不多1秒的频率依次输出0,1,2,3呢?改写一下

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

虽然此时使用了闭包,每个延迟函数都会将立即执行函数表达式(IIFE)在每次迭代中创建的作用域封闭起来,但是如果作用域是空的,那么仅仅封闭它们是不够的。如果要达到效果,则需要将变量存到相应的作用域中,也就是:

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

此时终于如愿以偿了。但是还有更方便的方法哦:

function getItem(){
    for(let i=0;i<=3;i++){
        setTimeout(function(){
            console.log(i);
        },i*1000);
    }
}

此时,将var换成了let,也能达到一样的效果。因为for循环头部的let声明还会有一个特殊的行为,即变量在循环的过程中不止被声明一次,每次迭代都会声明,随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。

闭包存在的问题