变量定义变化let/const

let

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

  • 不允许重复声明:let不允许在相同作用域内,重复声明同一个变量。

  • 块级作用域:let 声明的变量,只在 let 命令所在的代码块内有效。解决命名冲突问题,能体现代码的封装性

    let命令每次定义的作用域都在本次循环的方法体内,即每一次循环的i其实都是一个新的变量。

  • 不存在变量提升:var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefinedlet命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

  • 暂时性死区:只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

    var tmp = 123;
    
    if (true) {
      tmp = 'abc'; // ReferenceError
      let tmp;
    }
    // 上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。
    

const

const声明一个只读的常量。一旦声明,常量的值就不能改变。

  • const定义常量时,必须初始化,否则Missing initializer in const declaration
  • 如定义了常量,再去修改就会报错error Assignment to constant variable 不可以给常量赋值
  • const定义的常量,作用域与let相同
  • 应用场景:const用来定义静态变量,加载模块的时候,定义个常量,把模块赋值给常量

比较

var let const
重复定义 允许 不允许 不允许
作用域 不受块级作用域限制 块级作用域(只在声明所在的块级作用域内有效) 块级作用域
变量提升
可以改变 可以改变 不可以改

关于const和let的声明提升

其实,let和const是会被提升的,准确的说是创建被提升了,但是初始化没有被提升

JS中将变量的生命周期分为:创建、初始化、赋值、(回收先不说)

根据ECMA-262中13.3.1的NOTE中所述:

let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

这段话的大概意思就是let和const定义了变量执行的词法环境,变量会在这个环境(上下文环境?作用域?)实例化时被创建,但是在变量的词法绑定之前不允许以任何方式对其进行访问

同时MDN上关于let介绍中也说到: var 和 let 的不同之处在于后者是在编译时才初始化

而通过var声明的变量,变量提升时相当于把创建和初始化进行了提升,没有提升赋值操作。

总结

原则:变量初始化之前,不允许访问,现象:

  • var变量,创建和初始化都提升了,所以可以在声明前直接访问。
  • let 和 const 变量,只提升了创建,编译时才初始化,所以按照编译顺序
    • 如果肯定是声明之前使用的,那必然报错。见现象一
    • 如果不能确定声明与使用的先后顺序,那代码检验不报错,等待编译时。
      • 如果先编译到变量使用的代码,那报错。见现象二
      • 如果先编译到变量声明的代码,那运行正常。见现象三

现象:

function test() {
    console.log(a);
    // console.log(b);
    // let b = 30;  // 现象1:代码直接编写报错
}

// test(); // 现象2:放在这,代码检查无报错,解释运行时报错,ReferenceError: Cannot access 'a' before initialization

let a = 10;

test(); // 现象3:放在这,代码检查、解释执行均不报错。代码成功执行,输出10

results matching ""

    No results matching ""