理清定义
执行环境(execution context):定义了变量或函数有权访问的其他数据。每个执行环境对应一个「变量对象」。
变量对象(variable object):包含当前能够访问的所有变量、函数总和。即是 this
所代指的内容。
作用域链:本质是一个指向变量对象的指针表,当代码被执行时就会被创建,包含有序的变量对象。(自己可以理解为一个数组[当前变量对象,上一级变量对象……全局变量对象])
活动对象(activation object):当代码进入一个环境,而此环境是一个函数时,则变量对象 = 活动变量,初始包含 arguments
对象,传入的参数变量。
闭包:有权访问另一个函数作用域中的变量的函数。
实例分析
闭包的本质是什么?
当某个函数被调用,创建一个执行环境(execution context)及作用域链,重点是,此时闭包的作用域链包含了外部函数的活动变量,由于存在引用,内存不会回收外部函数的活动变量,直到闭包被销毁。
function outer() {
var private_val_1 = { val: 1 },
private_val_2 = { val: 2 }
function inner() {
var inner_val_1 = private_val_1
console.log(private_val_2)
}
return inner
}
var closure = outer()
closure() // { val: 2 }
由于 inner 函数的作用域链中,包含了 outer 函数的活动对象的索引,所以在执行 inner 函数时,会从 inner 函数 [[Scope]]
属性中,读取类似这样的一份指针列表
[outer 变量对象的引用,window 变量对象的引用]
- 创建一个执行环境
- 复制
[[Scope]]
中指向对象,构建作用域链 - 创建 inner 的活动对象(=变量对象)推至作用域链的顶端
- 访问某个变量,从作用域链中搜索
- 执行结束,移除执行环境,销毁局部活动对象。
反观 outer 函数执行过程,在最后一步,由于 outer 活动对象在 inner 函数的 [[Scope]] 被引用,不能被销毁,如此闭包产生。