let,var和const 有什么区别?
1、作用域不同
let和const是块级作用域,var是函数作用域,花括号{}就是块级作用域,函数作用域就是只能在函数里面使用。
下面是一些例子:
(1)变量 a 用 let 声明,是块级作用域,在{}外未声明,所以使用时会报错
{
let a = 0;
}
console.log(a); // ReferenceError: a is not defined
(2) 变量 b 用 let 声明,是块级作用域,可以在{}内声明后使用
{
let b = 0;
console.log(b); // 0
}
(3) 变量 c 用 var 声明,是函数作用域,可以在{}外使用。第一个 c 时变量还未赋值,所以打印 undefined,第二个 c 变量赋值 0,所以打印的 0
console.log(c); // undefined
{
var c = 0;
}
console.log(c); // 0
暂时性死区:
如果区块中使用 let 和 const 命令声明变量,这个区块就会对这些命令声明的变量形成封闭的作用域。在变量被声明之前使用这些变量,就会报错,这就是暂时性死区。
console.log(person); // ReferenceError: Cannot access 'person' before initialization
let person = 'a';
块级作用域解决了ES5中的两个问题:
(1)内层变量可能覆盖外层变量
使用 var 赋值时,在{}外给 a 赋值为0,在{}内给 a 赋值为1,可以看到最终内层的 1 覆盖了外层 a 赋值的 0
var a = 0;
{
var a = 1;
}
console.log(a); // 1
使用 let 赋值时,由于 let 是块级作用域,所以 {} 内的变量 a 不会影响 {} 外的变量 a ,这是两个完全独立的变量
let a = 0;
{
let a = 1;
console.log(a); // 1
}
console.log(a); // 0
(2)用来计数的循环变量泄露为全局变量
可以看到使用 let定义的变量 i 在 for 循环外不能访问,但是使用 var 定义的变量 i,在 for 循环外还能继续访问。
let arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {};
console.log(i); // 报错:i is not defined
for (var i = 0; i < arr.length; i++) {};
console.log(i); // 5,泄露为全局变量
2、变量提升
var存在变量提升(将变量声明提升到当前作用域的顶部),let和const不存在变量提升,所以 let 和 const 变量只能在声明之后使用,否则会报错。
(1)使用 var 声明变量 b,由于变量声明被提升,所以访问不会报错,只是没有赋值,所以输出的是 undefined
console.log('b', b); // b undefined
var b = 2;
(2)使用 let 和 const 声明变量,不存在变量提升,所以不能在声明之前使用,会报错
console.log('a', a); // ReferenceError:Cannot access 'a' before initialization
let a = 1;
console.log('c', c); // ReferenceError:Cannot access 'a' before initialization
const c = 3;
3、常量与变量
const 声明的值是常量,赋值后就不可以修改,但是并不是值不能改动,而是值指向的那个内存地址不能改动,而 let 和 var 声明的变量值可以被修改。
(1)对于基本类型的数据(Number、String等),其值就保存在变量指向的那个内存地址,因此不能修改。如下:
使用 const 声明变量 person,初值为 a 字符串,将 person 的值修改成 b 时会报错
const person = 'a'
person = 'b'; // TypeError: Assignment to constant variable. 赋值失败
(2)对于引用类型的数据(Object、Array)来说,变量指向数据的内存地址,保存的只是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。如下:
使用 const 声明变量 person,初值为一个对象,对象中包含属性 name,值为 a 字符串,将 person.name 的值修改成 b,再添加一个属性person.age,可以看到输出值 person 已被修改成功
const person = {
name: 'a'
};
person.name = 'b';
person.age = 12;
console.log(person); // { name: 'b', age: 12 }
4、给全局添加属性
在全局作用域中用var声明的变量为全局变量,并且能用全局对象访问,但是let和const不行。(浏览器的全局对象是window,Node的全局对象是global)
var a = '111';
window.a // '111'
let b = 2;
window.b // undefined
5、重复声明
var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的变量,const和let不允许重复声明变量,重复声明会报错。
var a = 0;
var a = 'aaa'; // a 被覆盖
console.log(a); // aaa
let a = 0;
let a = 'aaa';
console.log(a); // SyntaxError: Identifier 'a' has already been declared
6、初始值
在声明时,var 和 let 可以不用设置初始值,而且const声明变量必须设置初始值,且不能使用null占位
(1)let 和 var 声明的变量可以不设置初值,输出是 undefined
let a;
console.log('a', a); // a undefined
var a;
console.log('a', a); // a undefined
(2)const 声明的变量不设置初值会报错
const a; // SyntaxError: Missing initializer in const declaration