预编译(通常称为 "变量提升")是 JavaScript 的一种特殊机制,发生在编译阶段的早期 。它会将变量和函数声明 "提升" 到当前作用域的顶部,但赋值不会提升。
编译过程
v8 引擎读取到js代码后,先编译后执行
编译全局 ->执行全局(有函数) ->编译函数 ->执行函数 ->继续执行全局
函数体编译过程
- 创建函数的上下文执行对象
- 找形参和变量(包括函数表达式)声明,将形参和变量名作为对象的属性名,值为undefined
- 将实参和形参相统一
- 在函数体里面找函数声明(不管函数表达式),函数名作为上下文对象的属性名,值为函数体
全局编译过程
- 创建全局执行上下文对象
- 找变量声明,将变量名作为对象的属性名,值为undefined
- 在全局找函数声明,函数名作为上下文对象的属性名,值为函数体
练习
代码最后输出什么?
js
function foo(a, b) {
console.log(a);
c = 0
var c;
a = 3
b = 2
console.log(b);
function b() {}
console.log(b);
}
foo(1)
编译全局
- 创建全局执行上下文对象
- 未找到变量声明
- 找到函数声明,建立对象属性foo,值为函数体
最后得到如下的全局执行上下文对象
js
GLOBAL:{
foo: 函数体
}
执行全局
执行foo(1)
,编译foo函数
编译函数
- 创建函数的上下文执行对象
- 找到形参a、b和变量变量声明c作为对象属性名,值为undefined
- 将实参和形参相统一,把实参1赋值给形参a
- 在函数体里面找到函数声明
function b()
,建立对象属性b,值为函数体(因为已经存在属性b,所以将b的undefined属性改为函数体)。
最后得到如下函数的上下文执行对象
js
FUNCTION:{
a:/*undefined ->*/ 1
b:/*undefined ->*/函数体
c:undefined
}
执行函数
- 执行打印语句
console.log(a);
,在函数的上下文执行对象中找到属性a的值为1,则输出 1 - 执行赋值语句
c = 0
,将属性c的值改为0 - 执行赋值语句
a = 3
,将属性a的值改为3 - 执行赋值语句
b = 2
,将属性b的值改为2 - 执行打印语句
console.log(b);
,在函数的上下文执行对象中找到属性b的值为2,则输出 2 - 执行打印语句
console.log(b);
,在函数的上下文执行对象中找到属性b的值为2,则输出 2
因为全局只需执行一个函数,所以当函数执行完毕全局也执行完毕,至此整个程序的编译执行过程我们已经分析完成。
答案为 :
1
2
2