我认为在搞清楚变量提升这个问题之前先弄清楚JS引擎执行和域(下一篇再说吧)这两个东西
JS引擎执行
分为两个阶段:预处理阶段(编译),执行阶段
(1)预处理阶段
一共完成四件事情:(以 var a=10;
进行解释)
- 词法分析:将
var a=10;
分解为var/a/=/10/;五个代码块 - 语法分析:将词法单元流(数组)转化成抽象语法树AST(Abstract Syntax Tree)
- 生成可执行代码:相当于是再把AST转换成浏览器可执行的代码,或者是各种语言引擎可执行的代码。
在编译阶段,会创建变量对象(VO),变量对象中存放所有变量声明(以变量名建立一个属性,undefined为变量值),所有函数声明(在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用), 函数的所有形参(如果是在函数上下文中)。
var a=10;
相当于变为了:
var a=undefined;//编译阶段
(2)执行阶段
执行阶段,变量对象(VO)变为了活动对象(AO)
未进入执行阶段前,变量对象中的属性都不能访问!但是进入到执行阶段之后,变量对象转变成了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。
var a=10;//执行阶段
提升机制
(1)变量提升
变量提升我理解为:在编译阶段将变量提升到所在域的最开头。
console.log(a);//undefined
var a=10;
在编译阶段,a的值为undefined,所以输出为未知。
(2)函数提升
函数分为表达式函数和声明式函数,只有声明式函数会进行变量提升(将整个函数提到所在域的最开头)。
function myTest(){
foo();
function foo(){
alert("我来自 foo");
}
}
myTest();//我来自 foo
function myTest(){
foo();
var foo =function foo(){
alert("我来自 foo");
}
}
myTest();//报错,因为表达式函数不会提升
总结:变量提升也就是在编译阶段,将变量和声明式函数赋了一个默认值,一个是undefined,一个是函数体本身。
注意:还有一个很有意思的例子
var a = 1;
function foo() {
if (!a) {
var a = 2;
}
alert(a);
};
foo();
我刚开始很纳闷为啥是2,后来想了下:在函数作用域中,变量提升为var a=undefined
,if语句中对undefined取非为true,所以会执行if里面的内容。
参考:https://www.jb51.net/article/172608.htm
https://www.cnblogs.com/damonlan/archive/2012/07/01/2553425.html
https://www.cnblogs.com/TomXu/archive/2012/01/16/2309728.html