原文地址:https://finget.github.io/2018/03/01/javascriptPrecompile/
JavaScript在运行时,要经历三步
1. 语法分析 2.预编译 3.解析执行(自上而下)
JavaScript预编译
先思考这么一个题
function fn (a) {
console.log(a);
var a = 123;
console.log(a);
function a(){};
console.log(a);
var b =function (){};
console.log(b);
}
fn(1);
预编译四部曲
- 创建AO对象 Activation Object(执行期上下文)
- 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
- 将实参值和形参统一
- 在函数体里面找函数声明,值赋予函数体
这四步的权重比4>3>2>1,也就是一个覆盖的过程.
函数声明在变量声明的前面
函数声明才存在变量提升。即
function a(){};
,而var b =function (){};
不会提升。
详细分析
先看一个面试中常遇到的问题
console.log(a); // function a(){}
var a = 1;
function a(){};
逐行执行,在AO中是:
AO{
a: undefied
}
AO{
a: function(){}
}
换一换
var a = 1;
console.log(a); // 1
function a(){};
逐行执行,在AO中是:
AO{
a: undefied
}
AO{
a: function(){}
}
// js是自上而下执行的,先执行var a = 1; 所有AO中的a就被覆盖
AO{
a: 1
}
按步骤分析文章开头的例子
- 第一步
AO{
}
- 第二步
AO{
a: undefined,
b: undefined
}
- 第三步
AO{
a: 1,
b: undefined
}
- 第四步
AO{
a: function a(){},
b: undefined
}
解释执行
执行的时候:
AO{
a: function a(){},
b: undefined
}
// a = 123;
AO{
a: 123,
b: undefined
}
结果:
function fn (a) {
console.log(a); // function(){}
var a = 123;
console.log(a); // 123
function a(){};
console.log(a); // 123
var b =function (){};
console.log(b); // function(){}
}
fn(1);
加入window,全局环境
global = 100;
function fn() {
console.log(global);
global = 200;
console.log(global);
var global = 300;
}
fn();
var global;
在全局环境中会生成一个 GO对象 (Global Object),还是按照上面的四步执行。
GO {
global: undefined
}
// 执行到 global = 100
:
GO {
global: 100
}
当执行fn
之前会先生成一个AO:
AO {
global: undefined
}
所以第一次打印global
是undefined
。
这个时候虽然全局变量中的
global
已经是100
,但是fn
函数中自己有global
变量,所以不会引用全局中的。
当执行到global = 200
:
AO {
global: 200
}
所以第二次打印global
是200
最后思考一个问题
function fn(){
var a = b = 100;
console.log(window.a);
console.log(window.b);
}
var a = b =100;
先将100赋值给b,即b=100,此时b没有声明就被赋值,在JavaScript中如果一个变量未声明就直接赋值,那么这个变量就是个全局变量。