一、前置知识
- JS是如何运行的
- 编译型语言(如:Java),编译步骤分为:词法分析->语法分析->语义检查->代码优化和字节码生成。
- 解释型语言(如:js),编译步骤为:词法分析->语法分析->生成语法树。
- 词法分析阶段
- 词法分析拆解为三部分:
分析形参、分析变量、分析函数三个部分。 以var answer = 1;举例,在词法分析过程被拆分成了多个token(词法单元)。
<示例1>
- 语法分析
- 将步骤1.2生成的token转换为抽象语法树(AST),如下示例。
<示例2>
- 执行阶段
js在解释执行阶段,Js引擎是严格按着
作用域机制(scope)来执行的,并且Js的变量和函数作用域是在定义时决定的,而不是执行时决定的。具体作用域相关可以参考我的另一篇系列文章《JS系列之二、作用域》
截图使用AST解析网站参考地址:esprima.org/demo/parse.…
二、变量提升
- 只有声明被提升,初始化不会被提升 <示例3>
- 源语句
var a='我是一个变量'
- 提升后的语句
var a; // 声明被提升
a= '我是一个变量';
- 声明会被提升到当前作用域的顶端
- 参考目录一下第三小节
《执行阶段》的介绍,Js引擎是严格按着作用域机制(scope)来执行的 <示例4> 以函数作用域示例 - 源代码语句
function A(){
//...其他逻辑
var a = '我是函数内部变量'
}
- 提升后语句
function A(){
var a;// 提升到作用域顶端
//...其他逻辑
a = '我是函数内部变量'
}
三、函数提升
函数的声明有两种方式:声明式和表达式
声明式:function A(){...}
表达式:var a = function(){...}
- 函数声明和初始化都会被提升
- 函数表达式只提升声明 上面1、2可共同参考下面<示例5>
// 未提升前的源代码
console.log(a);
console.log(b);
function a() {}
var b= function(){}
// 提升后的代码
function a() {} // 函数声明和初始化都会被提升
var b; // 函数表达式只提升声明
console.log(a); // function a() {}
console.log(b); // undefined
var b= function(){}
四、提升优先级
- 函数的优先级高于变量,且函数与变量存在同名的情况,函数会覆盖变量
// 提升前的源代码
console.log(a);
console.log(a());
var a = 3;
function a() {
console.log(10);
return "im's function"
}
console.log(a)
// 提升后的代码
function a() {
console.log(10);
return "im's function"
}
// var a; //被覆盖掉了
console.log(a); // a() {console.log(10);return "im's function"}
console.log(a()); // im's function
a = 3; // 相当于给函数a赋值
console.log(a) // 3
- 这里的覆盖关系还可以参考
活动对象,
JS运行的时候,会产生一个活动对象
Active Object,简称AO这个AO是干什么的呢,通俗点讲就是这个AO 在分析形参、分析变量、分析函数中记录和整合变量及函数。类似于下面代码
var a = 1;
function a() {}
// AO挂载属性:
真实的AO过程并不是这样,这里只是方便理解通俗举例。
var AO ={}
// 1.分析变量:发现了一个a变量
AO.a=undefined
//2. 分析函数:发现了一个a方法
AO.a=function() {} // 这里覆盖掉了同名变量a
五、结语
本篇文档到此结束,有任何不妥和问题之处欢迎各位大佬能及时指正。