# 预编译
预编译又称为预处理,是做些代码文本的替换工作。是整个编译过程的最先做的工作。
一段js代码的执行需要经过三个步骤,
- 分析代码
- 预编译
- 执行语句
# 预编译表现
- 任何变量,如果变量未经申明就赋值,此变量就为全局对象所有
- 一切var申明的全局变量全是window的属性
解释一下第一点, 变量a没有使用任何的申明关键字, 所以window具有了属性a
a = 10;
console.log(a); // 10
console.log(window.a); // 10
第二点,只有var申明的全局变量才会变成window属性,支持块级作用域的浏览器,用let或者const申明的没有这个行为
var b = 20;
console.log(b); // 20
console.log(window.b); // 20
let c = 20;
console.log(c); // 20
console.log(window.c); // undefined
看一下下面的例子
function foo() {
var a = (b = 10);
}
foo();
console.log(b); // 10
执行的顺序是
- b = 10
- var a = b
b 没有被关键字申明,所以变成了全局对象的属性。
# 预编译详解
- 函数声明整体提升
- 变量 声明提升
看一段下面的例子
function f(a) {
console.log(a);
var a = 10;
console.log(a);
function a() {}
console.log(a);
var b = function () {}
console.log(b);
}
f(1);
要看懂上面的输出,还是需要再借助一下变量对象的概念。
变量对象会包括:
- 函数的所有形参 (如果是函数上下文)
- 由名称和对应值组成的一个变量对象的属性被创建
- 没有实参,属性值设为 undefined
- 函数声明
- 由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建
- 如果变量对象已经存在相同名称的属性,则完全替换这个属性
- 变量声明
- 由名称和对应值(undefined)组成一个变量对象的属性被创建;
- 如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性
预编译步骤
创建AO对象
AO = { }
找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
AO = { a: undefined b: undefined }
将实参值和形参统一
AO = { a: 1 b: undefined }
在函数体里面找函数声明,如果变量对象已经存在相同名称的属性,则完全替换这个属性
AO = { a: function a(){} b: undefined }
第一行 console.log(a);
我们去AO对象找,输出函数a
第二行 var a = 10
,变量声明其实已经提升, 所以这里做的主要就是AO.a = 10。此时的AO变成
AO = {
a: 10
b: undefined
}
第三行 console.log(a);
我们去AO对象找,输出 10
第四行 在预编译阶段就已经被提升到顶层了,所以这一句可以看做没有
第五行 console.log(a);
我们去AO对象找,输出 10
第六行 解释完, AO变成
AO = {
a: 10
b: function b() {}
}
第七行输出函数b