JavaScript 变量提升

143 阅读2分钟

es6之前

关于变量提升这一定义,MDN种是这样说的

变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识。在 ECMAScript® 2015 Language Specification 之前的JavaScript文档中找不到变量提升(Hoisting)这个词。

从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。

在es6之前,只有var关键字,不管变量在哪里定义的都会提升到作用域的顶端(确切来说在es6之前JavaScript只有全局作用域函数作用域)

console.log(a);  // undefined
var a = 1

被编译后大致是这样的

var a;
console.log(a);
a = 1;

除了变量的提升,方法也是一样的

fn();  // this is a function
function fn(){
    console.log('this is a function');
}

需要注意的是,如果是变量接收方法,在变量赋值之前调用会报错

fn();  // TypeError: fn is not a function
var fn = ()=>{
    console.log('调用啦~');
}

es6之后

在es6加入了两个声明变量的关键字letconst,并且有了块级作用域概念的出现. 块级作用域,简单来说就是用{}包裹起来的区域,包括但不限于方法for循环if语句甚至是单独的{}等.

在声明let或者const变量时,是不存在变量提升这一说的,并且出现了暂时性死区这一概念.

consolg.log(a);  // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 1;  // const 定义是一样的

暂时性死区

ES6 规定:如果区块中存在 let 和 const,这个区块对这两个关键字声明的变量,从一开始就形成了封闭作用域。假如尝试在声明前去使用这类变量,就会报错。这一段会报错的区域就是暂时性死区。

注意点,在块级作用域中定义了跟外部一样的变量,可能会出现暂时性死区而报错

const a = 1;
function fn(){
    console.log(a);  // Uncaught ReferenceError: Cannot access 'a' before initialization
    // ...do something
    const a = 2;
}
fn()

使用switch时要特别注意下

let a = 1;
let b = 1;
switch(a) {
    case 1:
        console.log('b is ', b); // Uncaught ReferenceError: Cannot access 'b' before initialization
        break
    case 2:
        let b = 2;
        // ... do something
        break
    default:
        console.log('b is ', b)
        break
}

尽量别在switch中声明变量,如果要声明的话请用下面这种方式

let a = 1;
let b = 1;
switch(a) {
    case 1:
        {
            console.log('b is ', b); // b is  1
            break
        }
    case 2:
        {
            let b = 2;
            // ... do something
            break
        }
    default:
        console.log('b is ', b)
        break
}