AO
和GO
主要是用来解决「作用域」和「作用域链」相关所产生的一切问题。AO
和function
相关,函数相当于一个独立的仓库,比如A
仓库和B
仓库。
作用域 & 作用域链
我们都知道在JavaScript
中「一切皆对象」,每个对象都有自己的属性和方法。
var obj = {
name: "1",
address: "1",
teach: function () {},
};
console.log(obj.name);
而函数也是一种对象类型(引用类型)。
function test(param1,param2) {}
console.log(test.name); // 函数的名字
console.log(test.length); // 形参的个数
在JavaScript
中对象有一些属性是我们无法访问的,它们是JS
引擎固有的「隐式属性」,是不供外界访问的(私有属性)。
那什么是作用域[[scope]]
呢???
- 函数创建时生成的一个
JS
内部的隐式属性 - 函数存储作用域链的容器,作用域链存储的是
AO
和Go
对象,每个函数的作用域链都是独立的!!!AO
是函数执行期上下文对象GO
是全局执行期上下文对象
AO
是一个即时存储的容器,当函数执行完成之后,AO
是要被销毁的,再次执行函数,AO
会重新生成。
下面来看一个例子:
function a() {
function b() {
var b = 2;
}
var a = 1;
b();
}
var c = 3;
a();
以上以一段完整的代码,我们将代码逐步分解:
以上图解作用域链的时候,我们可以总结出:
函数被定义的时候就已经生成了[[scope]]
,[[scope]]
已经存储了Scope Chain(作用域链)
,作用域链已经存储了GO
。
而函数执行的前一刻才生成的函数自己的AO
为什么函数的外部无法访问函数内部的变量呢?
从图中我们可以得知,作用域链是自上而下执行的,所以b
函数可以访问a
函数的变量,反过来则不行。
function a() {
function b() {
var b = 2;
console.log(a); // 1
}
var a = 1;
b();
console.log(b); // error
}
var c = 3;
a();
下面再来举个例子🌰 :
function a() {
function b() {
function c() {}
c();
}
b();
}
a();
// 以上代码的作用域链是:
/**
* a定义:
* a.[[scope]] > [GO]
* a执行
* a.[[scope]] > [AO,GO]
* b定义
* b.[[scope]] > [AO,GO]
* b执行
* b.[[scope]] > [AO,AO,GO]
* c定义
* c.[[scope]] > [AO,AO,GO]
* c执行
* c.[[scope]] > [AO,AO,AO,GO]
*/
/**
* c结束:
* c.[[scope]] > [AO,AO,GO]
* b结束:
* b.[[scope]] > [AO,GO],c.[[scope]] 销毁
* a结束:
* a.[[scope]] > [GO],b.[[scope]] 销毁
*/
闭包
当内部函数被返回到外部并保存的时候,就一定会产生闭包
- 闭包会产生原来的作用域链不释放
- 过度的闭包可能会导致内存泄漏或加载过慢
举栗 🌰 :
function test1() {
function test2() {
var b = 2;
console.log(a);
}
var a = 1;
return test2;
}
var c = 3;
var test3 = test1();
test3();
以上图解解释了什么是闭包。
举栗子🌰 :
function test() {
var n = 100;
function add() {
n++;
}
function reduce() {
n--;
console.log(n);
}
return [add, reduce];
}
var arr = test();
arr[0](); // 101
arr[1](); // 100
因为add
和reduce
被保存到了外部执行,test
函数的AO
并没有被销毁,所以add
和reduce
操作的其实都是test
函数的n
。
同理看下面的例子:
function breadMgr(num) {
var breadNum = num || 10;
function supply() {
breadNum += 10;
console.log(breadNum);
}
function sale() {
breadNum--;
console.log(breadNum);
}
return [supply, sale];
}
var breadMgr = breadMgr();
breadMgr[0](); // 20
breadMgr[1](); // 19
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(); // My schedule on sunday is studying