一、什么是闭包
MDN
一个函数和对其周围状态(词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包
JavaScript高级程序设计第三版
闭包是指有权访问另一个函数作用域中的变量的函数
JavaScript高级程序设计第四版
闭包是指那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的
二、闭包的保持
一般情况下,函数作用域中的AO会在函数执行完被销毁。在闭包中,因为引用了父级函数作用域中的变量,一旦闭包被return出去被全局变量接收,那就意味着被引用的这个变量不会被销毁,从而会导致父级函数已经执行完了,而它的AO却被保存在闭包的作用域链上,这种情况就是闭包的保持。
三、闭包的作用
实例解释闭包在[[Scopes]]中发挥的作用
function test1() {
function test2() {
var b = 2
console.log(a)
a = 2
console.log(a)
}
var a = 1
return test2
}
var c = 3
var test3 = test1()
test3()
这段代码在预编译和[[Scopes]]中是怎样流转的
test1函数声明时,系统生成[[Scopes]]属性,保存test1的作用域链,顶端存储GO
GO: {
c: undefined,
test1: f test1(){},
test3: undefined
}
test1函数执行时(前一刻),函数test2被声明
test1AO: {
a: undefined,
test2: f test2(){}
}
GO: {
c: undefined,
test1: f test1(){},
test3: undefined
}
此时test2的作用域链和test1保持一致
test1执行完,由于test2被返回到外部,并且被全局变量test3接收,因此test1AO没有被销毁,test2中依然可以访问test1AO
test1AO: {
a: 1,
test2: f test2(){}
}
GO: {
c: 3,
test1: f test1(){},
test3: undefined
}
test3执行时(前一刻),实质上是执行了test2,test2在自己的作用域链上增加了test2AO
test2AO: {
b: undefined
}
test1AO: {
a: 1,
test2: f test2(){}
}
GO: {
c: 3,
test1: f test1(){},
test3: f test2(){}
}
test3执行完,test2AO被销毁,但test1AO任然存在,并且test2可以访问到test1AO
test1AO: {
a: 2,
test2: f test2(){}
}
GO: {
c: 3,
test1: f test1(){},
test3: f test2(){}
}