文章目录
1. let 和 const 命令共同点
let
和 const
命令异同点如下:
1.1 声明的全局变量,不属于顶层对象的属性
顶层对象,在浏览器环境中指的是 window
对象,在 Node 环境中指的是 global
对象。
1️⃣ 在 ES5 中,顶层对象的属性与全局变量是等价的。
var name = '小鱼'
window.age = 18
console.log(window.name); // '小鱼'
console.log(age); // 18
上面代码中,顶层对象的属性赋值与全局变量的赋值是同一件事。说明通过 var
声明的全局变量会自动挂载到 window
对象下(成为 window
对象的属性)。
2️⃣ 在 ES6 中,为了保持兼容性,var
和 function
命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let
、const
和 class
命令声明的全局变量,不属于顶层对象的属性。
var name = '小鱼';
let age = 18;
const PI = 3.14
console.log(window.name); // '小鱼'
console.log(window.age); // undefined
console.log(window.PI); // undefined
上面代码中,全局变量 age
由 let
命令声明,所以它不是顶层对象的属性,返回 undefined
。
1.2 声明的变量只在它所在的代码块有效
{
var v_name = '小鱼';
let l_name = '小熊';
const PI = 3.14;
console.log(l_name); // '小熊'
console.log(PI); // 3.14
}
console.log(v_name); // '小鱼'
console.log(l_name); // Uncaught ReferenceError: l_name is not defined
console.log(PI); // Uncaught ReferenceError: PI is not defined
上面代码中,在代码块之外访问 var
、let
和 const
命令声明的变量,结果 var
声明的变量返回了正确值,let
和 const
声明的变量报错。这表明,let
和 const
命令声明的变量只在它所在的代码块内有效。
🚨 注意:在 for
循环中,设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = "小鱼";
console.log(i);
}
// '小鱼' x3
上面代码中,输出了3次 小鱼
。这表明函数内部的变量 i
与循环变量 i
不在同一个作用域,有着各自单独的作用域(同一个作用域不可使用 let
重复声明同一个变量)。
1.3 不存在变量提升
1️⃣ var
命令会发生 变量提升 现象,即变量可以在声明之前使用,值为 undefined
。
console.log(name); // undefined
var name = '小鱼';
// 相当于下面代码
var name;
console.log(name);
name = '小鱼';
上面代码中,变量用 var
命令声明,会发生变量提升,即脚本开始运行时,变量 name
已经存在,但是没有值,所以会输出 undefined
。
2️⃣ let
和 const
命令声明的变量一定要在声明后使用,否则报错。
console.log(l_name);
// ReferenceError: Cannot access 'l_name' before initialization
// 在初始化之前无法访问“l_name”
console.log(PI);
// ReferenceError: Cannot access 'PI' before initialization
// 在初始化之前无法访问“PI”
let l_name = '小熊';
const PI = 3.14;
console.log(l_name); // '小熊'
console.log(PI); // 3.14
上面代码中,let
和 const
命令声明的变量不会发生变量提升。这表明在声明变量之前前,变量是不存在的,这时如果用到它,就会抛出一个错误。
1.4 暂时性死区
ES6 规定,如果块级作用域中存在 let
和 const
命令,这些命令声明的变量就绑定了这个区域。凡是在声明变量之前操作(获取或赋值)这些变量,就会报错。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)
{
// TDZ 开始
console.log(l_name); // ReferenceError: Cannot access 'l_name' before initialization
l_name = '小熊'; // ReferenceError: Cannot access 'l_name' before initialization
let l_name; // TDZ 结束
console.log(l_name); // undefined
l_name = '小鱼';
console.log(l_name); // '小鱼'
}
上面代码中,在 let
命令声明变量之前,都属于变量 l_name
的死区。
1.5 不允许重复声明
let
和 const
命令不允许在相同作用域内,重复声明同一个变量。
{
let name = '小鱼';
var name = '小熊'; // SyntaxError: Identifier 'name' has already been declared
}
{
const NAME = '小鱼';
var NAME = '小熊'; // SyntaxError: Identifier 'name' has already been declared
}
因此,不能在函数内部重新声明参数
// 报错
function fun(name, age) {
let name; // SyntaxError: Identifier 'name' has already been declared
}
// 不报错
function fun(name, age) {
{
let name;
}
}
2. const 命令
2.1 声明的变量是只读的
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.14;
console.log(PI); // 3.14
PI = 3; // TypeError: Assignment to constant variable.
const
声明的变量不得改变值,这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。
const PI; // SyntaxError: Missing initializer in const declaration
2.2 不允许更改的是变量保存的内存地址
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
const obj = {};
obj.name = '小鱼';
obj.age = 18;
obj = {}; // TypeError: Assignment to constant variable.
上面代码中,常量 obj
储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把 obj
指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。