js预编译
js运行过程分为三步:
1. 语法分析
2. 预编译
3. 解释执行
1.语法分析:
也就是检查js 代码是否有【语法错误】 写法错误
3:解释执行:
对每一行代码分别解析执行,
对提升的变量重新赋值。
预编译的产生总体来说的话就是变量提升,变量提升的过程某些情况下会导致变量在各个阶段值不同
GO、AO对象
在预编译过程之中全局上编译前会产生GO对象(也就是window对象去对对象去做判断以及存储数据),函数体中会产生AO对象去实现相同的功能,AO实现步骤如下图所示(那么全局时步骤也相同,只是少了不存在的参数部分)
函数体预编译四部曲
预编译(函数执行前)
创建AO对象
找形参和变量声明
实参形参统一
在函数体里面找函数声明,值赋给函数体
脚本代码块script执行前与之相同就是少了相关参数的部分,输出使用对象时函数会在AO对象中去找,如果AO对象中没有那么会顺到GO对象中去找。
实例分析
看个案例解析一下上述过程
function test() {
console.log(b);
if(a) {
var b = 100;
}
console.log(b);
c = 234;
console.log(c);
}
var a;
test();
a = 10;
console.log(a);
console.log(c);
这边分解了一下预编译过程更加方便理解一点
// 过程分析
1.生成GO到test()执行生成AO
GO {
a: undefined, // test执行还没到给a赋值
test: function() {...}
}
AO {
b: undefined // 解释一下这一步虽然这边对b的声明实在条件语句之中,但预编译过程是这一步还是会被提升到最前面
},
2. 函数执行完
// c=234函数内部直接赋值相当于直接定义在GO之中
// a 为undefined所以if语句没有实现
GO {
a: 10,
test: function() {...},
c: 234
}
AO {
b: undefined
}
// 所以函数最后输出为: undefined、undefined、234、10、234
复制代码特例:非匿名立即执行函数
因为当 JS 解释器在遇到非匿名的立即执行函数时,会创建一个辅助的特定对象,然后将函数名称作为这个对象的属性,因此函数内部才可以访问到该对象属性,但是这个值又是只读的,所以对它的赋值并不生效,所以打印的结果还是这个函数,并且外部的值也没有发生更改。
var foo = 1;
(function foo() {
foo = 10;
console.log(foo); // 输出foo这个函数
}())
当然我们只用let块级声明去定义变量时由于产生暂时性死区就不会有这一类变量提升的问题
总结
预编译(函数执行前)
- 创建AO对象(Active Object)
- 查找函数形参及函数内变量声明,形参名及变量名作为AO对象的属性,值为undefined
- 实参形参相统一,实参值赋给形参
- 查找函数声明,函数名作为AO对象的属性,值为函数引用
预编译(脚本代码块script执行前)
- 查找全局变量声明(包括隐式全局变量声明,省略var声明),
2:变量名作全局对象的属性,值为undefined - 查找函数声明,函数名作为全局对象的属性,值为函数引用
预编译阶段发生变量声明和函数声明,没有初始化行为(赋值),匿名函数不参与预编译 ,只有在解释执行阶段才会进行变量初始化 。