1. 什么是变量提升?
- JS代码文件的运行分为编译阶段和执行阶段,而变量提升发生在编译阶段
- 在当前上下文中(全局、局部、块级),JS代码自上而下执(之前,浏览器会提前处理一些事情,(可以理解为词法解析的一个环节),会把当前上下文所有带var和function关键字进行提前声明或者定义。
- var关键字只会提前声明
- function会提前声明并且定义
- 基于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的区别
- var和function会进行变量提升,ES6中的let和const不会
- 使用var声明的变量,在词法环境中会被初始化为undefined,所以在访问这些变量的时候,不会抛出异常报错
- 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