一、前言
JavaScript运行代码总结下来就是三步,即分析代码、预编译和执行语句。分析代码就是对代码排查一些错误;预编译就是在执行代码前对变量、函数等声明;执行语句就是从上往下执行代码。本文主要对第二步预编译进行分析,希望可以帮助到大家!
二、代码是怎样运行的
1. 在执行过程中,若使用未声明的变量,js执行会报错。
例如:
console.log(a); // a 在执行前没有声明,执行会报错
a = 123; // 赋值
2. 在一个变量定义之前使用它,不会报错,但该变量的值为undefined,而不是定义的值。
例如:
console.log(a); // 执行时不会报错,a 的值为 undefined,而不是123
var a = 123; // 定义并赋值
3. 在一个变量定义之前使用它,是不会报错的,且函数能正常执行。
例如:
console.log(a); // 值为 undefined
foo(); // 结果为 hello
var a = 123;
function foo() {
console.log('hello');
}
为什么代码执行会出现这些现象呢?代码执行的顺序到底是怎么样的呢?要解释这些问题,下面我们引入变量提升。
三、变量提升
JavaScript 代码在执行过程中,JavaScript 引擎会把变量声明部分和函数部分提升到代码的最前面的"行为"。变量提升发生在代码编译的时候。(这里提到的变量提升包括变量提升和函数提升)
先看下面这段代码:
function foo() {
console.log(a); // undefined
var a = 123;
}
foo();
上述代码中 a 的结果是undefined,它的实际执行顺序如下:
function foo() {
var a;
console.log(a);
a = 123;
}
foo();
接下来我们看一道经典的面试题:
console.log(a); // undefined
var a = 100;
function foo(){
console.log(a); // undefined
var a = 200;
console.log(a); // 200
}
foo();
console.log(a); // 100
输出的结果为:
// undefined
// undefined
// 200
// 100
相信大家对变量提升已经有了一定的了解,那么问题来了,在变量提升的过程中,是先提升的在前面,还是后提升的在前面呢?是变量声明提升在前面还是函数提升在前面?下面我们就引入今天的主角预编译。
四、预编译
1.预编译发生在函数体里时(四部曲)
- 创建AO对象(Activation Object);
- 找形参和变量声明,将变量声明和形参作为AO的属性名,值为undefined;
- 将实参和形参的值统一;
- 在函数体里找函数声明,将函数名作为AO对象的属性名,值赋予函数体。
来看下面这个例子:
function fn(a) {
console.log(a); // [Function: a]
var a = 123;
console.log(a); // 123
function a() {}
console.log(a); // 123
var b = function() {}
console.log(b); // [Function: a]
function d() {}
}
fn(1);
在函数执行之前会发生预编译,也就是在 fn(1) 之前会创建一个 AO 对象,fn(1) 执行时 AO 对象有如下变化过程:
AO = {
a: undefined
b: undefined
}
------>
AO = {
a: 1
b: undefined
}
------>
AO = {
a: function a() {}
b: undefined
d: function d() {}
}
代码执行时,先执行 AO 对象,然后从上往下执行代码,最后代码的执行结果为:
// [Function: a]
// 123
// 123
// [Function: b]
2.预编译也发生在全局 (三部曲)
- 创建GO对象;
- 找形参和变量声明,将变量声明和形参作为GO的属性名,值为undefined;
- 在全局里找函数声明,将函数名作为GO对象的属性名,值赋予函数体。 来看下面这段代码:
a = 100
function fn() {
console.log(a); //undefined
a = 200;
console.log(a); //200
var a = 300;
console.log(a); //300
}
fn();
var a;
先创建一个 GO 对象
GO:{
a: undefined --> 100
fn: function fn(){}
在函数体里创一个 AO 对象
AO:{
a: undefined
}
执行时先执行 AO 在执行 GO,然后再按从上到下执行代码。
最后的执行结果为:
// undefined
// 200
// 300
以上就是预编译的一些底层原理!希望对大家有用,谢谢支持!