什么是闭包?

140 阅读3分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

作用域

要了解闭包首先要晓得作用域;
javascript中,作用域主要分为全局作用域与局部作用域(函数作用域)
浏览器中全局环境就是window对象

这里主要关注 函数作用域 :
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。 而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。

作用域链

在js中,全局环境中是无法读取局部环境中定义的变量或函数的,但是在局部环境却可以使用外部环境的数据。 为什么会这样呢?因为有作用域链。

红宝书中有说:作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。dddd
这句话里的关键词:有权访问有序访问

当某函数被调用时,首先会创建一个执行环境及相应的作用域链,然后使用arguments和其他命名参数的值来初始化执行环境的变量对象。 这个变量对象加到作用域链的前端。(全局环境的变量对象始终是作用域链的最后一个变量对象)

当js在读取一个变量时,它会沿作用域链逐级搜索这个名称,从作用域链的最前端开始,逐级往后查找,直到找到(如果没找到,那就报错啦,不干活就想要钱?)。

//全局环境 window
var hello = 'hello window';
function ddtt(){
    // 局部作用域
    console.log(arguments)
    var ac = 'hello function'
    console.log(hello);//hello window
}
console.log(ac)//Uncaught ReferenceError: ac is not defined

什么是闭包?如何产生闭包?

大致了解了作用域和作用域链后,再来看闭包。
红宝书中说: 闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
在js中,只有在函数内部的子函数才能访问到这个函数的内部变量, 所以说在一个函数内定义的函数都可称为闭包(但这不是必须的)。

如下面例子有两个特点

  • 函数套函数
  • 返回的是一个函数
function Parent() {
    // 巴拉巴拉
    let eat = '吃了么?'
    return function son() {
        // 巴拉巴拉
        console.log(eat)
    }
}

当Parent执行后,返回会了son函数(son使用了它外部函数的变量)。
通常情况下,函数执行完成后,它内部的所有活动对象(定义的变量就在这里)、作用域链都会被销毁; 但是因为son的执行依赖于Parent中的变量,所以Parent占用的资源不会被回收;直到son也执行完成后才会释放内存。

  • 但是如果说,Parent没有return son(),只是在Parent内部引用了son, 这时son也不会被第三方引用,是可以及时释放内存的。

  • return 这个函数,只是为了它可以被第三方使用,但不是必须的