声明变量的五种方案:
    var function 传统方案
    let const import ES6的新方案
    var VS const:
    let 和const声明的都是变量,const 声明的变量是不允许指针重新指向,但是存储的值是可以改变的,比如存储的值是引用数据类型,可以基于地址改变堆内存中的信息。
    等号= 赋值本身就是指针关联或者指针指向的过程
    let n = 12, 先创建12存在栈里面,再创建n,然后n和12关联,n指向12

    1. let n = 12;
    2. n = 13;
    3. console.log(n); // 13
    1. const m = 12;
    2. m = 13; // 这里就报错了,指针重新指向的时候Uncaught TypeError: Assignment to constant variable.
    3. console.log(m);
    1. const m = {
    2. name: 'qiuhan'
    3. };
    4. m.name = 'erina';
    5. console.log(m); // {name: "erina"}

    平时在项目中用const 的情况也比较多,比如想声明一个变量,这个变量指针不能再重新指向别的值,只能关联初始创建的值。项目中一般创建函数的时候用const,fn只能和这个函数关联,不能指向别的函数。
    const fn = function() {}
    var VS let:

    • 带var/function 存在变量提升,而let/const不存在变量提升机制
    • 在相同的上下文中,let不允许重复声明(而且检查是否重复声明发生在“词法解析阶段”)
      • 此法解析-> 变量提升 -> 代码执行
      • 也就是词法解析阶段检测到重复声明,则直接报错,JS代码一行(句)都不会执行
      • 且不论基于什么方式声明的变量,只要在当前上下文中有了,则不允许再基于let/const声明;
    • 暂时性死区(浏览器遗留BUG):基于typeof检测一个未被声明的变量,不会报错,结果是undefined
    • 块级私有上下文(作用域)
      • 除函数或者对象的大括号之外,如果括号中出现 let/const/function 则会产生块级私有上下文
      • 当前块级上下文也只是对let/const/function他们声明的变量有作用
        1. // 在全局代码执行之前,首先会变量提升:把当前上下文中所有带var/function关键字的提前声明或者定义
        2. //(带var只是提前声明,带function会提前的声明+定义)
        3. // var n;
        4. console.log(n); // undefined
        5. var n = 12;
        1. console.log(n); //VM42645:1 Uncaught ReferenceError: n is not defined
        2. let n = 12;
    1. // 此写法不建议写
    2. // 变量提升: fn = 0x000000;
    3. fn(); // 'ok'
    4. function fn() {
    5. console.log('ok')
    6. }
    7. // 建议函数表达式方式创建函数
    8. fn(); // Uncaught ReferenceError: Cannot access 'fn' before initialization
    9. const fn = function () {
    10. console.log('ok');
    11. };
    12. fn(); // 'ok'
    1. // 函数的变量提升在新版浏览器中很变态
    2. // 变量提升: fn = 0x000000; 声明+ 定义
    3. console.log(fn); // 函数本身: 只是打印变量fn,非执行fn() { console.log('ok')}
    4. function fn() {
    5. console.log('ok')
    6. }
    • 在相同的上下文中,let不允许重复声明(而且检查是否重复声明发生在“词法解析阶段”) ```javascript var n = 12; var n = 13; console.log(n); // 13

    =============================================================================== // 注意ok都没有打印出来,因为在相同的上下文中,let不允许重复声明(而且检查是否重复声明发生在“词法解析阶段”) console.log(‘ok’); let n = 12; let n = 13; // Uncaught SyntaxError: Identifier ‘n’ has already been declared console.log(n);

    =============================================================================== // 且不论基于什么方式声明的变量,只要在当前上下文中有了,则不允许再基于let/const声明; var n = 12; let n = 12; // Uncaught SyntaxError: Identifier ‘n’ has already been declared */

    1. 暂时性死区(浏览器遗留BUG):基于typeof检测一个未被声明的变量,不会报错,结果是undefined
    2. ```javascript
    3. console.log(a); // Uncaught ReferenceError: a is not defined
    4. console.log(typeof a); // undefined 暂时性死区
    5. ======================================================================
    6. //此法解析阶段报错,优先级高于typeof,避开了暂时性死区
    7. console.log(typeof a); // Uncaught ReferenceError: Cannot access 'a' before initialization
    8. let a = 12;

    let 可以解决暂时性死区问题

    • 块级私有上下文(作用域)
      • 除函数或者对象的大括号之外,如果括号中出现 let/const/function 则会产生块级私有上下文
      • 当前块级上下文也只是对let/const/function他们声明的变量有作用
        1. // 这里面没有块级上下文,只有全局上下文
        2. if(1==1) {
        3. var n = 12;
        4. console.log(n); //12
        5. }
        6. console.log(n); // 12
        7. =================================================================================
        8. // 大括号里有let,形成块级上下文
        9. if(1==1) {
        10. let n = 12;
        11. console.log(n); //12
        12. }
        13. console.log(n); // Uncaught ReferenceError: n id not defined
        14. ================================================================================
        15. if (1 == 1) {
        16. let n = 12;
        17. var m = 13; //依然是全局的,私有块级上下文对他无效
        18. console.log(n, m); // 12 13
        19. }
        20. console.log(m); //13
        21. console.log(n); //Uncaught ReferenceError: n is not defined */

    函数变量提升阶段:
    [老版本浏览器] 不论条件是否成立,都要fn 声明+ 定义,而且fn只是全局上下文中的变量
    [新版本浏览器] 为了兼容ES3/ES6,规则处理的非常的复杂

    • 全局变量提升:如果创建函数的代码出现在”非函数或者对象的大括号中(例如:判断替、循环体、代码块…)”,只会对它进行声明,不进行赋值 =》 function fn;
    • 条件成立,代码执行进入到大括号当中,如果大括号中出现了 function xxx(){} ,此时当前大括号会形成一个私有的上下文,私有上下文中的第一件事情也是变量提升,它会把函数进行声明+定义 ```javascript console.log(fn);// [新版本]:undefined [老版本]:函数本身fn(){console.log(‘ok’)} if(1 == 1) { // 私有上下文变量提升: fn = 0x000000; console.log(fn); // [新版本]:函数本身 fn(){console.log(‘ok’)} function fn() { // 代码执行到这的时候,会把私有上下文中,之前对于fn的操作,映射到全局一份 console.log(‘ok’); } fn = 12; console.log(fn); //[新版本]:12 } console.log(fn); // [新版本]:函数本身 fn(){console.log(‘ok’)}

    =================================================================================== console.log(fn); {// 和前面的if(1 == 1){} 判断的语句的大括号效果一样 console.log(fn); function fn() { console.log(‘ok’); } fn = 12; console.log(fn); }

    1. **[老版本]:**
    2. - 变量提升:找到所有带var/function的关键字的提前声明或者定义
    3. - 代码执行
    4. ```javascript
    5. console.log(fn); // 函数本身 fn(){console.log('ok')}
    6. if(1==1) {
    7. console.log(fn); // 函数本身 fn(){console.log('ok')}
    8. function fn() {
    9. console.log('ok');
    10. }
    11. fn = 12;
    12. console.log(fn); // 12
    13. }
    14. console.log(fn); // 12

    image.png
    [新版本]:

    • 变量提升:只是声明了fn->function fn “出现在大括号中”
    • 代码执行

    块级上下文中没有this、arguments

    1. console.log(fn); // undefined
    2. if(1==1) {
    3. // 私有“块级”上下文,fn做变量提升-> 声明+定义, 且“映射”给全局一份
    4. console.log(fn); // 函数本身 fn(){console.log('ok')}
    5. function fn() {
    6. console.log('ok');
    7. }
    8. fn = 12;
    9. console.log(fn); // 12
    10. }
    11. console.log(fn); // 函数本身 fn(){console.log('ok')}

    函数的超级变态机制.png

    debugger 断点调试F10/F11
    F11 step in
    F10 step over