引言
在学习JavaScript的函数这一章节时,闭包这个概念对很多同学来说有点不好理解,今天特地来记录一下, 如有错误的地方,还请指正。
闭包的概念
闭包是指一个函数可以记住其外部作用域中的变量并可以访问这些变量
我们先来看一段示例代码,如下:
function makeCounter() {
return(count++)
}
alert(makeCounter()) //Uncaught ReferenceError: count is not defined
这个代码很容易理解,count没有被定义,去使用的话,就会报错了。 我们再看一个示例代码,如下:
function makeCounter() {
let count = 0
return function() {
return count++
}
}
let counter = makeCounter()
alert(counter()) // 0
alert(counter()) // 1
通过上面示例代码,通过对比这两段代码,有同学会有疑问,第二段代码为什么不报错?
一般来说,一个函数执行完之后,就会被移出调用栈,进行内存回收,其作用域中定义的变量也随之回收。在上面示例代码中,makeCounter()执行完以后,一般来说,count变量会被回收掉。return出来的函数也就无法访问它了。
然而,return出来的函数中还存在对count变量的引用,引用还存在的情况下,就不会被回收。那么函数内对外部作用域变量的引用是什么原理呢?介绍另外一个重要的概念:词法环境
词法环境
在JavaScript中,我们知道有三种作用域:块级作用域,函数作用域,全局作用域。在每个作用域中都有一个称为词法环境的内部对象,词法环境对象由两部分组成:
- 环境记录——一个存储所有局部变量作为其属性(包括一些其他信息,例如
this的值)的对象。 - 对 外部作用域中词法环境对象 的引用,与外部代码相关联。
我们知道词法环境这个对象之后,对于上述的问题就有了一个基本的答案了,因为return出来的函数作用域中的词法环境对象存在着对makeCounter函数作用域中词法环境对象的引用,即使是被return出去了,该引用也依然存在,能够随时去访问makeCounter函数作用域中词法环境对象里的环境记录对象,该环境记录对象里面存储了它的局部变量。所以你理解了吗?