相比ES5中单一的var定义变量方式,ES6发展成var、let、const三种,前端人员在面试时也经常被问到let和var的区别,这里参考了阮一峰老师的es6和其他博文对这三种命令简单总结一下,如有错误欢迎指正,与君共勉。
一、作用域
1. 为方便后面的理解,这里先简单介绍一下作用域,我对作用域的理解是,
- 只会对某个范围产生作用,而不会对外产生影响的封闭空间。
- 在封闭空间里,外部不能访问内部变量,但内部可以访问外部变量。
2. js中的作用域有:
(1)ES5里只有两种作用域:全局作用域和函数作用域,且函数作用域内声明的变量由于存在hoisting,它的有效范围是整个函数体,如果对下面代码的输出结果有疑问,可结合JavaScript基础——ES5变量提升(Hoisting)理解。
//定义全部变量
var a=2;
//定义外部函数
function outer(){
//访问全部变量
console.log(a);
//定义内部函数
function inner(){
console.log(a);
var a=10;
console.log(a);
}
inner();
console.log(a);
}
outer();
//输出结果:
2
undefined
10
2
(2)ES6除了上面两种,还新增了块级作用域,即最近的花括号所涵盖的范围。
注意:对类似 a=10; 这种给尚未声明的变量赋值的语句,会使得该变量a自动成为全局变量。但在"use strict"定义的严格模式下,不允许使用未声明的变量。
二、var、let的区别
如图,本文总结了var和let的5点区别,分别是:块级作用域、变量提升、暂时性死区、重复声明和window全局访问。
1. let声明的变量拥有块级区域
{
var a=1;
let b=2;
}
console.log(a);
console.log(b);
//输出结果:
1
Uncaught ReferenceError:b is not defined
let声明的变量b作用域为块级,因此在花括号之外访问会报错,而var声明的变量a最小的作用域也是函数作用域,因此可以正常输出。
2. let不存在变量提升
由于var变量具有变量提升的特性,因此变量可以先使用后声明,而let所声明的变量一定要在声明之后使用,示例如下:
console.log(a);
var a=1;
//输出结果:
undefined
console.log(b);
let b=2;
//输出结果:
ReferenceError
3. let有暂时性死区(temporal dead zone,简称TDZ)
只要块级作用域内存在let、const命令,它们所声明的变量就绑定这个区域,不再受外部的影响。并且该区域内,使用let或const命令声明变量之前,该变量都是不可用的,我们把这种语法称为暂时性死区。
示例一:
var tmp=1;
if(true){
tmp=2; //ReferenceError
let tmp;
}
let声明的变量tmp需要先声明再使用,因此上面的代码报错。
示例二:
if(true){
// TDZ开始
tmp=1;
console.log(tmp); //ReferenceError
let tmp; //TDZ结束
console.log(tmp); //undefined
tmp=2;
console.log(tmp); //2
}
上面这段代码,在let对变量声明之前,都属于tmp的“死区”,会出现ReferenceError.
4. let不允许重复声明
当用var重复声明变量时,变量只会在最顶部开始的地方声明一次,后面的声明都会被忽略,仅用于赋值。
var a=1;
console.log(a);
if(true){
var a=10;
console.log(a);
}
console.log(a);
//输出结果:
1
10
10
let不允许在相同作用域内,重复声明同一个变量,也不能在函数内部重新声明参数。
//抛出语法错误:SyntaxError
function func(){
let a=1;
var a=2;
}
//抛出语法错误:SyntaxError
function func(){
let a=1;
let a=2;
}
5. let声明的全局变量不是全局对象的属性
也就是说,不能通过window.变量名的方式访问该变量
三、const
ES6中新增了const关键词,const就是用来定义常量的,常量名习惯大写,遵循的规则有,
- const声明的常量不得改变值
- 声明的同时,必须初始化赋值
- 和let一样:块级作用域+没有hoisting+TDZ+不可重复声明