JS闭包

1 篇文章 0 订阅

这又是一个老生常谈的js基础问题了,一千个人眼里有一千个哈姆雷特,同样,一千个人眼里也可能有一千个对闭包的理解,每个人对它的理解都会带一些自己的特色,而这个特色有时候又不太好表达,这就造成了网上对它的介绍实在太多太多,今天我就说一下我对闭包的认识。

闭包是基于词法作用域书写代码时所产生的自然结果,其实可能在你的代码中早已经到处是闭包了,现在缺少的是根据自己的意愿来识别,拥抱和影响闭包的思维环境。

阮一峰老师在博客里说 闭包就是能够读取其他函数内部变量的函数,我觉得说的特别通俗易懂,但也可以稍微扩展一下,这是我在书中看到的,我觉得可能更恰当一些。(阮一峰老师的很多博客都强烈建议新手去看一看,像es6,没有那些晦涩的术语,知识点介绍的也比较清晰,适合大家学习)

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这样就产生了闭包

废话不多说,上 板 栗 ~

先从简单的看

function foo () {
    var a = 2;
    function bar () {
        console.log(a)
    }
    return bar;
}
var baz = foo();
baz(); //2 -朋友,这就是闭包的效果

在foo执行后,通常foo()整个内部作用域都被销毁,因为引擎有垃圾回收机制,不再使用的内存空间会被释放,但拜bar()声明的位置所赐,它依然持有对foo内部作用域的引用,拥有涵盖foo内部作用域的闭包,使得该作用域能够一直存活不被释放,这就叫闭包。

接着上板栗

function foo () {
    var a = 2;
    function baz () {
        console.log(a)
    }
    bar(baz);
}
function bar (fn) {
    fn() //这也是闭包
}

无论何种手段将内部函数传递到所在词法作用域之外,他都会持有对原始定义的作用域的引用,无论在何处执行这个函数都会产生闭包。

function wait (message) {
    setTimeout(function timer (){
        console.log(message)
    },1000)
}
wait('hello') //这还是闭包

timer具有涵盖wait内部作用域的闭包。
而且,只要使用了回调函数,基本上都有闭包的存在。

循环与闭包

这有一个for循环最常见的例子

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

相信大家都能看出来,这段代码最终结果是输出5个6,而不是表面上看到的1到5。
这是为什么呢?

首先,因为js是单线程的,setTimeout会把函数放入执行队列中等待被执行,但队列中在他们前面有for循环,所以函数会等for循环结束后才开始执行,而有因为这些函数用的都是一个作用域内的i,for循环结束时i=6,所以结果为5个6

那如何才能解决这个问题呢

第一种,用立即执行函数(IIFE)

它的原理就是每次循环通过IIFE来创建闭包,这样每次循环时都会记录下当时的内部作用域,每次循环内部作用域都有不同,i都在变,把i传入这个IIFE中,这样内部的函数所引用的i就各不相同了,问题就解决了

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

第二种,块级作用域

es6引入了let声明(具体细节之后再讲,或大家可以先去了解一下),它有个特点是可以统治块级作用域。

回想下我们刚才的方法,我们用IIFE在每次循环生成新的作用域,也可以理解为每次生成一个块作用域,既然这样,我们就可以利用let劫持块作用域,本质上也就是将块转为一个可以被关闭的作用域,每次在里面声明变量,相当于每次循环都生成不同的块作用域,每个块里的东西互不干扰

for(let i = 1; i<=5; i++){
    setTimeout(function timer(){
        console.log(i)
    },i*1000)
} //就是这么简单的解决了

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

今天就先到这里吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值