前提:通过两天的努力,我总算学懂了闭包。从词法作用域和动态作用域-->执行上下文和执行上下文栈-->活动对象(GO/AO)-->预编译-->闭包。这一流程下来,我对js的理解也更加深入了。
本文思路和参考链接(感谢大佬们的分享)如下:
-
- 函数预编译
- 全局预编译
以下是我的知识整理思路,如有错误,烦请指出
词法作用域和动态作用域
var value = 1;
function foo()
{ console.log(value); }
function bar()
{ var value = 2;
foo(); }
bar();
首先,js是词法作用域(静态作用域),词法作用域就是作用域在函数定义的时候就已经决定了!!函数作用域基于函数创建的位置,与动态作用域有着巨大差别。
上述代码,如果按照词法作用域,当执行bar函数时,输出value,而foo函数没有value变量就回去上一级作用域中查找,在foo函数定义时它的上一级作用域就是全局作用域。所以会输出1。
而动态作用域就会去调用foo函数的bar函数中寻找value,输出2.
执行上下文和执行上下文栈
let value='foo'
function foo() {
console.log(value);
}
foo();
js引擎在执行script代码和script代码里面的函数前,都会生成一个执行上下文(execution context),然后将该执行上下文压入执行上下文栈中(execution context stack)。执行完script里面所有代码和函数后,对应的执行上下文弹出栈,然后销毁。
上述代码,js引擎到了第一行时,生成全局执行上下文,压入栈中。
当到了最后一行时(执行函数前),生成函数执行上下文,压入栈中。
执行上下文由活动对象,作用域链,this组成。
活动对象
let value='foo'
function foo(n) {
let a=2
console.log(n);
}
var b=function(){
console..log('b')
}
foo(1);
活动对象包含变量和函数
上述代码,全局执行上下文的活动对象:变量value和变量b,函数foo;foo函数执行上下文的活动对象:形参(变量)n和变量a
预编译
函数预编译:
1.生成活动对象AO(Activation Object)
2.为AO添加属性:属性名为变量名/形参名,属性值为undefined
3.将形参对应属性的属性值改为实参值
4.为Ao添加属性:属性名为函数名,属性值为函数体。
例子:
1. function test(a) {
2. console.log(a);
3. var a = 123;
4. console.log(a);
5. function a() {}
6. console.log(a);
7. var b = function() {}
8. console.log(b);
9. function d() {}
10. }
11. test(1)
1.当开始执行这段代码前,生成全局执行上下文GO
GO:{
test:function test(){略}
}
2.第11行,生成了函数执行上下文AO
第一步:生成活动对象
AO:{
}
第二步:为AO添加属性:属性名为变量名/形参名,属性值为undefined
AO:{
a:undefined //第2行 形参
a:undefined //第3行 变量
b:function(){} //第7行 变量
}
由于覆盖,最后AO为
AO:{
a:undefined //第3行 变量
b:function(){} //第7行 变量
}
第三步:由于覆盖了形参,所以这步不执行
第四步:为Ao添加属性:属性名为函数名,属性值为函数体。
AO:{
a:undefined //第3行 变量
b:function(){} //第7行 变量
a:fucntion(){} //第5行 //函数
d:fucntion(){} //第9行 //函数
}
由于覆盖,最后AO为
AO:{
b:function(){} //第7行 变量
a:fucntion(){} //第5行 //函数
d:fucntion(){} //第9行 //函数
}
至此,预编译完成。
```
根据上述产生的AO对象执行代码
1. function test(a) {
2. console.log(a); //输出function a(){}
3. var a = 123; //a变为123
4. console.log(a); //输出123
5. function a() {}
6. console.log(a); //输出123
7. var b = function() {}
8. console.log(b); //输出function(){}
9. function d() {}
10. }
11. test(1)
闭包
我对闭包有两种理解:
1.上级作用域内变量的生命周期,因为被下级作用域内引用,而没有被释放。就导致上级作用域内的变量,等到下级作用域执行完以后才正常得到释放。
2.当执行函数后,返回值为一个函数时,随之返回的还有声明这个函数(函数中的函数)时执行上下文里面其他的所有变量。(这些变量就组成了一个闭包)