JS 变量提升简单了解, window 属性 和 var 定义的全局变量 有 映射关系

1. 变量提升以及私有作用域
console.log(a, b);  //1: undefined, undefined, 变量提升(var 只声明), var a; var b; fn => 普通函数,声明 + 定义
  var a = 12, b = 12;
  function fn1 () {
    /*fn1() 私有作用域下
     * => 带var就是私有变量;
     * => 不带不是私有变量: 向上级作用域查找,找到就停止,否则 一直找到 window 为止(这种查找机制叫做 "作用域链")
     * 注意: 也就是说, 在私有作用域中操作的非私有变量, 是一直操作的别人的变量.
    */
    console.log(a, b) // 3. 变量提升, var a; a = undefined; b = 12;
    var a = b = 13;   // 等价于 var a = 13; b = 13;
    console.log(a, b) // 4: a = 13; b = 13;
  }
  fn1();               // 2: 跳过函数创建的代码, 直接执行
  console.log(a, b)   // 5: a = 12; b = 13;  这里的 a 是全局的变量, 和 函数内私有变量 a 不冲突;
2. 作用域链的拓展
function fn2 () {
    // console.log(b2) // Uncaught ReferenceError: b2 is not defined
    b2 = 13;
    console.log('fn 内 b2: ', b2, 'b2' in window);
    // 'b2' in window: true => 在作用域链查找的过程中, 如果找到 window 也没有这个变量, 相当于给 window 设置了一个属性 b(window.b = 13)
  }
  fn2();
  console.log('fn 外 b2: ', b2);
  console.groupEnd();
3. 只对等号左边变量提升
console.group("3. 只对等号左边变量提升");
// fn3();      // Uncaught TypeError: fn3 is not a function
  var fn3 = function () {
    console.log('fn3')
  }
  fn3();      // fn3
4. 条件判断下的变量提升
/*当前作用域下, 不管条件是否成立, 都要进行变量提升
   *  => 带 var 的还是只 声明
   *  => 带 function 的在老版本浏览器渲染机制下, 声明 + 定义 都处理;
   *   新版本浏览器为了迎合ES6的块级作用域, 对于函数(条件判断中的函数), 不管条件是否成立, 都只 声明, 没有定义, 类似于 var;
  */
  console.log(a4);     // undefined
  if ('a4' in window) {
    var a4 = 100
  }
  console.log(a4);     // 100

  // 匿名函数, 变量 没有变量提升
  f4 = function () { return true };   // window.f4 = func...
  g4 = function () { return false };  // window.g4 = func...
  ~function () {
    /* 变量提升 func g4, g4 = undefined, 是私有变量 =>
     * 老版本浏览器可以通过;
     * 新版本浏览器报错: g4() => Uncaught TypeError: g4 is not a function
    */
    if (g4 === undefined && [] == ![]) {
    // if (g4() && [] == ![]) {
      f4 = function () { return false };  // 把全局下的 f4 修改
      function g4 () { return true };     // 私有变量, 不影响外部的 g4
    }
  } ()
  console.log(f4());
  console.log(g4());

[] == ![] // 结果: true, (null, undefined, NaN, ‘’, 取反为 true)

5. ES6中基于 let/const 定义的变量或者函数
/* 1. 不存在变量提升机制;
   * 2. 切断了 全局变量与 window 属性的映射关系;
   * 3. let 不允许声明相同名字的变量;
   * 注意: 当前作用域代码自上而下执行之前, 浏览器会做一个重复性检测(语法检测), 自上而下查找当前作用域下所有变量, 
   * 一旦发现重复的,直接抛出异常,代码也不会执行(虽然没有提前声明, 但是浏览器已经记录了所有的变量).
  */
  let a5 = 10, b5 = 10;
  let fn5 = function () {
    // console.log(a5, b5);  // can't access lexical declaration 'a5' before initialization
    let a5 = b5 = 12;
    console.log(a5, b5)
  }
  fn5();
  console.log(a5, b5)