声明提前

在js中存在一种机制,在程序正式执行之前,会将var声明的变量和function声明的函数预读到当前作用域的顶部,这就是变量提升和函数提升,这种机制也可以称为变量提升。
例如:

  1. function a(){
  2. console.log(10);
  3. }
  4. a(); //20
  5. function a(){
  6. console.log(20);
  7. }
  8. a(); //20
  1. 本意是想通过这个函数分别打印输出1020,但是由于声明提前,后一个a函数将前面第一个a函数覆盖,导致10无法输出,更要注意的是函数的声明提前是整体提前,而变量只是把声明给提前了,所以为了解决这种全局作用域弊病的问题,建议采用letconst来代替varletconst是块级作用域),或者采用闭包来解决。<br />通常JS引擎会在正式执行前进行一次“预编译”,就是将变量声明和函数声明提升至当前作用域的顶端,然后再执行。下面将具体介绍JavaScript中的变量提升与函数提升。


  1. console.log(a); // undefined
  2. console.log(fn); // fn(){var b = 2}
  3. console.log(b); // Uncaught ReferenceError: b is not defined
  4. var a = 1;
  5. function fn() {
  6. var b = 2;
  7. };
  1. 在预编译之后的代码逻辑如下:
  1. //预编译之后
  2. function fn(){
  3. var b = 2;
  4. }
  5. var a;
  6. console.log(a);
  7. console.log(fn);
  8. console.log(b);
  9. a = 1;

声明提前优先级

“函数会首先被提升,然后才是变量。”——《你不知道的JavaScript》

  1. console.log(foo);
  2. function foo(){
  3. console.log("函数声明");
  4. }
  5. var foo = "变量";
  6. /* 输出为:function foo(){
  7. console.log("函数声明");
  8. } */

预编译之后的实际情况:

  1. function foo(){
  2. console.log("函数声明");
  3. }
  4. var foo
  5. console.log(foo);
  6. foo = "变量";

函数提升优先级⽐变量提升要⾼,且不会被变量声明覆盖,但是会被变量赋值覆盖。

可以得出声明提前的优先级为:函数声明>变量声明。

暂时性死区

ES6规定,如果在区块中存在let和const命令(class也有暂时性死区),这个区块对这些命令声明的变量,从这个区块的一开始到它声明之前的这块区域形成了封闭作用域,凡是在这之间(声明之前)使用了这些变量,就会报错。即使用了let或const声明的变量之前,这些变量都是不可用的。这就成为“暂时性死区”。
例如:

  1. var a = 10;
  2. if (true) {
  3. //变量a的暂时性死区 start
  4. console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
  5. //变量a的暂时性死区 end
  6. let a = 20;
  7. }