0. 引入
1. AO & GO & Function
学习AO,GO是为了解决‘学习作用域、作用域链’所产生的一切问题
AO与function密切相关 -> function 相当于独立的仓库 =? 独立的作用域
2. Function作为对象
- 函数也是一种对象类型/引用类型/引用值 ```javascript function test(a, b) {}
test.name -> ‘test’ test.length -> 2
- **对象有些属性是我们无法访问的,是JS引擎内部固有的 隐式属性 / 私有属性,如 **`**[[scope]]**`** 属性**<br /><a name="GUnLL"></a>## 1. 概念1. 函数创建时,生成的一个JS引擎内部的隐式属性`[[scope]]`1. 函数**存储作用域链**的容器1. 作用域链以**链**的形式存储 AO/GO,即函数执行器上下文和全局执行器上下文1. 函数执行完成后,AO是要销毁的,再次执行函数时会重新生成AO,即 AO是一个即时的存储容器<a name="rYhVP"></a>## 2. 过程精讲```javascriptfunction a() {function b() {var b = 2;}var a = 1;b();}var c = 3;a();
Line: 0 — 全局执行的前一刻,生成GO,此时函数声明已经定义,而函数表达式没有定义
Line:1 — 当a函数被定义时,系统生成函数的[[scope]]属性,[[scope]]保存该函数的作用域链。
该作用域链的第0位存储当前环境下的全局执行期上下文GO,GO里存储全局下的所有对象,其中包含函数a和全局变量c。
*每个函数在定义的时候,作用域链中都保存有GO
Line: 10 — 当函数a被执行时(前一刻),作用域链的顶端(第0位)存储a函数生成的执行期上下文AO,同时第1位存储GO。
*JS引擎查找变量时,到a函数存储的作用域链中,从顶端开始一次向下查找
*函数自身的AO总是存在自己作用域链的最顶端
Line:2 — 当b函数被定义时,是在a函数的环境下,所以b函数这时的作用域链就是a函数被执行期的作用域链
Line: 6 — 当b函数被执行时(前一刻),生成函数b的[[scope]],存储函数b的作用域链,顶端第0位存储b函数的AO,a函数的AO和全局的GO依次向下排列。
Line: 6 — 当b函数执行结束后,b函数的AO被销毁,回归被定义时的状态
Line: 7 — 当a函数执行结束后,a函数的AO被销毁的同时,b函数及其[[scope]]也将不存在。a函数回归到被定义时的状态。
3. 案例
function a() {function b() {function c() {};c();}b();}a();
- a 定义
- a.[[scope]]
- 0: GO
- a.[[scope]]
- a 执行
- a.[[scope]]
- 0: AO_a
- 1: GO
- a.[[scope]]
- b 定义
- b.[[scope]]
- 0: AO_a
- 1: GO
- b.[[scope]]
- b 执行
- b.[[scope]]
- 0: AO_b
- 1: AO_a
- 2: GO
- b.[[scope]]
- c 定义
- c.[[scope]]
- 0: AO_b
- 1: AO_a
- 2: GO
- c.[[scope]]
- c 执行
- c.[[scope]]
- 0: AO_c
- 1: AO_b
- 2: AO_a
- 3: GO
- c.[[scope]]
- c 结束
- c.[[scope]]
- 0: AO_b
- 1: AO_a
- 2: GO
- c.[[scope]]
- b 结束
- b.[[scope]]
- 0: AO_a
- 1: GO
c.[[scope]]
- b.[[scope]]
- a 结束
- a.[[scope]]
- 0: GO
b.[[scope]]
- a.[[scope]]
4. 闭包引入
function test1() {function test2() {var b = 2;console.log(a);}var a = 1;return test2;}var c = 3;var test3 = test1();test3();
Line: 8 — 当test1函数被执行结束时,由于test2被返回到外部且被全局变量test3接收,因此test1的AO没有被销毁,只是AO_test1被从test1的作用域链中去除,但仍被test2的作用域链保存。
Line: 11 — 当test3执行时,test2的作用域链增加自己的AO,当打印a的时候,在自己的AO上没有查到,则向test1的AO查找。
再次执行test3时,实际操作的仍然是原来test1的AO。
Line: 11 — 当test3函数被执行结束时,test2的AO被销毁,但原来test1的AO仍然存在且被test2连着

总结:当内部函数被返回到外部并保存时,一定会产生闭包,闭包会导致原来的作用域链不释放,过度的闭包可能会导致内存泄漏或加载过慢
5. 闭包案例
案例1:
function test() {var n = 100;function add() {n++;console.log(n);}function reduce() {n--;console.log(n);}return [add, reduce]}var arr = test();arr[0](); // 101arr[1](); // 100
案例2:面包管理
function breadMgr(num){var breadNum = num || 10;function supply() {breadNum += 10;consol.log(breadNum);}function sale() {breadNum--;consol.log(breadNum);}return [supply, sale]}var breadMgr = breadMgr(20);breadMgr[0]();breadMgr[1]();breadMgr[1]();
案例3:日程管理,函数作为对象方法返回
function sunSched() {var sunSched = '';var operation = {setSched: function(thing){sunSched = thing;},showSched: function() {console.log("My schedule on Sunday is " + sunSched);}}return operation;}var sunSched = sunSched();sunSched.setSched("studying");sunSched.showSched();
闭包相当于做了数据缓存
