yellow

理清定义

执行环境(execution context):定义了变量或函数有权访问的其他数据。每个执行环境对应一个「变量对象」。

变量对象(variable object):包含当前能够访问的所有变量、函数总和。即是 this 所代指的内容。

作用域链:本质是一个指向变量对象的指针表,当代码被执行时就会被创建,包含有序的变量对象。(自己可以理解为一个数组[当前变量对象,上一级变量对象……全局变量对象])

活动对象(activation object):当代码进入一个环境,而此环境是一个函数时,则变量对象 = 活动变量,初始包含 arguments 对象,传入的参数变量。

闭包:有权访问另一个函数作用域中的变量的函数。

实例分析

闭包的本质是什么?
当某个函数被调用,创建一个执行环境(execution context)及作用域链,重点是,此时闭包的作用域链包含了外部函数的活动变量,由于存在引用,内存不会回收外部函数的活动变量,直到闭包被销毁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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]] 属性中,读取类似这样的一份指针列表

1
[outer 变量对象的引用,window 变量对象的引用]

  1. 创建一个执行环境
  2. 复制 [[Scope]] 中指向对象,构建作用域链
  3. 创建 inner 的活动对象(=变量对象)推至作用域链的顶端
  4. 访问某个变量,从作用域链中搜索
  5. 执行结束,移除执行环境,销毁局部活动对象。

反观 outer 函数执行过程,在最后一步,由于 outer 活动对象在 inner 函数的 [[Scope]] 被引用,不能被销毁,如此闭包产生。

推荐阅读:深入理解JS中声明提升、作用域(链)和this关键字