一、变量提升是什么?
1.1 一个变量提升的例子
console.log(myName)
var myName = 'xiaoming'
showName()
function showName() {
console.log('xiaoli')
}
执行结果如下:
AB1462FF-A0B6-427A-8353-D8A49F775726.png
可以看到,在声明变量前打印变量,打印出的是 undefined;在声明函数前执行函数,则会直接执行。
这是为什么?
二、变量提升是怎么发生的?
2.1 声明与赋值
在了解为什么变量提升前,先要弄清楚什么是声明,什么是赋值
- 变量声明与赋值
var myName = 'xiaoming'
上面这行代码,其实是两行代码:
var myName // 声明语句 其中 var 是声明语句的关键字
myName = 'xiaoming' // 赋值语句
- 函数声明与赋值
function showName() {
console.log('xiaoli')
}
上面这行代码整体,是一个函数声明语句,其中 function 就是函数声明的关键字
var showName = function() {
console.log('xiaoli')
}
这行代码虽与之前的代码执行结果一样,都有了一个函数 showName,但真正的执行过程却不同。
这行代码可以分为两行代码:
var showName // 声明语句 var 是关键字
showName = function() {
console.log('xiaoli')
} // 赋值语句
这样拆开是不是清楚多了?和变量一样,也是先声明,后赋值,只是赋的值是一个函数而已。
2.2 JS 的执行过程
上面分清楚了声明与赋值,就可以来讨论下在 JS 代码究竟是如何执行的了。
编译
在浏览器中运行的代码会先进行编译,在编译后,会生成两个东西:
执行上下文、可执行代码。
执行上下文就是 JS 代码的运行环境,里面会保存变量、函数、函数的this指向、函数的参数等信息。所有的声明语句都会在编译时被解析并保存在对应的上下文中。
可执行代码按字面理解就是“可以被执行的代码”。那什么代码可以被执行呢?目前我的理解是:除了声明语句之外的语句,比如赋值语句、函数调用语句等执行
在这个过程,就是我们熟悉的 JS 引擎会一行一行地执行编辑阶段生成的可执行代码
2.3 变量提升是如何发生的
到现在为止,所有的了解变量提升前需要了解的内容都了解了。来讨论下开头的那个例子:
console.log(myName)
var myName = 'xiaoming'
showName()
function showName() {
console.log('xiaoli')
}
我们来模拟一下代码的执行过程:
2.3.1 编译阶段:
- 生成全局的执行上下文。第一行放入执行代码
- 第二行拆分成两行代码:
1 var myName
2 myName = 'xiaoming'
执行第一行时,在全局执行上下文中放入一个变量:
globalContext: {
myName: undefined
}
将第二行放入可执行代码
- 将
showName()
这行代码放入可执行代码 - 识别出最后一句代码是赋值语句,在全局执行上下文中放入一个变量:
globalContext: {
myName: undefined,
showName: function(){console.log('xiaoli')}
}
// 实际情况下,函数会被存储在堆里,这里的 showName 存储的只是函数的引用
2.3.1 执行阶段:
- 执行第一行代码
console.log(myName)
,在目前的全局上下文中寻找 myName 变量,发现值为 undefined,打印出 undefined - 执行第二行代码
myName = 'xiaoming'
,将全局上下文中的 myName 变量的值改为'xiaoming'
- 执行第三行代码
showName()
,在目前的全局上下文中寻找 showName 变量,发现存在,且值为函数,执行这个函数,打印出'xiaoli'
最终,打印出的值如下:
// undefined
// 'xiaoli'