• let const var区别


    写在前面

    该格式的文字为个人理解,纯大白话

    文章标题前加* 代表该知识点为不好理解或容易遗漏的知识点

    1.var关键字

    1.1 没有块级作用域的概念,有全局作用域、函数作用域的概念

    例1:

    //Global Scope
    {
      var a = 10;
    }
    console.log(a);  //10
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在大括号内部声明a,在大括号外部仍然可以访问,由此可以看出var声明的变量不存在块级作用域(Block Scope)的概念

    例2:

    //Global Scope
    var a = 10;
    function checkscope(){
        //Local Scope
        var b = 20;
        console.log(a);  //10
        console.log(b);  //20
    }
    checkscope();
    console.log(b);  //ReferenceError: b is not defined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上面代码中,在 Global Scope 中用 var 声明了 a,在 checkscope 函数中的 Local Scope(本地作用域、函数作用域)中打印出了 10,但是在 Global Scope 中打印的变量 b 报错了。

    在全局中声明了a,存在全局作用域中,函数(checkscope)内部也可以访问。在函数(checkscope)内部声明的变量,仅存在与函数内部作用域中,函数外无法访问

    1.2 存在变量提升

    例3:

    //Global Scope
    console.log(a);  //undefined
    var a = 10;
    
    checkscope();
    function checkscope(){
        //Local Scope
        console.log(a);  //undefined
        var a;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    先打印了a,然后用var声明变量a,打印结果为undefined。
    变量提升是因为js需要经理编译和执行阶段。js在编译阶段的时候,会搜集所有的变量声明并且提前声明变量

    1.3 全局作用域用var声明的变量会挂载到window对象上
    //Global Scope
    var a = 10;
    console.log(a);  //10
    console.log(window.a);  //10
    console.log(this.a);  //10
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面代码中,打印出了 310,访问 awindow.a 或是 this.a 都是等价的。
    举个例子:比如我要访问 location 对象,使用 location 可以访问,使用 window.location 也可以访问,只不过 window 对象可以省略不写,就像 new Array( )new window.Array( ) 是等价的。

    1.4 同一作用域中允许重复声明
    //Global Scope
    var a = 10;
    var a = 20;
    console.log(a);  //20
    
    checkscope();
    function checkscope(){
        //Local Scope
        var b = 10;
        var b = 20;
        console.log(b);  //20
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    上面代码中,在 Global Scope 中声明了 2a,以最后一次声明有效,打印为 20。同理,在 Local Scope 也是一样的。

    1.5 不初始化值默认为undefined
    //Global Scope
    var a;
    console.log(a);  //undefined
    
    • 1
    • 2
    • 3

    Global Scope 中用 var 声明了 a,但没有初始化值,它的值默认为 undefined
    这里是 undefinedundefined 类型,而不是字符串。

    2.let关键字

    2.1 有块级作用域的概念
    {
       //Block Scope
       let a = 10;
    }
    console.log(a);  //ReferenceError: a is not defined
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在大括号内声明的变量,在大括号外无法被访问

    2.2 不存在变量提升
     console.log(a);  // Uncaught ReferenceError: a is not defined
      let a = 10;
    
    • 1
    • 2

    上面代码中,打印 a 报错:无法在初始化之前访问。说明不存在变量提升。

    2.3 同一作用域内不允许重复声明
    {
      //Block Scope
      let A;
      var A;  //SyntaxError: Identifier 'A' has already been declared
    }
    {
      //Block Scope
      var A;
      let A;  //SyntaxError: Identifier 'A' has already been declared
    }
    {
      //Block Scope
      let A;
      let A;  //SyntaxError: Identifier 'A' has already been declared
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    *2.4 暂时性死区(dead zone)

    在作用域内第一行到使用let、const声明语句之间,存在暂时性死区,访问该变量就会报错。

    ECMAScript2015文档中的解释:
    当程序的控制流程在新的作用域(module、functionblock 作用域)进行实例化时,在此作用域中用 let/const 声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区。

    个人理解:let/const 命令会使区块形成封闭的作用域,在此作用域中,let/const* 声明的变量会先在作用域中被创建,一直到变量被声明之前,该变量都是不可用的,访问就会报错。在语法上称为 “暂时性死区”temporal dead zone,简称 TDZ

    if (true) {
      //暂时性死区(TDZ)开始
      console.log(a);  //ReferenceError: Cannot access 'a' before initialization
    
      let a; //暂时性死区(TDZ)结束
      console.log(a);  //undefined
    
      a = 123;
      console.log(a);  //123
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    例4:

    {
      //Block Scope
      console.log(a);  //ReferenceError: Cannot access 'a' before initialization
      let a = 20;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    专属报错:初始化之前无法访问
    在这里插入图片描述
    区别:

    这里的暂时性死区存在于块级作用域中,若在全局作用域中,则报错‘is not defined’

    在这里插入图片描述

    3.const关键字

    const除了具有以上let中的四种特性(存在块级作用域、不存在变量提升、同一作用域内不允许重复声明、存在暂时性死区)之外,还有以下特点:

    3.1 创建后必须立即初始化,不能留到以后赋值

    在这里插入图片描述
    const 声明的变量 a 没有进行初始化,所以报错。

    *3.2 创建常量

    const创建常量的特点:

    const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
    对于简单类型的数据(数值、字符串、布尔值)而言,值就保存在变量指向的内存地址中,因此等同于常量。但对于复合类型的数据(主要是对象和数组)而言,变量指向的内存地址保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,这完全不能控制。

    例5:
    在这里插入图片描述

    const 创建的变量的内存地址不可改变,而对于简单的数据类型,值就存在于变量指向的内存地址中,所有a的值不可以改变
    例6:
    在这里插入图片描述
    对于复杂的数据类型,内存地址中保存的是一个指针,const只能保证这个指针是固定的,指针指向的数据结构是可以改变的

    3.2.1 es5实现创建常量

    Object.defineProperty
    三个参数:

    1. 属性所在的对象
    2. 属性的名字
    3. 一个描述符对象:
      • configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
      • enumerable:表示能否通过for in循环访问属性,默认值为false
      • writable:表示能否修改属性的值。默认值为false。
      • value:包含这个属性的数据值。默认值为undefined。

    实现:

    Object.defineProperty(window,"args",{
    value: ' this is es5',
    writable: false
    })
    
    • 1
    • 2
    • 3
    • 4

    报错:常量变量赋值
    在这里插入图片描述

    3.2.1 实现const创建的引用类型为常量
    const obj={
     id:1,
     age:18,
     name:'aaa'
    }
    
    obj.age=19       // cosnt 创建一个对象,对象中的属性可以被改变
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    追问1:如何使对象中的属性不可改变
    解决:使用Object.freeze(),冻结对象,一个被冻结的对象再也不能被修改

    const obj2={
     id:2,
     name:'bbb',
     age:20,
     food:['banana','apple']
    }
    Object.freeze(obj2)
    
    obj2.age=21                 //被Object.freeze()冻结后,不可以改变    
    obj2.foods[1]='pear'        //可以改变  freeze只能冻结根层 嵌套引用类型需要嵌套递归
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    解决:实现创建引用类型
    思路:使用递归,将每一层的引用类型冰冻,实现创建引用类型的常量。

       function deepFreeze(obj) {
                Object.freeze(obj);
                (Object.keys(obj) || []).forEach((key) => {
                    let innerObj = obj[key]
                    if (typeof innerObj === 'object') {
                        deepFreeze(innerObj);
                    }
                }
                )
            }
    
    
            const tempObj = {
                id: 23,
                name: '1',
                food: ['banana', 'apple']
            }
            deepFreeze(tempObj)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    volatile 关键字有什么用?它的实现原理是什么?
    python | 巧用正则表达式
    ALU,半加器,全加器,减法电路
    Python 周期任务神器,太实用了
    序列化-序列化的嵌套
    单片非晶磁性测量系统典型磁参数的不确定度与重复性
    VS保存后Unity不刷新
    MySQL 23道经典面试吊打面试官
    Shiro-集成验证码
    Selenium基础 — POM设计模式(一)
  • 原文地址:https://blog.csdn.net/weixin_44247866/article/details/128061972