闭包相关
执行上下文
当JS代码执行时,会产生三种执行上下文
- 全局执行上下文
- 函数执行上下文
- eval执行上下文
每个执行上下文中都有三个重要属性
- 变量对象 VO,包含变量、函数声明和函数参数,该属性只能在全局上下文中访问
- 作用域链(JS采用词法作用域,也就是说变量的作用域是在定义时就决定的)
- this
js
var a = 10
function foo(i) {
var b = 20
}
foo()对于该代码,执行栈中有两个上下文: 全局上下文和函数foo上下文:
js
stack = [
blobalContext,
fooContext
]对于全局上下文来说,VO大概是这样:
js
blobalContext.VO = {
a: undefined,
foo: <Function>
}对于函数foo来说,VO不能访问,只能访问到活动对象AO
js
fooContext.VO === foo.AO
fooContext.AO = {
i: undefined,
b: undefined,
arguments: <>
}arguments是函数独有的对象,箭头函数没。该对象是一个伪数组,有length属性且可以通过下标访问。该对象的callee属性代表函数本身,caller属性代表函数的调用者
对于作用域链可以把它理解成包含自身变量对象和上级变量对象的列表,通过[[Scope]]属性查找上级变量
js
fooContext.[[Scope]] = [
globalContext.VO
]
fooContext.Scope = fooContext.[[Scope]] + fooContext.VO
fooContext.Scope = [
fooContext.VO,
globalContext.VO
]闭包
绑定变量及变量执行环境的表达式
闭包的定义很简单:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
js
function A() {
let a = 1
function B() {
console.log(a)
}
return B
}你是否会疑惑,为什么函数 A 已经弹出调用栈了,为什么函数 B 还能引用到函数 A 中的变量。因为函数 A 中的变量这时候是存储在堆上的。现在的 JS 引擎可以通过逃逸分析辨别出哪些变量需要存储在堆上,哪些需要存储在栈上。
