let、const

let
  • let声明的变量不提升
 if(true){
        //tmp = 'abc';//ReferenceError
        //console.log(tmp);//ReferenceError

        let tmp;  //暂时死区(TDZ)结束,在let命令声明变量tmp之前,都属于变量tmp的死区
        console.log(tmp);//undefined

        tmp = 123;
        console.log(tmp);//123
    }

    //typeof x;  //ReferenceError   "暂时性死区"也意味着typeof不再是一个百分之百安全的操作,
               //上面代码中,变量x使用let命令声明,所以在声明之前,都属于x的"死区",只要用到该变量就会报错.因此
               //typeof运行时就会抛出一个ReferenceError
    //let x;


    typeof  undeclared_variable;  //undefined   在上面代码中,undeclared_variable是一个不存在的变量名,结果返回"undefined",
                                  //所以,在没有let之前,typeof运算符是百分百安全的,永远不会报错.现在这一点不成立了


    //var x = x;  //不报错
    let x = x;   //ReferenceError  : x is  not defined
                 //使用let变量声明时,只要变量在还没有声明完成前使用,就会报错.在变量x的声明语句还没有执行完成前,就去取x的值,
                 //导致报错"x未定义"




    //报错
    function (){
        let a = 10;
        var a = 1;
    }
    //报错
    function (){
        let a = 10;
        let a = 1;
    }//let不允许在相同的作用域内,重复声明同一个变量.


    function func(arg){
        let arg;  //报错
    }

    function func(arg){
        {
            let arg;  //不报错,新的作用域
        }
    }
块级作用域
var tmp = new Date();

    function f(){
        console.log(tmp);  //undefined
        if(false){
            var tmp = 'hello world';//  if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量,但是,函数f执行后,
                                    //输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量
        }
    }

    f();

    var s = 'hello';
     for(var i = 0 ;i<s.length;i++){
         console.log(s[i]);// h e l l o
     }
    console.log(i);//5  变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量


    function f1(){
        let n = 5;
        if(true){
            let n = 10;
        }
        console.log(n); //5
    }
    f1();



    {{{{
        let insane = 'hello world';
        {
            let  insane = 'hello world'
        }
    }}}}   //内层作用域可以定义外层作用域的同名变量
块级作用域与函数声明

ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明

   //es6中
//    function f(){
//        console.log('i am outside');
//    }
//
//    (function (){
//        if(false){
//            //重复声明一次函数f
//            function f(){
//                console.log('i am inside');
//            }
//        }
//        f();  //Uncaught TypeError: f is not a function
//    }());



    // 浏览器的 ES6 环境
    function f() { console.log('I am outside!'); }
    (function () {
        var f = undefined;
        if (false) {
            function f() { console.log('I am inside!'); }
        }

        f();
    }());
    // Uncaught TypeError: f is not a function



    {
        let a = 'secret';
        function f(){   //函数声明语句
            return a;
        }
    }


    {
        let a = 'secret';
        let f = function (){  //函数表达式
            return a;
        };
    }
    //考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数.如果确实需要,
    // 也应该写成函数表达式,而不是函数声明语句
const
 const PI = 3.1415;
   console.log(PI) //3.1415

    PI = 3;// TypeError: Assignment to constant variable.
    // 上面代码表明改变常量的值会报错.const声明的变量不得改变值,这意味着,const一旦表明变量,
    // 就必须初始化,不能留到以后赋值


    const foo;  // SyntaxError: Missing initializer in const declaration
                //const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值
                //对于const来说,只声明不赋值,就会报错

    if(true){
        const MAX = 5;
    }
    MAX  //Uncaught ReferenceError: MAX is not defined
         //const的作用域与let命令相同,只在声明所在的块级作用域内有效


    if(true){
        console.log(MAX); //ReferenceError
        const MAX = 5;
    }   //const命令声明的常量也是不提升,同样存在暂存性死区,只能在声明的位置后面使用


    var message = "hello!";
    let age = 25;

    //一下两行都会报错  const 声明的常量,也与let一样不可重复声明
    const message = "goodbye!";
    const age = 30;

本质

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

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

  • 上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。
const a = [];
a.push('Hello'); // 可执行
a.length = 0;    // 可执行
a = ['Dave'];    // 报错
//常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。
原文地址:https://www.cnblogs.com/hixxcom/p/7412355.html