0%

作用域链 & 闭包

7.1 作用域链

作用域产生于程序源代码中定义变量的区域,在程序编码阶段就确定了。javascript 中分为全局作用域(Global context: window / global)和局部作用域(Local Scope , 又称为函数作用域 Function context)。简单讲作用域就是当前函数的生成环境或者上下文,包含了当前函数内定义的变量以及对外层作用域的引用。JS 中作用域是可访问变量、对象、函数的集合。

7.11作用域

函数作用域[[scope]]
  • 外部对内部可见

    意思简单点就是 函数里面的变量可以调用函数外面的值,看个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    var scope='g';
    function t(){
    console.log(scope);//undefined
    var scope='1';
    console.log(scope);//1
    }
    t();

    而如果我们把 第四行语句去掉,结果就不一样了,结果值分别是 g , g 。这就是所谓的外部对内部不可见。

  • 内部对外部不可见

    示例:

    1
    2
    3
    4
    5
    6
    7
    function t(){

    var scope='1';

    }
    t();
    console.log(scope);

    运行结果是报错的,因为没有找到 scope 这个变量。

  • 内部优先

这里声明一下,在 c 和 c++ 里面,控制作用域的一般叫做块,块就是用一个大括号括起来的,大括号里面作用域存在,大括号没有,作用域消失。而JS 中只有函数级别的作用域,没有块级别的作用域;换句话来说,只有在进入或者退出函数的时候,作用域会发生变化。

js 的作用域都是函数级别的,例如:

1
2
3
4
5
6
var scope='g';
if(true){
var scope='l';//替换掉 g
console.log(scope);//l
}
console.log(scope);//l

7.12 执行环境和作用域链

执行环境

执行环境(execution context),定义了执行期间可以访问的变量和函数。

知识点:

  • 作用域[[scope]] ,每个函数都有;
  • 作用域是私有属性,只能由 JS 引擎访问;
  • 作用域链,是 AO 和 GO 构成的链
  • 所谓的执行环境,就是根据作用域链依次查找变量和函数:
    • 找到即停
    • 全部找完无果,报错。
  • 作用域链每个函数都要
生成作用域链
  1. 每个函数在定义(函数声明\函数表达式)时会拷贝其父亲函数的作用域链;
  2. 在函数被调用时,生成 AO 然后将 AO 压入作用域链的栈顶。

在这边,有个容易混同的概念:

  1. 函数多次调用时,是产生相同的 AO 还是不同的 AO ?
  2. 函数递归调用时,是产生相同的 AO 还是不同的 AO ?

函数每次调用都会产生一个属于自己的 AO ,一个函数被不同的调用就会产生不同的 AO ,这个函数出去的话,如果计数器变为 0,则这个 AO 就被摧毁。递归调用产生的 AO 也是不同的,在递归调用过程中,每个 scope chair 拷贝的不是他自己的函数,而是拷贝定义它的函数。这是与  

c 和 c++ 不一样的,c 和 c++如果递归调用的话,只有一个栈,这个时候,函数调用自己的话是全部压在栈里边的。如果一个函数递归调用五次的话,那么实际上栈里面压着前4次它们的栈信息。这是与 JavaScript 不一样的地方。

示例:

1
2
3
4
5
6
7
8
function fa(){
console.log(a);//三次返回结果都是 undefined
var a=100;
a++;
}
fa();
fa();
fa();

问个问题,在这边,三次调用 fa() 是使用同一个 AO ,还是不同的 AO ?这个呢,是每次产生不同的 AO ,三次返回结果都是 undefined ,并不会自加,下面是大致解题思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*栈内存
* ST001:GEC-SC:Hp001
* ST002:faEC-SC:HP003(第一次调用,拷贝 scope chair) 退出 ,销毁
* ST003:faEC-SC:HP005(第二次调用,拷贝父类的 scope chair) 退出 ,销毁
* ST004:
* ST005:
* ST006:
* ST007:
*
* 堆内存
* 第一次调用
* HP001:GEC-SC: [Hp002-(GO)](1)
* HP002: GO: {this:window,fa:function{[HP002-(GO)]}}(2) (2)->(3)->第二次调用时(2)
* HP003:faEC-SC:[HP002-(GO),HP004-(fa-AO)](1) 引用次数变为 (0),然后第二次调用时垃圾收集,HP003为空
* HP004:fa-AO: {this:window,a:undefined}(1) a 变为 101,然后第二次调用时 (1)->(0)
*
* 第二次调用
* HP005:faEC-SC:[HP002-(GO),HP006-(fa-AO)](1) 压入栈顶,第二个函数退出时,(1)->(0)
* HP006:fa-AO: {this:window,a:undefined}(1)
*
*
*
* */

第三次 fa() 函数调用与前面两次一样,就不再详写。

全局执行环境
  • Global Object(window)
  • 从见到 JS 代码开始创建
  • 到网页关闭时销毁
函数执行环境
  • Activation Object
  • 从函数调用开始创建
  • 到函数调用结束时销毁

7.2 闭包

7.21 闭包原理

7.22 应用

7.3 立即执行函数

您的支持是对我最大的鼓励