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. 过程精讲
```javascript
function 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](); // 101
arr[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();
闭包相当于做了数据缓存