美文网首页
第二十节: ES6变量声明

第二十节: ES6变量声明

作者: 心存美好 | 来源:发表于2021-12-13 16:30 被阅读0次

    1. 前言: 理解ES6 与 ECMAScript 2015 的关系

    2011 年,ECMAScript 5.1 版发布后,就开始制定 6.0 版了。因此,ES6 这个词的原意,就是指 JavaScript 语言的下一个版本。

    ES6 的第一个版本,就这样在 2015 年 6 月发布了,正式名称就是《ECMAScript 2015 标准》(简称 ES2015)。2016 年 6 月,小幅修订的《ECMAScript 2016 标准》(简称 ES2016)如期发布,这个版本可以看作是 ES6.1 版,因为两者的差异非常小(只新增了数组实例的includes方法和指数运算符),基本上是同一个标准。根据计划,2017 年 6 月发布 ES2017 标准。

    因此,ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,

    ECMAScript的历代版本:
    1997 ECMAScript 1.0

    1998 ECMAScript 2.0

    1999 ECMAScript 3.0

    3.0 版是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了 JavaScript 语言的基本语法,以后的版本完全继承。

    2000 ECMAScript 4.0

    这个版本最后没有通过但是它的大部分内容被 ES6 继承了。因此,ES6 制定的起点其实是 2000 年。

    2007 年 10 月

    ECMAScript 4.0 版草案发布,本来预计次年 8 月发布正式版本,以 ,Yahoo、Microsoft、Google 为首的大公司,反对 JavaScript 的大幅升级,主张小幅改动,以 JavaScript 创造者 Brendan Eich 为首的 Mozilla 公司,则坚持当前的草案。

    2008 年 7 月

    由于争议太多中止 ECMAScript 4.0 的开发,小幅的改进后,发布为 ECMAScript 3.1,激进的部分放到以后版本,会后不久,ECMAScript 3.1 就改名为 ECMAScript 5。

    2009 年 12 月,

    ECMAScript 5.0 版正式发布。ES5 与 ES3 基本保持兼容,有争议的部分放在了next版本中

    2011 年 6 月,ECMAScript 5.1 版发布

    2013 年 3 月,ECMAScript 6 草案冻结,不再添加新功能

    2013 年 12 月,ECMAScript 6 草案发布。然后是 12 个月的讨论 期,听取各方反馈。

    2015 年 6 月,ECMAScript 6 正式通过,2000至今15年

    ECMAScript 6于2015年6月正式发布,成为企业级开发语言,又称ES2015。

    ES6细枝末节很多(因为大家知道,JS是一个千疮百孔的语言,所以ES6在定稿的时候就特别细致,细枝末节极多)

    一. let 命令

    1. ES5 的变量定义

    定义变量(声明变量)

    var a = 12;
    

    1.1 var 的变量的特性

    1. 会发生变量提升
        var a = 12;
        function fn(){
            alert(a);  // undefined
            var a = 5;
        }
        fn()
    
        function fn(bol) {
          /*
          变量提升,自动提升到当前作用域顶层,初始化语句留着条件判断中
          AO
          bol:false
          num:undefined
          */
          if(bol){
            var num =10;
            console.log(num);
          }else{
            console.log(num); 
          }
        }
        fn(false)
        fn(true)
    
    1. ES5 作用域只有全局和函数内部作用域
      for(var i = 0;i < 10; i++){
      // TODO
      }

      // 突然有一天我想弹出i,这个i是几
      alert(i)
      

    变量提升,这个我们之前讲这就是JS语言的特性,后来就有人提议,说这种特性不好,我不知道我程序发生了什么事情,同时for循环的循环变量i会污染全局变量,比较讨厌

    2. ES6 变量定义

    为了解决ES5 变量声明的问题,所以在ES6 新增了两个定义变量的关键词

    let, 就相当于之前的var

    const 常量,定义好了以后就不能改变了

    我们先来看看let;

    2.1 基本用法

    跟使用es5的var一样

    let a = 12;
    console.log(a);
    
    3. 关于ES6 变量

    3.1 变量提升的问题

    之前讲var的时候有变量提升,那么let, const 是否具有变量提升呢

    let a = 12;
    function fn(){
        alert(a);  // 这里当然能用了,这不就是作用域的查找吗
    }
    fn();
    
    // 那么如果我在函数内定义一个let a; 如下
    let a = 12;
    function fn(){
        alert(a);  // 报错,这个时候报引用错误
        let a = 5;
    }
    fn();
    // Uncaught ReferenceError: a is not defined
    // 报错信息是 a 没有并定义
    

    所以let,const不会进行变量提升,必须先定义再使用

    官方的称发是在变量没有let前的所有区域叫TDZ,暂时性死区

    所以必须先定义再使用.用来规范大家编程行为

    let会引发暂时性死区(面试常考):

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

    var m = 10;
    function fun(){
        m = 20;  //报错。函数在预解析阶段会预读所有的语句,发现了let语句,所以就将这个函数变为了一个m的暂时性死区,此时m不允许在let前被赋值。
        let m;
    
        console.log(m);
    }
    
    fun();
    

    3.2. 块级作用域

    我们都知道 在语句中 {}叫做块,

    我们都知道ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

    比如:

    我们刚讲过的for循环的循环变量污染全局

    var s = 'hello';
    for (var i = 0; i < s.length; i++) {  
      console.log(s[i]);
    }
    console.log(i); // 5
    

    还有就是内层变量可能会覆盖外层变量。

    var tmp = 123;
    function f() {
      console.log(tmp);
      if (false) {
        var tmp = 'hello world';
      }
    }
    f(); // undefined
    

    这也是为什么我们需要块级作用域的原因

    但ES6 let const 所定义的变量具有块作用域

    if(true){
        var a = 12;
    }
    alert(a);  // 能用  之前讲的作用域只有全局和函数作用域,什么if for 在全局就当全局用
    
    // 如果此时换成let
    if(true){
        let a = 12;
        // 接下来只能在这里使用
    }
    alert(a);
    

    到了es6 里面

    {let num =111;}
    console.log(num);
        // 只要代码块碰到let,就会组成一个新的块级作用域  let定义的变量不会提升
    

    3.3 同一个变量多次定义的问题

    之前咱们说过变量至少要var一次吧;但是我们多次var一个变量也没有问题

    // ES5
    var a = 1;
    
    var a = 5;
    alert(a);    // 弹出5
    
    // ES6 的年代
    let a = 1;
    let a = 5;
    alert(a);  // 报错
    //Uncaught SyntaxError: Identifier 'a' has already been declared
    

    3.4 关于for循环的循环变量问题

    我们之前讲for循环的时候,是不是讲循环变量是当前作用域的

    for(var i =0 ;i < 10; i++){
        
    }
    alert(i);   // 这个时候i指定是一个10
    

    那么用let定义的for循环呢,会有哪些不同呢

    // 如果我们使用let呢
    for(let i =0 ;i < 10; i++){
        console.log(i);  // 在循环里面是可以正常使用的
    }
    alert(i);   // 报错: 报一个引用错误
    //Uncaught ReferenceError: i is not defined
    // i未定义
    
    // 如果我们在循环体内在let i呢
    for(let i =0 ;i < 10; i++){
        let i = "abc";  // 感觉应该会报错,重复定义嘛,其实不是
        // 可以理解()中的i是父级作用域,循环体内i是子级作用域,互不干扰
        console.log(i);  // 在循环里面是可以正常使用的
    }
    alert(i);   // 报错: 报一个引用错误
    //Uncaught ReferenceError: i is not defined
    // i未定义
    

    3.5 ES6 允许块级作用域的任意嵌套。

    也就是扩展父子级的块作用域

    {
        let a = 12;
        console.log(a);  //12
    }
    console.log(a); // Uncaught ReferenceError: d is not defined
    
    // 如果我开心我是不是可以在块作用域中在加一个块作用域啊
    {
        let a = 12;
        {
            let b = 10;
            console.log(b);  // 10
        }
        console.log(a);  //12
    }
    console.log(a);  // 报错
    
    // 这不叫重复定义,重复定义是不能在同一个作用域中重复定义
    // 如果你愿意包含多少层作用域都可以.但是没什么意义,无论你包多少层,外边又访问不到,只有里面能访问到
    

    let 特点:

    1. 没有预解析,不存在变量提升
      官方的称发在变量没有let前的所有区域叫TDZ,暂时性死区
      所以必须先定义再使用.用来规范大家编程行为
    2. 不能重复定义变量
    3. for循环里比较特殊,可以将()中的循环变量理解为父级作用域中的变量,循环体中的变量理解为子作用域中的变量,所以他们let同一个变量不会报错

    还记得我们讲for循环里面函数的问题吗

    var arr = [];
    for(var i = 0; i < 10; i++){
        arr[i] = function(){
            console.log(i);   // 这里我们是不是希望,数组第几项函数执行,打印几啊
        }
    }
    arr[6]();  //10  结果你发现所有的打印都是10
    
    // 有些小伙伴说我代码没有问题,是的,代码真没有什么问题,这是这么语言特性造成的
    
    // 这是不是就是我们之前讲的函数闭包造成的问题啊
    
    // 这个时候我们换成let去定义
    var arr = [];
    for(let i = 0; i < 10; i++){
        arr[i] = function(){
            console.log(i);   // 你发现这个时候就符合我们的预期了
        }
    }
    arr[6]();  // 6
    

    示例:

    <input type="button" value="aaa"/>
    <input type="button" value="bbb"/>
    <input type="button" value="ccc"/>
    
    <script>
        var aInput = document.querySelectorAll("input");
        
        for(var i = 0 ;i < aInput.length; i++){
            aInput[i].onclick = function(){
                console.log(i)
            }
        }
    

    4. const 声明常量

    4.1 const 基本使用

    const声明一个只读的常量。

    const: 特性和let一样

    只是const定义的变量不能修改,因为人家叫常量,所以不能修改.

    let a  = 12;
    a = 20;
    console.log(a);  // 20
    

    有些人可能会说.老师修改怎么了

    但是实际开发中有些我是不希望它修改的,比如一些配置文件

    const root = 12;
    console.log(root);  // 12 你发现能正常使用
    
    // 一旦更改
    root = 20;  // 直接报出 错误, TypeError 类型错误
    //Uncaught TypeError: Assignment to constant variable.
    // 你把一个常量转换到变量的错误
    

    4.2 声明变量赋初值问题

    const还有一个特点平时我们喜欢先定义变量,再赋初值

    var a;
    a = 10;
    console.log(a);   // 10  没什么问题
    
    let a;
    a = 20
    console.log(a);   // 20  let 也没什么问题
    
    // 但是const有问题
    const a;
    a = 10;
    console.log(a);  // 报错  语法错误
    //Uncaught SyntaxError: Missing initializer in const declaration 
    //缺少初始值在常量定义时
    

    也就是说cons定义时必须赋值,不能后赋值,后赋值也是修改,不能修改

    如果只是定义不赋值也会报错.

    const a;
    console.log(a);  // 报错  语法错误
    //Uncaught SyntaxError: Missing initializer in const declaration
    //缺少初始值在常量定义时
    

    4.2 const 无提升

    const a = 12;
    function fn(){
        alert(a);  // 报错,这个时候报引用错误
        const a = 5;
    }
    fn();
    // Uncaught ReferenceError: a is not defined
    // a未定义
    

    有人说不能修改真的假的,看个例子

    const arr = ["apple","banana"];
    //arr = [];
    //console.log(arr);  // 报错
    // Uncaught TypeError: Assignment to constant variable.
    
    // 哎 我们学过push修改数组
    arr.push("wuwei");
    console.log(arr);  //["apple", "banana", "wuwei"]
    // 发现可以为什么呢
    
    
    const obj ={
        name:"小明",
        age:18
    }
    obj.like ="喜欢乒乓球"
    console.log(obj);
    obj ={}   //报错,地址不能变,属性值可以改变。基本数据类型是操作值的,引用数据类型是操作地址的,const保存的是内存地址,初始化后不能修改
    console.log(obj);
    

    如果真想定义一个动都不能动的,那么对象身上有个方法Object.freeze(),冻结的意思

    const arr = Object.freeze(["apple","banana"]);
    arr.push("orange");
    console.log(arr);  // 这个时候报类型错误
    // Uncaught TypeError: Cannot add property 2, object is not extensible
    // 不能添加属性,对象是不可扩展的
    
    
    const obj ={
        name:"小明",
        age:18
    }
    Object.freeze(obj);   //冻结Object.freeze后修改不了
    obj.like ="喜欢";
    console.log(obj);
    

    这不是const的问题,这是对象的特性问题。冻结后对象的属性不能修改。

    const obj ={
        name:"小明",
        age:18
    }
    const objdj = Object.freeze(obj);   //objdj不能重新赋值,对象的属性也是冻结的不能修改,但是没什么意义
    // obj ={}
    obj.age =20;
    console.log(obj);
    

    总结: const的特点

    1. const`一旦声明变量,就必须立即初始化,不能留到以后赋值。
    2. 一旦声明,常量的值就不能改变。

    5.变量的其他问题

    5.1自执行函数

    IIFE

    (function(){
        // TODO
    })();
    
    // 因为之前只有函数作用域
    
    // 现在有块作用域了
    // 可以使用
    {
       // TODO 
    }
    

    5.2. 关于顶层对象属性与全局变量

    顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。

    window js运行的环境对象(es5)

    global js的全局对象

    window.a = 1;
    a // 1
    
    a = 2;
    window.a // 2
    

    顶层对象的属性与全局变量挂钩,被认为是 JavaScript 语言最大的设计败笔之一。

    为了解决这个问题,es6引入的let 、const和class声明的全局变量不再属于顶层对象的属性。

    而同时为了向下兼容,var和function声明的变量依然属于全局对象的属性

    var a = 1;
    window.a // 1
    
    let b = 1;
    window.b // undefined    b不在是window的属性了
    

    相关文章

      网友评论

          本文标题:第二十节: ES6变量声明

          本文链接:https://www.haomeiwen.com/subject/lkajfrtx.html