JavaScript之闭包

167 阅读4分钟

前提:通过两天的努力,我总算学懂了闭包。从词法作用域和动态作用域-->执行上下文和执行上下文栈-->活动对象(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.当执行函数后,返回值为一个函数时,随之返回的还有声明这个函数(函数中的函数)时执行上下文里面其他的所有变量。(这些变量就组成了一个闭包)

例子和我的部分理解完全来自上述参考资料的文章或者评论区,至此,闭包算是完全理解了。