深刻理解JS中的“预编译阶段”和“执行阶段”
什么是“预编译”
1、javascript是一种解释型语言,例如C、Java等强类型语言中的编译阶段,
它是没有这个步骤的,因此,javascript中有了类似于强类型语言编译阶段的步骤---预编译,
同时,我们需要知道,js引擎不是逐行的解释代码,而是按照代码块解释,
即,以<script></script>标签为块,进行解释,
另外,我们还需要知道的是,预编译过程是在执行过程的前一刻发生并执行完毕,
也就是说到了执行阶段的时候,预编译的过程已经完成了;
2、我们在编程过程中有自己的一套语言和语法,同样的,计算机也有自己的一套,
面对代码,我们可以清晰的知道它的运行方式和顺序,但是,计算机不一定能懂,
因此,预编译也是让计算机“认识”我们所写的代码的过程,
例如下文将会提到的,AO对象,就是JS引擎在执行代码过程中重要的依赖;
javascript中的声明方式
在js中声明的方式有两种,变量声明和函数声明
即,var(let、const)和function
了解这个对下面预编译的过程有重要帮助
下面随着代码分析预编译的过程
<script>
function test(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {
};
console.log(a);
var b = function () {
};
console.log(b);
function d() {
};
}
test(1);
</script>
先自己尝试着写出这段代码执行后的结果是什么?
函数预编译发生在函数执行的前一刻
第一步 创建AO对象
AO对象:官方翻译为执行期上下文
在上述代码中,script标签内,简单的看,有一个函数的定义及该函数的传参执行两个步骤,
js引擎扫描完代码,在 “test(1)”这行代码将要执行的前一刻,开始了“预编译”的过程,
创建js引擎所认识的AO对象,该对象中存放着变量和方法的声明,仅供js引擎进行读取等操作,
每个函数都会有自己的AO对象,此时这个对象是属于test这个函数的,
也就是说这个AO对象就是test函数的作用域
AO {
}
第二步 找形参和变量声明,将变量和形参名作为AO的属性名,值为undefined
形参为a,作为键添加到AO对象中,值为undefined;
变量声明有两个,var a;var b;
注意,由于声明的变量a和形参相同,那么只需要添加一次就可以了,
那么AO对象就变为如下:
AO {
a:undefined,
b:undefined
}
注意:
1、相同的变量名作为一次属性名
2、变量声明,不是函数声明
第三步 将实参值和形参统一
test(1) ===> 实参为1,将1赋值给AO对象中的属性a
AO {
a:1,
b:undefined
}
第四步 在该函数体里面找函数声明,值为函数体
函数声明有两个
function a() {}
function d() {}
其中a函数声明的函数名与变量声明中的变量名相同,在此步骤中,函数声明优先级高于变量声明,
将会覆盖之前的变量声明,同时,添加d的函数声明,
此时AO对象变为:
AO {
a: function a() {},
b: undefined,
d: function d() {}
}
注意:
function xxx() {} 为函数声明
var xxx = function () {} 是函数表达式,是变量声明,不是函数声明,到此时,预编译的过程完成,下面是函数执行过程
1、第2行执行console.log(a);
此时从AO对象中可以知道,a的值为 function a() {}
故,第2行代码执行的结果为 fucntion a() {}
2、代码继续执行,第5行
var a = 123; 执行完该行代码之后AO对象变为
AO {
a: 123,
b: undefined,
d: function d() {}
}
故,第7行代码执行的结果为 123
3、9-11行代码为函数声明
在预编译过程的时候已将其提升,AO对象不变
故,第13行代码执行的结果为 123
4、执行到15-17行代码
变量b在预编译过程仅仅提升声明,并没有对其赋值,此时对变量b进行赋值
AO对象变为:
AO {
a: 123,
b: function () {},
d: function d() {}
}
故,第19行代码执行的结果为 function () {}
5、21-23行代码在预编译过程时候处理过,至此,函数执行完毕
上述代码执行的结果为
function a() {}
123
123
function () {}
练习题
function fn(a, b) {
console.log(a);
console.log(b);
var b = 234;
console.log(b);
a = 123;
console.log(a);
function a() {
};
var a;
var b = function () {
}
console.log(a);
console.log(b);
}
fn(1);