ES6:var、let、const的区别

本文深入探讨ES6中var、let和const三种变量声明方式的区别,包括变量提升、作用域、初始化及重复定义等问题,帮助读者理解如何正确使用这些关键字。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从ES6开始 js 申请变量的方式有:var、let、const。今天我们来一起讨论这三种的区别。

关于变量提升

想必大家都知道,用var声明变量,js会在预编译时进行变量提升。即如下代码,不会报错。

function a(){ 
	console.log(x);//undefined
}
a();
var x = 2;

可能你会疑惑,既然提升了为什么还是undefined,难道没有提升?
是这样的:js在预编译时,确实会将var声明的变量进行提升,但此时并没有为其赋值,所以是undefined,否则就会报错。不信我们看看const和let。
在这里插入图片描述
有图有真相^ _ ^ 由此我们知道const 和 let 必须先声明再使用 (let也是同样报错,在这里就不截图了)

暂时性死区

定义:在代码块内,使用let/const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // 暂时性死区结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

“暂时性死区”也意味着typeof不再是一个百分之百安全的操作。

typeof a;//ReferenceError
let a;

但直接typeof一个未声明的变量不会报错,返回undefined。

比较隐蔽的死区

function fun(x=y,y=2){
	return [x,y];
}
fun();//ReferenceError

调用 fun 函数之所以报错(某些实现可能不报错),是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于“死区”。

总结:ES6 规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

作用域

ES5中只有全局作用域和函数作用域。因此导致一些弊端

  • 变量提升导致覆盖
var a = new Object();
funcrion fun(){
  console.log(a);
  var a = 1;//编译到这一句不会进行操作,因为a已经声明过了,执行到这一句才会赋值为1
  console.log(a);
}
fun();//undefined 1

具体请看 js预编译

  • 循环计数变量污染
for(var i=0;i<5;i++){
	····
}
console.log(i);//5

而ES6中const,let 支持块级作用域,有效避免变量覆盖

const a = 'a'; 
let b = 'b';
var c = 'c';  
if(1) {  //创建块级作用域
	const a = 'a2';  
	let b = 'b2';  
	var c = 'c2';    
	console.log(a, b, c);// 输出 a2 b2 c2 
}  
console.log(a, b, c);// 输出 a b c2  如果if语句内容变成函数,本句c的值是也是c,原因是函数执行完后,销毁了函数内部变量,另外函数外部也不能访问内部变量,与覆盖没有关系

关于const

在很多人眼里,这个声明的是一个常量,但是这是一个很错误的认识。实质上是变量名字在内存中的指针不能够改变,但是指向这个变量的值 可能 改变

const arr =["Amy"];
arr.push("Mike"); 
console.log(arr);// ["Amy", "Mike"]

常量arr储存的是一个地址,这个地址指向一个对象(数组也是一种对象)。不可变的只是这个地址,即不能把arr指向另一个地址,但对象本身是可变的,可以为其添加值。

但是,如果我们尝试修改变量索引到一个新的数组——即使是和现在内容一样的数组,则会报错,如下:

const arr = ["Amy"];
arr=["Amy"];
console.log(arr);//  Uncaught TypeError: Assignment to constant variable.报错不能给常量赋值

符合预期的for循环

for (var i = 0; i != 3; i++) { 
 setTimeout(function() {  
	console.log(i);
  },10);
}// 依次打印 3 3 3

上面的变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

  for (let i = 0; i != 3; i++) { 
  setTimeout(function() 
  {  console.log(i); 
  },10);
}//依次打印 0 1 2

变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

有人想问:上面说,每次循环i都会创建一个新变量,但let不允许重复定义,这个作何解释?

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190401175329972.png
在这里插入图片描述
我们观察发现,这样声明变量并不会报错,说明1、2不是同一作用域,而是两个单独的作用域。而在1中 let 为什么可以在每次循环时被重新创建呢,是因为,在每次创建时,各个 i 都是一个独立的作用域。

设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
如果没有在子作用域对循环变量进行修改或重新定义时,则会沿着作用域链向上一级寻找,如上例子,会打印0、1、2;反之会使用本级作用域中的变量 i。

总结

const、let 缩小了变量作用域,完美避免变量污染;const 固定变量(即固定变量类型)。

声明方式变量提升作用域初始化重复定义
var全局不需要允许
const支持块级不允许
let支持块级不需要不允许
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值