JavaScript高级程序设计一书中指出
闭包指的是那些调用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
准备
执行上下文
执行上下文是用来记录代码运行时环境的抽象概念
代码都是在执行上下文中运行的
在代码运行过程中的变量环境信息都放在执行上下文,当代码运行结束,执行上下文也会销毁
作用域链
🎁 概念:多个作用域对象连续引用形成的链式结构。
🎁当在Javascript中使用一个变量的时候,首先会尝试在当前作用域下去寻找该变量,如果没找
到,再到它的上层作用域寻找,以此类推直到全局作用域,如果仍找不到,它就会直接报错。
🎁作用域链在JS内部中是以数组的形式存储的,数组的第一个索引对应的是函数本身的执行期上下文,也就是
当前执行的代码所在环境的变量对象,下一个索引对应的空间存储的是该对象的外部执行环境,依次类推,直到全局执行环
境
什么是闭包
探讨闭包
闭包是指指那些引用了另一个函数作用域中变量的函数
下面看一个例子(JS高级程设计上的一个例子)🌰
function createComparisonFunction(propertyName) {
return function (object1, object2) {
let value1 = object1[propertyName] // 在这里引用了外部函数的变量 propertyName
let value2 = object2[propertyName]
if (value1 < value2) {
return -1
} else if (value1 > value2) {
return 1
} else {
return 0
}
}
}
🍓上面做标注的地方,引用了外部函数的变量,在这个内部函数被返回后,这个引用变量依旧存在。这是因为内部函数的作用域链包含createComparisonFunction() 函数的作用域。
调用一个函数的时候,会为这个函数创建一个执行上下文,并创建一个作用域链。arguments和其它命名参数初始化这个函数的活动对象。外部函数是内部函数作用域上的第二个对象。这个作用域链一直向外串起了所有包含函数的活动对象,直到全局执行上下文才终止
❤️其实,在调用内部函数的时候,会创建一个包含arguments、value1和value2的活动对象,这个对象是其作用域链的第一个对象,而全局上下文的变量对象是compare()作用链上的第二个对象。
下面的例子说明执行上下文,与作用链之间的关系🌰
function compare( value1, value2){
if(value1 <= value2){
return -1
}else{
return 1
}
}
💥
函数执行时,每个执行上下文中都会包含一个其中变量的对象。全局上下文中的叫做变量对象,它会在代码执行期间一直
存在而函数局部上下文叫做活动对象,只在函数执行期间存在
上面的这个例子,compare()函数执行上下文的作用域链中有两个变量对象:全局变量对象和局部变量对象。
作用域链其实就是一个含指针的列表,每个指针分别指向一个变量对象(不是物理上的对象)
💥
函数内部访问变量时,就会使用给定的名称从作用域链中查找变量。函数执行完毕后,局部对象会被销毁,内存就只剩下全
局作用域
🐢不过,闭包就不一样了🐢
🍑
在一个函数内部定义的函数会把其包含函数活动的活动对象添加到自己作用域链中,
🍑
在此文中的第一个例子中,内部匿名函数的作用域链实际上包含createComparisonFunction()函数的活动对象。
匿名函数自身局部活动对象和comparisonFunction()函数局部活动对象还有全局变量对象
createComparisonFunction()活动对象执行完毕后并不被销毁,因为匿名函数仍有对它的引用。
也就是说,在createCompriseFunction()函数执行之后,其作用域链虽然消失,但是其活动对象仍存在匿名函数的的作用域链中
直到匿名函数被销毁。