(三)JS基础:变量提升

170 阅读3分钟

1. 什么是变量提升?

  1. JS代码文件的运行分为编译阶段和执行阶段,而变量提升发生在编译阶段
  2. 在当前上下文中(全局、局部、块级),JS代码自上而下执(之前,浏览器会提前处理一些事情,(可以理解为词法解析的一个环节),会把当前上下文所有带var和function关键字进行提前声明或者定义。
  3. var关键字只会提前声明
  4. function会提前声明并且定义
  5. 基于var和function在全局上下文中声明的变量(全局变量),会映射到GO(全局对象:window)上作为window的属性

1.1 var变量提升和function声明提升

console.log(num) // 打印出undefined:num变量会提前声明,初始化为undefined
var num = 12

// 打印出123
// 在词法解析阶段,遇到function会提前声明,并且定义函数(fn=函数)
fn() 
function fn() {
    console.log(123)
}

需要注意的是:只有函数声明才会被直接提升,使用函数表达式声明的函数不会被提升。

// 会报错 --> TypeError: fn2 is not a function
// 因为var 关键字只会提前声明,此时fn2应该为undefined
fn2() 
var fn2 = function() {
    console.log(999)
}

1.2 let、const与var的区别

  1. var和function会进行变量提升,ES6中的let和const不会
  2. 使用var声明的变量,在词法环境中会被初始化为undefined,所以在访问这些变量的时候,不会抛出异常报错
  3. ES6中的let和const不会变量提升,如果在声明之前访问这些变量就会抛出ReferenceError异常,temporal dead zone:暂时性死区
console.log(num)
var num = 12

console.log(n) // ReferenceError: n is not defined
let n = 99

1.3 在条件语句中的变量提升

不论条件是否成立,都要进行变量提升(在条件语句中,带function的在新版本浏览器中只会提前声明,此时是不会赋值的,所以此时fn是undefined)

  • 老版本浏览器:var会变量提升,function会声明提升并且赋值
  • 新版本浏览器:var会变量提升,function会声明提升,但不会赋值,也是undefined
console.log(num, fn) // undefined, undefined
if (!('num' in window)) {
    var num = 12
    function fn() {
        console.log(123)
    }
}

2. 变量提升练习题

/*
 * 一、变量提升阶段:
 *   1. 遇到第一个function fn时,会把函数声明提升,并且给fn赋值, 此时fn函数 => 1
 *   2. 第二个function fn时,此时fn已经声明过了,不会再继续声明,但是要重新赋值, 此时fn函数 => 2
 *   3. 遇到var fn,此时fn已经声明过了,不会再继续声明,所以此时不做任务处理
 *   4. 第四个function fn时,此时fn已经声明过了,不会再继续声明,但是要重新赋值, 此时fn函数 => 4
 *   5. 第五个function fn时,此时fn已经声明过了,不会再继续声明,但是要重新赋值, 此时fn函数 => 5
 * 二、变量提升阶结束后, fn = function () { console.log(5) }
 *   所以前面三个fn()打印出5,后面给fn重新赋值为function () { console.log(3) }
 *   所以最后三个fn()打印出3 
 */

fn()
function fn() { console.log(1) } 
fn()
function fn() { console.log(2) }
fn()
var fn = function () { console.log(3) }
fn()
function fn() { console.log(4) }
fn()
function fn() { console.log(5) }
fn()

// 打印出5,5,5,3,3,3