前置知识:作用域、作用域链、执行上下文、词法环境。本文主要记录怎么理解闭包这一概念。
前置知识
词法作用域
根据JS执行机制可以了解到,词法作用域在函数创建时就确定了,编写代码时变量定义的位置就可以确定该变量的作用域。
作用域链
函数第一次被调用时,会创建作用域链,当前变量的作用域会先在本身的执行上下文查找,若没有再去父级作用域寻找,直到找到全局上下文若还没有就是undefined。
执行上下文
执行上下文分为全局执行上下文和函数执行上下文,在全局执行上下文中声明的变量称为全局变量。函数内部声明的变量为该函数执行上下文的私有变量。
闭包基础知识
什么是闭包?
- 首先,我理解的闭包是和作用域链有很大关系,js执行过程中window是最大的作用域,它下边可以定义多个块级作用域,每个函数内部都可以看作一个作用域,当函数内部返回一个函数且子函数没在父级作用域内完成整个生命周期的话,父函就不能完成一整个生命周期,所以不能被销毁。闭包就是通过这点卡住父函数的作用域,函数嵌套函数,且内部函数调用父级作用域的变量就可以称之为闭包。
- 本质就是上级作用域内变量的生命周期,因为被下级作用域内引用,而没有被释放。就导致上级作用域内的变量,等到下级作用域执行完以后才正常得到释放。
闭包作用
函数执行时候会形成一个私有上下文,作用有两点:
-
保护:浏览器加载页面时会把代码放在执行栈中,函数进栈时会产生私有执行上下文,该上下文能保护里面的变量不受外界干扰,起到保护作用。
-
保存:浏览器加载页面时会把代码放在执行栈中,函数进栈时会产生私有执行上下文,若该执行上下文中内容一直被上下文以外的内容占用,那么当前上下文就不会出栈释放,从而保存里面的变量和变量值。
从函数调用角度理解闭包
闭包就是一个函数,一个函数返回另一个函数,在其他上下文调用该函数进而使用了其内部私有变量就形成了闭包。
-
形成私有上下文
-
进栈
-
初始化作用域链、this、arguments、赋值形参、变量提示、执行代码(变量先看是否为私有,否则沿着作用域链向上查找)
-
执行结束:有三种情况
- 该函数所在的执行上下文从执行栈中弹出
- 函数内的某个东西被当前执行上下文以外的东西占用,不会出栈进而不会被销毁,就形成闭包
- 该函数所在的执行上下文被销毁
正常情况下,代码执行完成之后,私有上下文出栈被回收。但是特殊情况下,如果当前私有上下文执行完成后被执行上下文以外的东西占用,则当前私有上下文就不会出栈,就形成了不被销毁的上下文,闭包。
注意事项
不恰当的使用闭包会造成内存泄漏