zoukankan      html  css  js  c++  java
  • es6入门1-- let与var的区别详解

    一、前言

    说到做到,现在暂时放了放JS模式的读书笔记,打算好好看看ES6,毕竟出了这么久了,还是靠JS吃饭的,都不好好学JS新特性,确实说不过去,我本来是想当读书笔记去记录ES6,但是这个确实是属于边看边用边记忆的,所以还是零散的挑重点去记录吧。

    二、let与var的区别

    1.let 不能重复声明,但var可以

    这是最容易记忆的一点,当我们使用var声明时,是可以反复声明同一个变量的,但并不推荐这个做,因为同名变量的创建对于内存使用是无意义的,所以let的出现也是为了解决早期设计缺陷。
    var a = 1;
    var a = 2;//不会报错
    let b = 1;
    let b = 2;//报错

    2.let会产生块级作用域,且只在自己的作用域内生效,但var不受限制

    ES5 只有全局作用域和函数作用域,那什么是块级作用域?当我们在{}中使用了let或者const时,{}的范围就是一个块级作用域,此时let或const只能在{}中访问,像这样:

    {
      let a = 1;
      var b = 2;
    }
    console.log(a);//报错
    console.log(a);//2

    或者这样:

    if(true){
      let a = 1;
      var b = 1;
    }
    console.log(a);//报错
    console.log(b);//1
    特别注意,let与var在for循环中表现不同
    //for循环用var
    var a = [];
    for (var i = 0; i < 5; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[1]();//5
    a[2]();//5
    a[3]();//5
    //for循环用let
    var b = [];
    for (let i = 0; i < 5; i++) {
      b[i] = function () {
        console.log(i);
      };
    }
    b[1]();//1
    b[2]();//2
    b[3]();//3

    在明白上面2个循环的区别之前,我们先来理一理知识点,我们要知道,for循环中设置循环变量的部位其实是一个父作用域,循环体内部是个子作用域。

    在父子作用域中使用let声明同一变量时,对于此变量而言,两个作用域的同名变量其实是互不相关的两个独立变量:

    {
      let a = 1;
      {
        console.log(a);//报错,暂时性死域,在let a前面使用a不合法
        let a = 2;
        console.log(a);//2
      }
      console.log(a);//1
    }

    即便父子作用域都使用了let,子作用域也能正常继承自己非let的变量,父也能读取子作用域非let的变量。

    {
      let a = 1;
      {
      //这里使用了let,也是块级作用域,但也能正常访问父块级作用域的变量a
      let c = 1;
    var b = 2; console.log(a);//1 } console.log(a);//1 console.log(b);//2 }

    所以下面这个循环输出了三次echo,因为在子作用域中声明的i跟父作用域中的i可以说是完全不同的两个i;

    for(let i = 0;i<3;i++){
      let i = 'echo';
      console.log(i);
    };//输出三次echo

    但如果你将let改为var,你会发现只输出一次,因为没有了块级作用域,父子作用域共用了一个变量i,第一次循环后,子作用域的i被改为了echo,父作用域中i<3的判断无法通过,所以只输出了一次。

    那么回头再看看最初的两个循环,为什么var声明输出了3个5,而let输出了1,2,3呢,尝试去理解,说到底还是块级作用域搞的鬼。

    3.let不存在变量提升

    变量提升可以说是var声明设计不严谨的一点,可能我们在开发中已经习惯声明一个全局变量,然后可以在声明前后都可以使用它,这很方便,但是不符合规范。
    console.log(a);//undefined
    var a = 1;
    console.log(b);//报错
    let b = 1;
    变量提升甚至会违背你代码设计的初衷,达到你不想要的结果。
    var a = 1;
    function f() {
      console.log(a);//undefined
      if (false) {
        var a = 2;
      }
    }
    f();
    你肯定在想,这个地方应该输出1啊,下面的条件判断是false,都没执行,很遗憾,变量提升是声明提前,赋值在原地,条件判断管的是要不要给a赋值,声明早就提前了。这段代码等同于:
    var a = 1;
    function f() {
      var a;
      console.log(a);//undefined
      if (false) {
        a = 2;
      }
    }
    f();

    就问你,惊不惊喜?意不意外?

    4.let存在暂时性死域

    当一个区域存在let声明时,这个区域就形成了一个封闭的作用域,在let声明前使用这个变量就会报错,也就是只能先声明再使用,这种语法也称为暂时性死域。
    {
      let a = 1;
      {
        console.log(a);//报错
        let a =2;
      }
    }

    三、const声明的特点

    可以理解为,let的特性const都有,不能反复声明,存在块级作用域,不存在变量提升,也有暂时性死域的特点。

    与let区别在于,const声明的是一个常量,一旦声明就无法修改,有个误区需要特别注意,举个例子:

    const a = {};
    a.b = 1;
    console.log(a);//{b:1}

    上述代码中我声明了一个常量对象a,但是a对象修改了,并没有报错,这是为什么呢?从本质上理解,const声明的变量并非变量的值不能修改,而是可以理解为变量的指向不能修改。

    只是基本类型的数据,键值都在栈内存中,但对于复杂数据类型,变量指向的其实是堆内存中存放值所提供的一个地址。所以我们不能修改复杂数据类型的指向,这样就会报错:

    const a = {};
    a.b = 1;
    console.log(a);//{b:1}
    a = [];//这里直接修改了变量a的指向,不允许了。
    console.log(a);//报错

    四、小总结

    let与const的出现对于JS有什么影响或者说好处呢?

    我们知道,在ES5环境中只有全局作用域与函数作用域,这也导致我们通过var声明的全局变量直接与全局对象挂钩,而浏览器环境全局对象是window,所以我们随便声明的全局对象都可以通过window访问,很明显,这样的环境很容易造成混乱。

    var a = 1;
    console.log(window.a)//1

    而ES6带来的let与const,可以说是宣告天下,从此我全局变量将逐步与顶层对象属性脱离开来,还你一片极乐净土。

    当然,ES6中var与function声明的全局变量还是会出现在顶层变量中,但至少现在提供了一种解决方式,JS语言也在成长的越来越好

    番外:

    在记录完let与var在for循环中的不同变现,我觉得我解释的不够合理,还是得单独拿出来说下,即使let与var只是做声明这件小事,我在拆分for循环步骤时,发现了一个很有趣的问题,所以深挖了一下。有兴趣可以阅读我的这篇博客:

    for循环中let var的区别,块级作用域到底造成了什么影响

  • 相关阅读:
    乐观锁和悲观锁
    [转载] Java实现生产者消费者问题
    [转载] 最全最强解析:支付宝系统架构内部剖析(架构图)
    [转载] Linux五种IO模型
    [转载] Hibernate与 MyBatis的比较
    Spring的69个知识点
    Spring MVC工作原理
    你不知道的JS系列 ( 12 ) - 声明提升
    你不知道的JS系列 ( 11 ) - 块作用域
    你不知道的JS系列 ( 10 ) - 立即执行函数表达式
  • 原文地址:https://www.cnblogs.com/echolun/p/10575676.html
Copyright © 2011-2022 走看看