变量、函数提升

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)

参考

Last Updated:
Contributors: Vsnoy