javascript执行流程
在介绍预编译之前,我们可以了解一下javascript文件的执行过程
- 语法分析
- 在执行之前,会对js文件的语法进行检查,如果有错误就会报错,js代码就无法执行
- 预编译
在执行之前,浏览器会将js中用var和function关键字声明的变量,将其声明提前到当前作用域的顶部 - 解释执行
从上到下逐行执行
预编译
预编译分为全局预编译和函数预编译,在预编译时用var和function声明的变量和函数会被提升到当前作用域的顶部,此过程叫做声明提前也叫变量提升。
全局预编译
全部局预编译发生在全局作用域中,在全局作用域中的代码执行前会发生全局预编译
全局预编译有以下流程
- 创建GO(全局对象),在浏览器环境下为window对象
- 将用var声明的变量作为属性名挂到全局对象上,值为undefiend,如果有重名的则直接覆盖
- 将用function声明的函数,作为属性挂到全局对象上,值为函数体,如果有重名直接覆盖
示例如下
console.log(name);
var name = 'Simith'
function sum(a,b){
return a+b;
}
console.log(sum(1,2));
function sum(a,b){
return a+b+1;
}
console.log(sum(2,4));
如果是在强类型语言中第一行输出是会报错的,因为name此时还没有被声明,但是在js中因为预编译的存在使得,第一行仍能正常执行。
变量name的声明被提升到当前作用域的顶部,也就是在输出语句前就有了name的声明,此时并没有被赋值所以其值为undefined
那么下面的4和7youshizenmehuishine
根据预编译的流程,会将sum函数挂在到全局对象上,但是此时有两个sum函数,那么前一个sum函数就会被后面的sum函数覆盖,也就是说,执行的两次sum函数都调用的是后面的sum函数所以输出的结果都是两个数相加之后在再加1的值。
函数预编译
函数预编译发生在函数中,在函数执行前会发生预编译
函数的预编译流程
- 创建AO(执行期上下文),它是一个类似于全局作用域的一个对象
- 查找形参和变量声明,将形参和变量作为属性名挂到对象上值为undefined,如果有重名的则直接覆盖
- 实参值赋给形参
- 将用function声明的函数挂到对象上值为函数体,如果有重名的直接覆盖
示例如下
function test(name,age){
console.log(name);
age = 12;
console.log(height);
var height = 190;
console.log(height);
}
test('Steve',30);
根据以上结果我们可以看出,在预编译期间,name已经被赋值为Steve,因此输出的是Steve。
height在预编译期间,被提升到当前作用域的顶部,被挂到AO对象上值为unfined,因此输出的是undfined
当height被赋值之后值为190因此第二个输的是190
ES5会有预编译的情况,但是在ES6中由于let和const两个关键字的特性,用let和const声明的变量不会出现声明提前的情况
示例如下
console.log(age);
console.log(name);
console.log(PI);
let name = 'Boo';
const PI = Math.PI;
var age = 12;
由于age是用var声明的由于预编译的存会将其声明提前,在因此在赋值前的值为undefiend。
但是name是用let声明的不会将其声明提前因此此时name并不存在所以会报错,这种情况在ES6中叫作暂时死区。
总结
- 预编译发生在js代码执行之前
- 预编译分为全局预编译和函数预编译
- 预编译会发生变量提升,但是用 let 和 const 声明的变量不会被提升