scope: 作用域,函数创建的时候就形成
scope chain: 作用域链
/*
* ECStack 执行环境栈
*
* EC(G) 全局执行上下文 => 进栈执行
* VO(G) 全局变量对象
* => 代码执行之前,全局上下文中的变量提升
* A => 函数 0x0000 -------- [[scope]]: EC(G) 形参: y
* => 全局上下文中的代码执行
* let x = 1;
* function A ... => 变量提升阶段已经处理过,此时不再处理
* let C = A(2);
*
* => EC(A) A 函数调用形成的私有执行上下文 => 进栈执行,全局上下文压缩到栈的底部
* => AO(A) 函数 A 的私有活动对象
* [[scopeChain]]: <EC(A), EC(G)>
* 形参赋值: y = 2
* 代码执行之前, A 函数的私有上下文中的变量提升
* B => 函数 0x0001 -------- [[scope]]: EC(A) 形参: z
* => A 函数的私有上下文中的代码执行
* let x = 2;
* function B ... => 变量提升阶段已经处理过,此时不再处理
* return B =>
*
* => 函数 A 执行结束,全局上下文重新弹到栈的顶部,继续执行全局下的代码
* C = 0x0001
* //=> 由于函数 A 内部的一个堆内存地址被外部的全局变量 C 所占用,因此函数 A 被调用时形成的私有上下文不会出栈释放(想让这个执行上下文出栈释放,可以通过移除指针的方式,一般都是直接重新指向 null )
* //=> 知识点拓展: 函数调用执行形成的私有上下文,无论是否被释放,都会起到一个划分独立区域的作用
* //=> 1.保护: 独立的代码执行区域中有自己私有变量所存储的空间,而用到的私有变量和其它区域中的变量不会有任何冲突(防止变量污染)
* //=> 2.保存: 如果上下文不被销毁,那么存储的私有变量的值也不会被销毁,可以被下级上下文调取使用
* //=> 这种函数执行,形成上下文,产生保存和保护私有变量的机制,就是"闭包"(一般认为,形成的私有上下文不被释放,才算闭包)
*
* C(3);
*
* => EC(C) C 函数调用形成的私有执行上下文 => 进栈执行,全局上下文压缩到栈的底部
* => AO(C) 函数 C 的私有活动对象
* [[scopeChain]]: <EC(C), EC(A)>
* 形参赋值: z = 3
* 代码执行之前的变量提升: --
* => C 函数的私有上下文中的代码执行
* console.log(x + y + z); => x,y,z 的值按照作用域链一级一级向上查找 3+2+2=7
*
* => 函数 C 执行结束,内部没有任何东西被外部占用,此时会出栈释放,全局上下文重新弹到栈的顶部
*
* ... ...
* =>关闭页面,全局执行上下文出栈释放
*
*/
let x = 1;
function A(y) {
let x = 2;
function B(z) {
console.log(x + y + z);
}
return B;
}
let C = A(2);
C(3); //=> 7