前端面试题:let、const的出现解决了什么问题

78 阅读3分钟

前端面试中出现概率最高的题目之一。要想知道这个题的答案,首先我们要知道它们的特性。

let命令和const命令

ES6新增了let和const命令用于声明变量,我们熟知的ES5声明变量只有var和function两种方法,那么let和const有哪些不同呢?又是为什么要设计成这样?知道了原因也就知道了let、const的出现解决了什么问题。以let命令为例:

1. 不存在变量提升

在ES6发布之前,变量可以在声明之前使用,值为undefined,而let命令声明的变量在之前使用会报错

   //var 声明时
   console.log(b)// 输出:undefined
   var b

   //let 声明时
   console.log(a)// 报错 ReferenceError
   let a

2. 暂时性死区

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。这在语法上称为“暂时性死区”(temporal dead zone,简称 TDZ)。

if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

3. 不允许重复声明

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

   // 报错 SyntaxError: Identifier 'a' has already been declared 
   function func() {
        let a = 10;
        var a = 1;
   }
    
   // SyntaxError: Identifier 'a' has already been declared 
   function func() {
        let a = 10;
        let a = 1;
   }

4. 块级作用域

ES6新增了块级作用域,在块级作用域外访问用letconst声明的变量报错ReferenceError

//var声明变量时 代码块内部的变量会提升到函数作用域的顶层覆盖原有的变量值
function f2() {
    var n = 5;
    if (true) {
        var n = 10;
    }
    console.log(n); // 10
}

f2(); //输出10

//使用let变量时 分别在两个代码块内定义了新的变量
function f1() {
    let n = 5;
    if (true) {
        let n = 10;
        console.log(n); // 10
    }
    console.log(n); // 5
}

f1(); //输出5

const命令

cosnt命令声明的值时一个常量,一旦声明就不可以改变。这也就意味着const声明的值必须立即初始化,不能留到以后赋值。 const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。如果真的想将对象冻结,应该使用Object.freeze方法。

const foo = Object.freeze({});

// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;

除了将对象本身冻结,对象的属性也应该冻结。

总结

知道了letconst命令的特性我们就知道了它们解决了什么问题。

  • ES6 规定暂时性死区和letconst语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,这可能导致意外的变量覆盖或命名冲突。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

  • letconst不允许重复声明的主要目的是为了增加代码的可读性和安全性。‌

  • 块级作用域的引入使得变量的作用范围更加清晰可控,避免了传统作用域中的一些潜在问题,例如闭包中变量值的共享和泄露。

参考:ECMAScript 6 入门-let和const命令