从作用域链开始深入认识闭包

281 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

前言

我在上一篇文章中详细的聊了我对作用域链的认识,这里我们接着聊一下和作用域链牵扯甚深的闭包。相信大家对于闭包已经很熟悉了,但是可能有的人或者新入坑的小伙伴对于闭包的内部机制不是很清楚,这里我们就深入的聊一下闭包和作用域链的牵扯。

什么是闭包

我们先看一下MDN官方对闭包的解释:

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure

这样其实理解起来比较晦涩,我们在说一下我对闭包的理解,仅供参考:在一个函数内部,返回另一个持有当前作用域的函数

我们这里再上一下代码,看一下有关闭包的代码实例:

    function makeFunc() {
        const name = "Mozilla";

        function displayName() {
            const age = 24;
            console.log(name);
        }
        return displayName;
    }
    
    const myFunc = makeFunc();
    
    myFunc();

作用域分析

执行makeFunc 的时候,返回了一个函数名为 displayName 的函数,在这个函数中,访问了 makeFunc 中的 变量name,在 执行myFunc 的时候,仍然能访问到 name,这就说明 myFunc劫持了makeFunc的作用域

这里需要特别说明一下,myFunc跟makeFunc并不是存在于同一个作用域链上。我们在上一篇关于作用域链的文章中详细讲解了作用域链的形成,这里就不再做过多的赘述了。

myFunc是如何访问到makeFunc的作用的呢?小朋友你是否有很多问号呢?

其实,在myFuncc执行的时候,发现变量name被访问,这时候其实会劫持makeFunc的作用域,然后在myFunc作用域生成的时候,放置于myFunc作用域的上游。当然最顶层作用域依然还是全局作用域,这一点在任何时候不会发生变化。

相信说到这里,大家应该都知道了为什么myFunc执行的时候,依然可以访问到makeFunc中的变量了吧。

这里,通过一个闭包作用域链的图片形象的展示一下:

QQ图片20220410190058.png

闭包副作用

闭包虽然带来的很多的好处,但是它也有副作用。

1. 内存占用

由于闭包劫持了作用域,导致函数执行完成之后,内存无法释放。如果大量的使用闭包的话,会导致内存开销直线飙升,所以,对于闭包的使用要谨慎。

2. 执行速度

闭包的好处就是可以跨作用域访问变量,但是,跨左右访问会影响执行速度。如果频繁的访问跨作用域变量的话,势必会对执行速度产生可感知的影响。这也从侧面的说明闭包的使用需要小心谨慎。

3. 处理方案

虽然闭包有各种副作用,但是我们也不能因噎废食,在恰当的场景还是需要使用闭包的。当时,闭包的副作用我们也不能完全忽略,需要进行解决一下。

变量拷贝赋值,这是我比较常用的一种方案:将需要跨作用域访问的变量储存到局部变量中,闭包直接访问局部变量。