前端面试中出现概率最高的题目之一。要想知道这个题的答案,首先我们要知道它们的特性。
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 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。这在语法上称为“暂时性死区”(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新增了块级作用域,在块级作用域外访问用let和const声明的变量报错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;
除了将对象本身冻结,对象的属性也应该冻结。
总结
知道了let和const命令的特性我们就知道了它们解决了什么问题。
-
ES6 规定暂时性死区和
let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,这可能导致意外的变量覆盖或命名冲突。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。 -
let和const不允许重复声明的主要目的是为了增加代码的可读性和安全性。 -
块级作用域的引入使得变量的作用范围更加清晰可控,避免了传统作用域中的一些潜在问题,例如闭包中变量值的共享和泄露。