Javascript核心概念(一):预编译

221 阅读3分钟

预编译

前言

Javascript核心概念之一就是预编译,这是Javascript独有的语言特性,Javascript属于解释性语言,何为解释性语言?那就是解释一行执行一行,不像Java等其它语言在执行前需要先进行编译工作,虽然Javascript语言不需要进行提前编译,但是Javascript执行引擎在执行Javascript代码时,会先进行一次通篇扫描进行语法分析,这个过程可以排除一些基本的错误,然后它会执行一个被称之为预编译的过程,因此我们需要了解什么是预编译

预编译的触发时机

在Javascript中预编译触发时机有两种情况,一种是Javascript程序运行前会进行一次预编译,这次预编译会生成一个GO对象(Golbal Object)称之为全局上下文,另一种情况是函数执行前也会进行预编译的过程,函数的预编译会产生一个AO对象(Activation Object)执行期上下文

预编译的作用

不管是全局上下文(Golbal Object)还是执行期上下文(Activation Object),它们都属于一个概念:作用域,对于全局上下文,它储存的信息可以被整个Javascript程序使用,而对于执行期上下文,它保存着函数内部的信息,供函数执行时使用,在Javascript中作用域呈现链式结构,形成作用域链,全局上下文被作为顶层的作用域,函数的执行期上下文在函数执行完毕后会被销毁

预编译的四步曲

我们先来看看函数的预编译过程:

  1. 第一步,创建执行期上下文

生成一个 AO 对象(Activation Object),我们可以把执行期上下文看作是一个对象:

AO {

}
  1. 第二步,形参和变量声明提升

查找形参和变量声明,把形参和声明的变量作为AO对象的属性名,属性值为undefined:

例如有个fn函数

function fn(a) {
    var b = 0;
    function c () {};
};

fn(1);

生成的AO对象如下

AO {
    a: undefined
    b:undefined
}
  1. 第三步,形参和实参相映射

统一函数的形参和实参值,将实参值作为AO对象上的形参名对应的属性值:

AO {
    a: 1
    b:undefined
}
  1. 第四步,函数声明提升

查找函数声明,把函数名作为AO对象的属性名,属性值为函数体:

AO {
    a: undefined
    b:1
    c: function c() {}
}

经过以上四个步骤后,函数的预编译过程就完成了,注意如果在预编译过程中存在同样的标识符,也就是形参、变量名以及函数名存在同名的情况下,由于预编译的步骤顺序,后执行的操作将会覆盖先前的操作。

而接下去函数执行的时候则会忽略那些已经进行过预编译的工作,直接执行有效的代码语句:

例如

var a = 0;

// 可以理解为:

var a; // 对于执行引擎而言,经过预编译后这行代码就是无意义的,因此会选择忽略它
a = 0;

我们再来看看全局的预编译过程:

全局预编译基本上和函数预编译没什么区别,首先:

  1. 第一步,创建全局上下文

生成GO对象(Golbal Object)

GO {

}
  1. 第二步,变量声明提升

查找变量声明,把声明的变量当作GO对象的属性名,属性值为undefined

GO {
 a: undefined
}
  1. 第三步,函数声明提升

查找函数声明,把函数名作为AO对象的属性名,属性值为函数体

GO {
 a: undefined
 fn: function fn(){}
} 

相比于函数预编译而言,全局预编译由于不存在形参和实参的概念,因此少了相关的操作,但其余步骤与函数预编译并无差别,因此整个Javacript程序执行其实也可以看作是一个函数的执行