1.回顾var
基本用法
早在ES6前我们定义变量使用var命令
使用方式如下:
var a = 10;
有些问题
在使用var来定义变量时,根据js的语言机制,我们会遇见一些问题,最最常见的就是以下几种
- 变量提升
console.log(a);//undefined 变量a进行了提升
var a = 1;
- 没有块级作用域
经典案例来袭 haha
var a = [];
for(var i = 0; i<10; i++){
a[i] = function(){
console.log(i)
}
}
a[6]();//10
console.log(i); //10 变量泄漏到全局
- 重复声明
var a = 1 ;
//...
//在代码量很大时,开发者会无意间重复声明覆盖变量
var a = 2 ;
ES6的解决
为了解决var没有块级作用域、变量泄漏到全局、变量提升、变量可以重复声明等问题,ES6出现了let、const两种新的命令,下面介绍这两种命令的各自特点
2.let 命令
基本用法
ES6新增命令,用来声明变量,和var的使用方式类似,但是声明的变量只在let所在的代码块内有效
{
let a = 10;
var b = 1;
}
console.log(a)// a is not defined 超出let定义的代码块范围
console.log(b)//1
for循环中的使用
因为let只在定义块级中有效,因为let特别适用在for循环中
for(let i = 0; i<10; i++){
//...
}
console.log(i);// i is not defined
上述的经典案例为了解决数组内每个函数都打印10的问题,通常会使用立即执行函数进行处理,但是使用let就方便很多了,我们来看一下
var a = [];
for(let i = 0; i<10; i++){
a[i] = function(){
console.log(i);
}
}
a[6]();//6
console.log(i) //i is not defined 这里就不存在变量泄漏全局的问题了
上述代码有两处需要注意
- 因为let定义的i只在每次循环的块级作用域中生效,也就是每次循环都会新生成一个i,每个i都是一个新的变量
- for循环中每次在生成i的时候,JS引擎会记住上轮循环的值,初始化本轮的i时会根据上一轮的i值进行计算
- 循环结束后在外围调用i会报错,因为i只在循环时的块级作用中生效,这也就解决了变量泄漏的问题
无变量提升
字面意思let定义的变量不存在变量提升
console.log(a); a is not defined 报错!无法在声明变量之前使用
let a = 1;
暂时性死区
暂时性死区:temporal dead zone,简称 TDZ
通俗的解释是在代码块内let、const命令声明变量之前,该变量都是无法使用的
var a = 123;
if(true){
// ReferenceError 报错,如果块级内let、const定义了该变量,那么在该变量声明之前无法使用,在声明前这个无法使用该变量的区域就叫做暂时性死区
a = 321;
let a;
}
不能重复声明
同字面意思,let和const定义的变量在同块级作用域内无法重复声明
//报错
function(){
let a = 1;
var a = 10;
}
//报错
function(){
let a = 1;
let a = 10;
}
3.块级作用域
为什么需要块级作用域
举两个最简单的例子
- 内层变量覆盖外层变量
var a = 1;
function fn(){
console.log(a);//2.这里打印的就是被提升的a,从而出现了内层变量覆盖了外层
if(false){
//1.没有块级作用域,此时if中的a会被提升到函数内部最上层
var a = 2;
}
}
fn(); //undefined
- 变量泄漏为全局变量
这点之前的案例中已经有提到,var定义的变量 for循环结束后,变量会泄漏到全局不会消失
let、const带来了块级作用域
ES6之前并没有块级作用域,实际上let、const为JS带来了块级作用域,使得JS在{}代码块内具有作用域,我们之前展示的案例,体现了这一点
但是有几点需要注意:
- ES6之前块级作用域内不予许声明函数ES6后可以
- 函数声明会提升到全局作用域或函数作用域的头部
- 函数声明会提升到所在块级作用域的头部
- ES6中的块级作用域必须要有大括号
if(true) let a = 1;//报错
if(true){
let a = 1;//可以
}
4.const 命令
基本用法
const 声明一个只读的常量,一但声明常量的值就不能改变
const a = 1
a = 2//报错
const b; //报错 由于b声明的变量不可以改变值,那么const声明的变量在声明时就必须要初始化值
const的本质
实际上const保证的并不是变量的值不发生改变,而是变量指向的内存地址保存的数据不能改变,简单数据类型(数字、字符串、布尔值),值就保存在变量指向的那个内存地址,相当于常量,但是复杂数据类型保存的是一个指向实际数据的指针,因此只要保证指针不发生变化就可以;
const obj = {};
obj.name = "jack"; //可以
obj = {}//报错!地址改变
const arr = [];
arr.push(1);//可以
arr = []//报错!地址改变
基于const的特性,建议开发时除非明确的知道变量需要发生改变,否则建议使用const声明变量
和let特性相同
const 和 let 相同都有块级作用域、不能重复定义、没有变量提升、暂时性死区等特性