一句话,let和const是var的改良版,能用const就不用let, 能用let就不用var。
一、var的缺陷,先来看看使用var定义变量会有哪些问题:
1.var不是块级作用域,下面的代码循环已经结束了,却还可以访问到变量test,可能会引起bug
- for(var i=0;i<10;i++){
- var test = i;
- }
-
- console.log(test);//9
- console.log(i)//10 用来记数的变量也可以访问, 无意中就污染了全局变量
2.var定义的变量,有变量提升,变量提升,会对程序的维护带来困扰。
- if(true){
- console.log('执行了');
- }else{
- console.log('没执行');
- var j = 2;
- }
-
- console.log(j); //输出undefined 定义变量j的代码虽然没有执行,但是却依然不会报错
3.var定义的变量,不会为异步任务单独绑定变量,下面这段代码,本意是让它每隔一段时间输出0-9,结果却输出了10个10
- for (var i = 0; i < 3; i++) {
- setTimeout(function () {
- console.log(i)
- }, 1000);
- }
4.var定义的变量,可以重复定义,这样就显得非常的随意松散
- var a =1;
- var a =2;
-
- console.log(a);//输出后面定义的2
二、let和const就是ES6针对以上问题提出的解决方案,let和var的区别具体如下:
1.let声明的变量是块级作用域的,这个特性解决了原来使用var容易污染全局变量的弊端。
- for(let i=0;i<10;i++){
- let test = i;
- }
-
- console.log(i);//Uncaught ReferenceError: i is not defined
- console.log(test)//Uncaught ReferenceError: test is not defined
2.let声明的变量不存在变量提升,从let的块级作用域开始,到初始化位置,称作“暂存死区”,对于变量的暂存死区中使用变量会报Reference错误。这个特性就使得我们先定义变量再使用的变量,避免了var变量提升带来的难以查找的bug,也增强了代码的可读性。(也有文章认为let和const是有变量提升的,但是从结果上我们直接把let和const理解成没有变量提升是正确的。)
- //使用var
- console.log(i); // 输出undefined
- var i = 2;
-
- //使用let
- console.log(j);
- let j =10; //Uncaught ReferenceError: Cannot access 'j' before initialization
3.var可以重复定义变量,而let不可以,使得定义变量不再像var那么随意
- //使用var
- var a = 1;
- var a = 2;
- console.log(a); // var可以重复定义,输出2
-
- //使用let
- let i =1;
- let i =2;
- console.log(i);// let不能重复定义, Uncaught SyntaxError: Identifier 'i' has already been declared
4.var定义的全局变量属于顶层对象,而let、const声明的全局变量属于顶层对象,这也很容易理解, 因为let的设计初衷就是块级作用域变量,不污染全局变量,显得自由灵活安全。以浏览器为例:
- var a = 0;
- console.log(window.a) // 0
- let b = 1;
- console.log(window.b) // undefined
三、以上let具有的特性,const都有,const和let的区别如下:
1.const在声明常量的时候, 一定要初始化一个值:
- const a=1; //正确
- const b = 1;
- b = 5; //TypeError: Assignment to constant variable.
- const c; //SyntaxError: Missing initializer in const declaration
2.const定义的常量值不允许修改,
- const a = 0;
- a = 1; // TypeError: Assignment to constant variabl
但是如果常量的类型为复杂类型(对象、数组等)时,对于常量值本身的操作是可以的, 因为const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变。
- const a = {};
- a.name='小明'
- console.log(a);
- a = {name:'小明'};
- console.log(a);
四、总结
1.let和const声明的变量时块级作用域,避免了无意中全局变量污染,更加的灵活安全。另一个好处就是在循环语句中,let关键字为每次循环绑定单独绑定一个变量。
2.let和const没有变量提升,提高了代码的可维护性。
3.let和const不可以重复定义变量,修复var可以重复定义变量,使得变量的定义不再随意任性。
4.let和const定义的变量不属于顶层对象。目的也是为了让变量定义更加自由灵活安全。
5.const声明一个常量的时候,一定要赋值。
6.const声明的常量并非真正意义上的常量,只保证变量名指向的地址不变,并不保证该地址的数据不变。
五、拓展
1.对于以下代码,改如何改正
- //本意是要输出0-9,这段代码却输出10个10
- for (var i = 0; i < 10; i++) {
- setTimeout(function () {
- console.log(i)
- }, 1000);
- }
解析:在js中先执行同步任务,再执行异步任务,setTimeout里面的代码是异步任务,等到执行时,var声明的变量i已经是10了。
解法1,利用setTimeout函数的第三个参数把i单独绑定
- for (var i = 0; i < 10; i++) {
- setTimeout(function (i) {
- console.log(i)
- }, 1000, i);
- }
解法2,利用闭包强制让setTimeout记住变量i
- (function(i){
- setTimeout(function () {
- console.log(i)
- }, 1000)
- })(i);
解法3,利用let关键字
- for (let i = 0; i < 10; i++) {
- setTimeout(function () {
- console.log(i)
- }, 1000)
- }
2.对于以下代码,本意是要每隔1秒输出0-9,但这段代码的效果却是1秒钟后,同时输出了0-9,如何修正?
- for (let i = 0; i < 10; i++) {
- setTimeout(function () {
- console.log(i)
- }, 1000)
- }
解析:在js中先执行同步任务,再执行异步任务,setTimeout函数的第二参数,相对的是同步任务结束的那一刻, 而不是上一个异步任务结束的那一刻,所以,修正的方法是动态改变第二个参数。
- for (let i = 0; i < 10; i++) {
- setTimeout(function () {
- console.log(i)
- }, 1000*i)
- }