变量提升: 在当前上下文中(全局/私有/块级),JS代码自上而下执行之前,浏览器会提前处理一些事情(可以理解为词法解析的一个环节,词法解析一定发上在代码执行之前)

    • 会把当前上下文中所有带VAR/FUNCTION关键字进行提前的声明或定义
    • 带VAR的只会提前声明,带FUNCTION会提前的声明定义
    • 在全局上下文中,我们声明过的变量VAR或者函数FUNCTION,也会给GO即window增加一个对应的属性,产生这样的一个映射机制,一个改另外一个也会跟着改
    • 特殊情况,带条件判断的,不管条件是否成立,都要进行变量提升。新版本要向后兼容ES6,新版ES6函数有个块级作用域,对于FUNCTION只会提前声明,不会提前定义。老版本IE10及以下,对于FUNCTION不仅提前声明,还要提前赋值。

    var a = 10;
    -> 声明declare: var a;
    -> 定义defined: a = 10;

    1. /** 代码执行之前:全局上下文中的变量提升
    2. * var a ; 默认值是undefined
    3. **/
    4. console.log(a); // undefined
    5. var a = 12; // -> 创建值12,不需要声明a(变量提升阶段完成,完成的事情不会重新处理) a=12赋值
    6. a = 13; // 全局变量 a=13
    7. console.log(a); // 13
    8. /**
    9. * 全局上下文中的变量提升
    10. * func = 函数 函数在这个阶段定义func和赋值都做了
    11. **/
    12. func(); // 函数可以执行
    13. function func() {
    14. // 全局上下文中的变量提升和这个a有关系么?没有关系。变量提升只会发生在当前上下文中
    15. // 这个a 是函数里的,而且函数未执行前只是一堆字符串
    16. var a= 12;
    17. console.log('ok')
    18. }
    19. /**
    20. * 实际开发中建议函数表达式创建函数,因为这样在变量提升阶段只会声明FUNC,不会赋值
    21. **/
    22. // func(); //如果在函数表达式前调用func(),就会报错 Uncaught TypeError: func is not function
    23. var func = function() {
    24. console.log('ok')
    25. func() ;
    26. var func = function AAA() {
    27. //把原本作为值的函数表达式匿名函数”具名化“
    28. //虽说是起了名字,但是这个名字不能在外面访问 =》也就是不会再当前上下文创建这个名字
    29. //当函数执行,在形成的私有上下文中,会把这个具名化的名字作为私有上下文中的变量(值就是这个函数)来处理
    30. console.log('OK')
    31. AAA(); // 递归处理 而不用严格模式下不支持的arguments.callee
    32. func(); // 递归调用这个也可以
    33. } ;
    34. // AAA(); // Uncaught ReferenceError:AAA is not defined
    35. func();
    36. setTimeout(function(){},1000);
    37. setTimeout(function func(){
    38. func();
    39. },1000);
    1. /**
    2. * EC(G)变量提升
    3. **/
    4. console.log(a);// Uncaught ReferenceError:a is not defined
    5. a=13;
    6. console.log(a);
    7. /**
    8. * EC(G)变量提升
    9. * 只有VAR/FUNCTION会变量提升(ES6中的LET和CONST不会)
    10. **/
    11. console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization 不能LET声明之前使用变量
    12. let a = 12;
    13. a = 13;
    14. console.log(a);
    15. /**
    16. * 基于VAR或FUNCTION在"全局上下文"中声明的变量(全局变量)会映射GO(全局对象window)上一份,作为window的属性;
    17. * 而且一个修改,另外一个也会跟着修改
    18. **/
    19. var a = 12;
    20. console.log(a); // ->12 全局变量
    21. console.log(window.a);; // -> 12 映射到GO上的属性a
    22. window.a = 13;
    23. console.log(a); // -> 13 映射机制一个修改另外一个也会被修改
    1. /**
    2. * EC(G) 全局上下文的变量提升
    3. * 无论条件是否成立,都要进行变量提升(条件中带FUNCTION在新版本浏览器中只会提前声明,不会提前赋值)
    4. * 【老版本】
    5. * var a;
    6. * func = 函数;
    7. * 【新版本】
    8. * var a; 全局上下文中声明a 相当于window.a
    9. * func; 全局上下文中声明func,相当于window.func
    10. **/
    11. // 新版浏览器里面答案是 undefined undefined
    12. // 老版本答案是:undefined func(){}
    13. console.log(a, func);
    14. if(!("a" in window)) {
    15. // "a" in window 检测a是否为window的一个属性 true
    16. // !("a" in window) false
    17. var a = 1;
    18. }
    19. console.log(a) // undefined
    1. /**
    2. * EC(G) 变量提升
    3. * 1) function fn() { console.log(1); } =》 声明加赋值 fn -> 1
    4. * 2) function fn() { console.log(2); } =》 前面声明过了,只需要赋值 2
    5. * 3)var fn = function () { console.log(3); } => var fn; 已经什么过了,不处理
    6. * 4)function fn() { console.log(4); } =》 前面声明过了,只需要赋值 4
    7. * 5)function fn() { console.log(5); } =》 前面声明过了,只需要赋值 5
    8. * 变量提升的结果是:全局上下文中有一个全局变量fn,值是输出5的函数(此时window.fn -> 5)
    9. **/
    10. fn(); // 5
    11. function fn() { console.log(1); } // 不再处理,变量提升处理过
    12. fn(); // 5
    13. function fn() { console.log(2); } // 不再处理,变量提升处理过
    14. fn(); // 5
    15. var fn = function () { console.log(3); } // var fn;声明不用管,但是赋值在变量提升阶段没处理过,此处需要处理 fn = window.fn -> 3
    16. fn(); // 3
    17. function fn() { console.log(4); } // 不再处理,变量提升处理过
    18. fn(); // 3
    19. function fn() { console.log(5); } // 不再处理,变量提升处理过
    20. fn(); // 3
    21. 答案是: 5 5 5 3 3 3

    image.png

    1. var foo = 1;
    2. function bar() {
    3. if(!foo) {
    4. var foo = 10;
    5. }
    6. console.log(foo);
    7. }
    8. bar(); // 10

    image.png