本文为历史博客迁移
首先应该先弄清楚闭包
是什么,感觉这个词从字面意思上进行切入会越看越浑。
我只能强行理解为某某某明明已经闭
合了,却又包
含着什么东西。诶?感觉好像有内味儿了。闭包不正是函数在其返回以后(理论上来说已经闭合)还能访问到函数内部的变量(包含对函数内部作用域的引用)么。
词法作用域
那么什么是词法作用域呢,就是根据声明变量的位置来确定变量可以发挥作用的域。
比如一个变量声明在全局,那它在全局作用域就是有作用的,可以被访问也可以被修改的,而操作它的时候,就要找到它的主人,也就是全局对象。
又比如一个变量声明在一个函数内,那它可以作用的域(范围)就是这个函数内。
特殊一点的是函数内嵌套函数,由于内部的函数是在外部函数的作用域内的,所以内部函数可以访问到外部函数中的变量,例如:
function outer() {
var a = 2;
function inner() {
console.log(a);
}
inner();
}
outer();
// 2
其实这里面是存在一个过程的,正如js
拥有原型链一样,作用域也存在一条链。
当inner
被调用的时候要访问一个叫a
的变量,在inner
作用域没找到,就会循着其作用域链向上找,它的上一层是outer
作用域,然后在outer
作用域找到了a
,于是打印出来。
闭包
那么闭包的存在导致了一个什么事呢,它好像打破了作用域的规律。例如:
function outer() {
var a = 2;
function inner() {
console.log(a);
}
return inner;
}
var inner = outer();
inner();
// 2
outer
先被声明,其内部声明了一个变量a
和一个函数inner
。
这两个东西理论上来说只属于 outer 函数内部(也就是仅供内部员工使用),像是私有的。
但是函数有权利定义自己的返回值,而函数这个东西在js
中又被认为是“一等公民”,也就是说它和变量什么的是平起平坐的。
函数可以返回一个变量,当然也可以返回一个函数,同时函数可以接受变量作为参数,也可以接受函数作为参数。
所以这里outer
就把 inner 函数返回了,重点是 inner 做了什么事呢,它里面在访问不属于自己却属于 outer 作用域的变量 a,这么一来这个 return inner 仿佛就在 outer 内部和其外部之间打了一个秘密通道。
导致结果有两个:
- outer 作用域内的变量被泄露到作用域外部;
- outer 作为一个函数,在 return 后理应释放占有的内存,也就是其内部声明的变量回被回收。
但这样一来,由于outer
返回的是一个函数inner
,而inner
还在引用这个a
,导致 a 无法被回收。更重要的是,最终在全局作用域调用inner
时真的访问到了a
,但a
明明不存在于全局作用域。
按照作用域链的规则,当访问一个变量时,如果在当前作用域访问不到,会向上找。但此时已经是全局作用域,如果找不到就应该返回 undefined 了。但这里却得到 2,可见父作用域(全局作用域)访问了子作用域(outer)的变量,即是由于闭包
的存在打破了阴阳两界(嵌套作用域之间的关系)。
代码仓库见: https://github.com/barnett617/codehub/blob/main/front-end/src/3-closure/index.md