变量、函数提升
在 ES6 之前,JS 没有块级作用域的说法,只有全局和函数作用域。 按照正常的编程逻辑来说,变量或者函数,都应该先声明,再调用。 变量提升即将变量声明提升到它所在作用域的最开始的部分。
变量提升
var a = 1
b = 2
console.log(a + b)
var b
// 输出
3
var a = 1
console.log(a + b)
var b = 2
// 输出
NaN
函数提升
fun()
function fun() {
console.log('fun')
}
// 输出
fun
fun()
var fun = function() {
console.log(1)
}
// 输出
Uncaught TypeError: fun is not a function
TIP
JS 中创建函数有两种方式:函数声明和函数字面量。只有函数声明才存在函数提升。
优先级
函数提升优先于变量提升。
console.log(a)
var a = 3
function a() {
console.log(10)
}
// 输出
function a() { console.log(10) }
等同于
function a() {
console.log(10)
}
console.log(a)
a = 3
代码块内的提升
以 if 代码块为例,var 变量提升照常,没什么好说的。比较诡异的是 if 块中的函数提升。
警告
不同浏览器中关于此的实现可能不同,以下研究基于 Chrome V103 版本,就图一乐,不用太在意。if 块内请谨慎使用函数声明,实在要用的话,请用函数表达式代替。
规则 (基于 Chrome V103 版本背景,其他浏览器内部实现规则可能不一致,导致结果不一样)
- 函数会有两个提升,一个提升到当前块级作用域顶部,一个提升到最靠近的作用域顶部
- 到当前块级作用域顶部的提升,属于常规的函数提升,没什么好说的
- 到最靠近的作用域顶部的提升,就比较诡异了
- 首先会类似于
var变量提升一样,将函数变量提上去,并初始化为undefined - 等到内部真正执行到函数声明语句处的时候,会将当前函数变量内最新的值顶上去,更新先前最靠近的作用域内提上去的函数变量的值
- 如果
if条件为false,则永远不会执行到函数声明语句处了,提到最靠近作用域的函数变量值没有外界更改的话,就一直是undefined了 - 如果
if块内的常规函数提升,函数变量在if块内函数声明语句之前,没有另外被赋值的话,执行到函数声明语句处时,就会将该函数声明的内容作为最新的值顶上去更新- 只有在函数声明语句之前,函数变量另外被赋值才能被更新到外界
- 在函数声明语句之后,另外被赋值,只在内部有效,对外界没有影响
- 如果
- 首先会类似于
文字太长,举个栗子分析一波
console.log(a)
if (true) {
a = 1
function a() {}
a = 5
console.log(a)
}
console.log(a)
// 输出
undefined
5
1
等同于
// 第一个函数提升,类似于 var,将函数变量 a 提升到顶部,初始化为 undefined
var a
a = undefined
// 输出 if 块外部当前函数变量 a 值 undefined
console.log(a)
if (true) {
// 第二个函数提升,常规函数提升,提升到顶部
function a() {}
// 函数变量值被另外赋值为 1
a = 1
// 执行到函数声明语句处时,将函数变量 a 的最新值更新到外界,这里最新值是 1
// function a() {}
// 函数变量值被另外赋值为 5。然而此次赋值不会对外界造成影响,影响外界的赋值仅到函数声明语句前为止
a = 5
// 输出 if 块内部当前函数变量 a 最新值 5
console.log(a)
}
// 输出 if 块外部当前函数变量 a 最新值 1
console.log(a)