3

JavaScript的解析(预处理)与执行

详见:http://www.cnblogs.com/foodoi...

执行上下文

JavaScript在执行一个“代码段”之前,即解析(预处理)阶段,会先进行一些“准备工作”,例如扫描JS中var定义的变量、函数名等,进而生成执行上下文。

JS中的“代码段”分为三种:全局代码段、函数体代码段、eval代码段。(注:ES6之前,JS不存在“代码块”作用域的概念,即除了函数之外所有“{}”里的代码,都属于全局作用域)

全局代码段“准备工作”包括:

1.变量、函数表达式 —— 变量声明,默认赋值为undefined;
2.this —— 赋值;
3.函数声明 —— 赋值。

函数体代码段“准备工作”包括:

1.变量、函数表达式 —— 变量声明,默认赋值为undefined;
2.this —— 赋值;
3.函数声明 —— 赋值;
4.参数 —— 赋值;
5.argument —— 赋值;
6.自由变量的取值作用域 —— 赋值。

evel()不推荐使用,所以不再分析evel代码段。

至此,“执行上下文”的定义可以通俗化为 —— 在执行代码段之前(预处理阶段),把将要用到的所有变量都事先拿出来,有的直接赋值,有的先用undefined占个空,这些变量共同组成的词法环境,即为执行上下文环境。

在执行js代码时,会有数不清的函数调用次数,会产生许多个上下文环境。这么多上下文环境该如何管理,以及如何销毁并释放内存呢?这就需要“执行上下文栈”来解释了。

执行上下文栈

通过上文我们知道:预处理全局代码时,会产生一个执行上下文环境。每次调用函数的预处理时,都会产生一个执行上下文环境。其实,当这个函数调用完成时,它的执行上下文环境以及其中的数据就会被销毁,执行过程再重新回到全局上下文环境。同一时刻,处于活动状态的执行上下文环境只有一个。

实现这一压栈出栈过程的机制就是“执行上下文栈”。

图片描述

执行上下文栈的压栈出栈过程实例代码:

var a = 10,                 //1.进入全局上下文环境
    fn,
    bar = function(x) {
        var b = 5;
        fn(x+b);            //3.进入fn函数上下文环境
    };

fn = function(y) {
    var c = 5;
    console.log(y+c);
}

bar(10);                    //2.进入bar函数上下文环境

预处理时,首先创建全局上下文环境:

图片描述

然后执行代码,全局上下文环境中的变量都被赋值:

图片描述

当执行到调用bar函数时,跳转到bar函数内部,对其进行预处理,创建bar函数的执行上下文环境:

图片描述

并将这个函数上下文环境压栈,设置为活动状态,开始执行bar函数体内代码:

图片描述

当执行到调用fn函数时,跳转到bar函数内部,对其进行预处理,创建fn函数的执行上下文环境,并压栈,设置为活动状态,开始执行fn函数体内代码:

clipboard.png

fn函数执行完毕后,此次调用fn所生成的上下文环境出栈,并且被销毁(已经用完了,就要及时销毁,释放内存),bar函数的执行上下文环境回到活动状态:

clipboard.png

bar函数执行完毕后,调用bar函数所生成的上下文环境出栈,并且被销毁(已经用完了,就要及时销毁,释放内存),全局上下文环境回到活动状态:

clipboard.png

全局代码执行完成,全局上下文环境出栈,并且销毁(已经用完了,就要及时销毁,释放内存),代码执行完毕。

以上是一段代码执行上下文环境的完整变化过程,但有一种情况的代码,其执行上下文环境并未按上述过程销毁,这就是接下来我们的重点研究对象 —— 闭包

要谈闭包,我们还得先认识下作用域自由变量,敬请期待... ...


Jerry
226 声望32 粉丝