• let与const


    let与const

    ES2015(ES6)新增加了两个重要的JavaScript关键字: letconst

    块级作用域

    代码块内如果存在let或者const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。

    {
        let a = 1;
        var b = 2;
        function s(){return a;}
        console.dir(s);
        /*
          ...
          [[Scopes]]: Scopes[2]
            0: Block {a: 1}
            1: Global ...
        */
    }
    // 此处不能使用 a ,a 是块级作用域
    // 此处可以使用 b , b 在此处是全局作用域
    

    [[Scopes]]是保存函数作用域链的对象,是函数的内部属性无法直接访问,[[Scopes]]中可以看到出现了一个Block块级作用域,这使得let特别适合在for中使用,在ECMAScript 2015引入let关键字之前,只有函数作用域和全局作用域,函数作用域中又可以继续嵌套函数作用域,在for并未具备局部作用域,于是有一个常见的闭包创建问题。

    function counter(){
        var arr = [];
        for(var i = 0 ; i < 3 ; ++i){
            arr[i] = function(){
                return i;
            }
        }
        return arr;
    }
    
    var coun = counter();
    for(var i = 0 ; i < 3 ; ++i){
        console.log(coun[i]()); // 3 3 3
    }
    

    可以看到运行输出是3 3 3,而并不是期望的0 1 2,原因是这三个闭包在循环中被创建的时候,共享了同一个词法作用域,这个作用域由于存在一个ivar声明,由于变量提升,具有函数作用域,当执行闭包函数的时候,由于循环早已执行完毕,i已经被赋值为3,所以打印为3 3 3,可以使用let关键字声明i来创建块级作用域解决这个问题

    function counter(){
        var arr = [];
        for(let i = 0 ; i < 3 ; ++i){
            arr[i] = function(){
                return i;
            }
        }
        return arr;
    }
    
    var coun = counter();
    for(var i = 0 ; i < 3 ; ++i){
        console.log(coun[i]()); // 0 1 2
    }
    

    当然也可以使用匿名函数新建函数作用域来解决

    function counter(){
        var arr = [];
        for(var i = 0 ; i < 3 ; ++i){
            (function(i){
                arr[i] = function(){
                    return i;
                }
            })(i);
        }
        return arr;
    }
    
    var coun = counter();
    for(var i = 0 ; i < 3 ; ++i){
        console.log(coun[i]()); // 0 1 2
    }
    

    一次声明

    同一作用域内letconst只能声明一次,var可以声明多次

    let a = 1;
    let a = 1; //Uncaught SyntaxError: Identifier 'a' has already been declared
    
    const b = 1;
    const b = 1; //Uncaught SyntaxError: Identifier 'b' has already been declared
    

    暂时性死区

    当使用letconst生成块级作用域时,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域,代码块内,在声明变量之前使用它会报错,称为暂时性死区。

    {
        console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
        let a =1;
    }
    

    变量提升

    letconst也存在变量提升,在ES6的文档中出现了var/let hoisting字样,也就是说官方文档说明letvar一样,都存在变量提升,但是与var的变量提升有所不同

    let 的「创建」过程被提升了,但是初始化没有提升。  
    var 的「创建」和「初始化」都被提升了。  
    function 的「创建」「初始化」和「赋值」都被提升了。
    

    stackoverflow中比较有说服力的例子

    x = "global";
    // function scope:
    (function() {
        x; // not "global"
    
        var/let/… x;
    }());
    // block scope (not for `var`s):
    {
        x; // not "global"
    
        let/const/… x;
    }
    

    js中无论哪种形式声明var,let,const,function,function*,class都会存在提升现象,不同的是,var,function,function*的声明会在提升时进行初始化赋值为 undefined,因此访问这些变量的时候,不会报ReferenceError异常,而使用let,const,class声明的变量,被提升后不会被初始化,这些变量所处的状态被称为temporal dead zone,此时如果访问这些变量会抛出ReferenceError异常,看上去就像没被提升一样。

    https://blog.csdn.net/jolab/article/details/82466362
    https://www.jianshu.com/p/0f49c88cf169
    https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6
    

    window

    在全局作用域中使用var直接声明变量或方法等会挂载到window对象上,letconst声明变量或方法等会保存在Script作用域中

    var a = 1;
    let b = 2;
    const c = 3;
    
    console.log(window.a); // 1
    console.log(window.b); // undefined
    console.log(window.c); // undefined
    
    let a = 1;
    {
        let b = 2;
         function s(){return a + b;}
         console.dir(s);
         /*
          ...
          [[Scopes]]: Scopes[3]
            0: Block {b: 2}
            1: Script {a: 1}
            2: Global ...
        */
    }
    

    初始化

    varlet在声明时可以不赋初值,const必须赋初值

    var a;
    let b;
    const c; //Uncaught SyntaxError: Missing initializer in const declaration
    

    只读常量

    const用以声明一个只读常量,初始化后值不可再修改

    const a = 1;
    a = 2; // Uncaught TypeError: Assignment to constant variable.
    

    const其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。对于简单类型numberstringbooleanSymbol,值就保存在变量指向的那个内存地址,因此const 声明的简单类型变量等同于常量。而复杂类型objectarrayfunction,变量指向的内存地址其实是保存了一个指向实际数据的指针,所以const只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了。

    const a = {};
    console.log(a); // {}
    a.s = function(){}
    console.log(a); // {s: ƒ}
    

    相关

    ES6新特性 https://github.com/WindrunnerMax/EveryDay/blob/master/JavaScript/ES6%E6%96%B0%E7%89%B9%E6%80%A7.md
    Js变量提升 https://github.com/WindrunnerMax/EveryDay/blob/master/JavaScript/JS%E5%8F%98%E9%87%8F%E6%8F%90%E5%8D%87.md
    
  • 相关阅读:
    MYSQL: MYSQLBINLOG命令查看日志文件
    JAVA MAIL 发送邮件(SSL加密方式,TSL加密方式)
    Spring和Email整合详解
    java 版百度网盘功能
    Spring @Conditional注解 详细讲解及示例
    spring注解之@Import注解的三种使用方式(转载)
    Redis protected-mode属性解读
    5
    4
    3
  • 原文地址:https://www.cnblogs.com/WindrunnerMax/p/12678688.html
Copyright © 2020-2023  润新知