了解JavaScript变量提升

什么是变量提升

变量提升(Hoisting)被认为是, JavaScript中执行上下文 工作方式的一种认识。从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到全局代码或者代码块的最前面,但这么说并不准确,实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。

变量提升的各种情况

  • 正常变量使用,先声明再使用
bla = 2;
var bla;
console.log(bla);// 2
//可以隐式地将以上代码理解为
var bla; // 变量声明会提升到作用域顶部
bal = 2; // 赋值会被保留在原位置
console.log(bla);// 2

建议始终在作用域顶部声明变量(全局代码的顶部和函数代码的顶部),这可以清楚知道哪些变量是函数作用域(局部),哪些变量在作用域链上解决。

  • 在变量声明之前使用
function doSomething() {
    console.log(bar); // undefined
    var bar = 111;
    console.log(bar); // 111
}
//可以隐式地将以上代码理解为
function doSomething() {
    var bar; //声明提升至代码块顶部
    console.log(bar); // undefined
    bar = 111;
    console.log(bar); // 111
}

如果去除变量声明

function doSomething() {
    console.log(bar); // Uncaught ReferenceError: bar is not defined
}
  • 同名变量多次声明

同名变量多次声明,重复的声明会被忽略,每次赋值都会执行,调用时,使用最近的一次赋值。

// example 1
var bar=9;
console.log(bar);//9
var bar;
console.log(bar);//9
// example 2
var bar=9;
console.log(bar);// 9
 var bar=3;
console.log(bar); // 3
  • 函数声明提升

JavaScript 中的函数声明被提升到了函数定义。你可以在函数声明之前使用该函数:

hoisted(); // logs "foo"

function hoisted() {
  console.log('foo');
}
//可以理解为
function hoisted() {
  console.log('foo');
}
hoisted(); // logs "foo"

重复的函数声明,后者会覆盖前者。

hoisted(); // logs "bar"

function hoisted() {
  console.log('foo');
}

function hoisted() {
  console.log('bar');
}

注意:函数表达式不会被提升,如下:

notHoisted(); // TypeError: notHoisted is not a function

var notHoisted = function() { //函数表达式
  console.log('bar');
};

变量提升也适用于其他数据类型和变量。变量可以在声明之前进行初始化和使用。但是如果没有初始化,就不能使用它们。

JavaScript 只会提升声明,不会提升其初始化。

函数和变量相比,会被优先提升。这意味着函数会被提升到更靠前的位置。

let 与 const 对变量提升的影响

  • 块作用域
{
    let a='test';
}
console.log(a); // throw Error: Uncaught ReferenceError: a is not defined
  • 必须先声明,再使用
let a = 1;
function test() {
    console.log(a);
    const a = 2;
}
test(); // throw Error: Uncaught ReferenceError: can't access lexical declaration 'a' before initialization
  • 同一作用域不能存在相同标识符的变量或者函数
// example 1
function test(params) {
    let params = 1;
}
test(0);// throw Error: Uncaught SyntaxError: redeclaration of formal parameter params
   
// example 2
function func() {
    let test = 1;
    let test = 3;
}
func();// throw Error: Uncaught SyntaxError: redeclaration of let test
   
   // example 3
function func() {
    let inner=2;
    function inner (){
        console.log(111);
    }
}
func();// throw Error: Uncaught SyntaxError: redeclaration of let inner
  • class关键字的在声明方面类似let和const
// example 1
{
    class Test {
    }
}
var t1 = new Test();
// throw Error: Uncaught ReferenceError: can't access lexical declaration 'Test' before initialization
   
// example 2
var t1=new Test();
class Test{}
// throw Error: Uncaught SyntaxError: redeclaration of let Test
   
// example 3
class Test{}
var Test=1;
// throw Error: Uncaught SyntaxError: redeclaration of class Test

参考

  1. MDN:https://developer.mozilla.org/zh-CN/docs/Glossary/Hoisting
  2. JavaScript变量提升总结:https://juejin.cn/post/7050746117734023181