前端面试-闭包-这个我会

154 阅读4分钟

前言

闭包在前端面试中,也是一道常见的题,现在许多大厂HR在面试时也会偶尔提到,虽然不难,但也可以证明自己基础知识储备够不够扎实。这里我就总结了关于闭包的一些知识点,让面试遇到轻松拿捏

调用栈

调用栈,是用来管理函数调用关系的一种数据结构。首先我们要了解栈这种数据结构,简单说就是存取方式为先进后出。当一个函数被调用时,这个函数的执行上下文就会进入调用栈,然后进行编译再执行,当一个函数执行完毕后,该函数的执行上下文会被销毁(出栈)

var a = 2
function add(){
    var b = 10
    return a + b
}
add()

首先,全局代码执行,开始全局预编译,那么全局执行上下文进入调用栈,在栈底;全局预编译后执行代码,遇到调用函数,开始函数预编译,那么函数的执行上下文进入调用栈;函数执行完毕后,该函数执行上下文被销毁

执行上下文有两个概念,一个是变量环境,另一个是词法环境,变量环境就是存 var 声明的变量和函数声明,词法环境就是存 let、const 声明的变量。

上面代码入栈演示:

image.png

出栈,即函数add执行完成后,调用栈中的add执行上下文被销毁,然后全局代码也都执行完,全局执行上下文也被销毁

闭包

闭包的形成: 简单理解,当函数A将它内部函数B返回出来调用时,就会形成闭包,当然这是不严谨的,我们可以下一个定义

定义: 当通过调用外部函数返回的内部函数后,即使外部函数已经执行结束了,但是内部函数引用外部函数的变量依然会保存在内存中,我们把这些变量的集合称为闭包

function a(){
    var aaa = 123
    function b(){
        var bbb = 234
        console.log(aaa)
    }
    return b
}
var demo = a()
demo()

在上面代码中,全局上下文先进入调用栈,该上下文中有声明的变量demo和声明函数a。执行全局代码遇到a函数的调用,那么a函数的执行上下文进入调用栈,里面有声明的变量aaa 和声明的函数b,执行代码后aaa赋值为123,然后返回声明的函数b,此时函数a算执行完毕了,其执行上下文应该被销毁。但是在demo被调用时,也就是a返回的函数b被调用,此时函数b的执行上下文进入调用栈,其中有声明的变量bbb,而需要打印的aaa在函数a中声明了,那么在函数a执行完被销毁时,就会形成一个闭包,携带函数b调用需要的变量aaa

image.png

因为函数a的调用才形成函数b的声明,那么在b执行时需要打印aaa,就会先从自己声明的变量里面找,没有,那么就在a函数形成的闭包里面找,所以最后打印的是123.

函数形成的闭包,只会给该函数内返回出来调用的函数使用,也就是需要有父子级关系,而不能被全局的其他函数使用

闭包的缺点

一旦形成闭包,只有在页面关闭后闭包占用的内存才会被回收,所以造成了内存泄漏,占用调用栈内存空间

闭包的作用

1.模块化开发(实现公有变量)
2.作缓存
3.封装私有化
4.防止全局变量污染

闭包的作用主要就一个意思,就是在开发过程中我们应该尽量不使用全局变量,因为会受到其他开发者定义的同名变量影响,这时候需要使用公有变量,那么就可以使用闭包,闭包内的变量不受全局变量影响,却也能被函数内多个返回函数公有使用。