乐趣区

关于javascript:JavaScript高级程序设计深入理解总结变量提升

什么是变量晋升?

定义:变量晋升是 当栈内存作用域造成时 JS 代码执行前,浏览器会将带有var, function 关键字 的变量提前进行申明 declare(值默认就是 undefined),定义 defined(就是赋值操作),这种事后解决的机制就叫做 变量晋升机制 也叫 预约义

1. 带 Var 申明与不带 Var 申明的变量晋升
区别:var 操作符定义的变量会成为蕴含他的函数的局部变量。不过在函数外部定义变量时省略 var 操作符能够定义一个全局变量。

function foo() {
     a = 0;
     var b = 0;
}
  foo()
  console.log(a);//0
  console.log(b);//Uncaught ReferenceError: b is not defined

var a = b =12   // 这里的 b 也是不带 var 的。/* 相当于
var a = 12;
b = 12
*/

var 申明相干 变量晋升练习题 –1:

console.log(a, b)
var a =12, b ='小白'
function foo(){console.log(a, b)
    var a = b =13
    console.log(a, b)
}
foo()
console.log(a, b)

/* 输入:undefined undefined
    undefined "小白"
    13 13
    12 13
*/

解释 –1:
1)a 和 b 相当于定义了两个 window 变量,变量在代码执行前定义但未赋值,所以此时为 undefined undefined;
2)当代码执行到函数时,第一个 console.log(a, b)时,函数中的局部变量 a 的定义被晋升然而 b 未声明,因而 作用域链以后流动对象(函数上下文)找不到 b 的申明,从而寻找下一个 蕴含上下文 流动对象 ,找到 b =’ 小白 ’,所以此时打印 undefined “ 小白 ”;
3)当执行了 var a = b =13,别离为函数局部变量 a 和全局变量 b 赋值后,此时打印的为 13 13;
4)因为函数赋值的变量 a 为局部变量,b 为全局变量,所以此时打印的 a,b 都是全局变量,因而 a 的值不变,b 的值赋值为 13,因而打印 12 13;
var 申明相干 变量晋升练习题 –2:

  console.log(a, b)
  var a =12, b = '小白'
  function foo(){console.log(a, b)
  }
  foo()
  console.log(a, b)

  /* 输入:undefined undefined
      12 "小白"
      12 "小白"
  */

解释 –2:
1)var 变量申明晋升到顶端,但未赋值,因而打印为 undefined undefined
2)拜访全局变量
3)拜访全局变量
var 申明相干 变量晋升练习题 –3:

  a = 2

  function foo() {
    var a = 12;
    b = '小白'
    console.log('b' in window)
    console.log(a, b)
  }

  foo()
  console.log(b)
  console.log(a)
  /* 输入:true
  12 '小白'
  小白
  2
  */

解释 –3:
1)b 未用 var 操作符申明,因而默认定义未 window 变量,因而打印 true
2)函数执行过程中以后流动对象会被压入作用域链最顶端,从作用域链中以后流动对象中寻找变量,如果找不到,才回去寻找下一个蕴含上下文的流动对象,因而此时打印的 a 为 12;
3)在函数内部打印变量 a,b 那么以后的上下文的流动对象便是 window 对象, 因而打印的时全局变量 a,b;
var 申明相干 变量晋升练习题 –4:

  fn();
  console.log(v1);
  console.log(v2);
  console.log(v3);

  function fn() {
    var v1 = v2 = v3 = 2019;
    console.log(v1);
    console.log(v2);
    console.log(v3);
  }

  /* 输入
      2019
      2019
      2019
      Uncaught ReferenceError: v1 is not defined
  */

解释 –4:
1)函数申明也会晋升,因而 2019、2019、2019 都是函数中打印的局部变量,当函数执行结束后,window 流动对象中并未定义 v1、v2、v3 因而此时报错。
2. 等号右边下的变量晋升

= 意味着赋值,因而右边应该为变量,左边都应该是值,因而只对等号右边的进行变量晋升

等号右边下的变量晋升相干 变量晋升练习题 –1:

  print()
  function print(){console.log('小白')
  }
  print()
  /* 输入:小白
  小白
  */

解释 –1:
1)因为 function 关键字也会变量晋升,因而不难理解相当于调用了两次 print()办法,因而打印两次小白;
等号右边下的变量晋升相干 变量晋升练习题 –2:

  console.log(printf)//undefined
  printf()//Error
  var printf = function () {console.log('林一一')
  }
  printf()// 未执行
  /* 输入
      undefined
      Uncaught TypeError: print is not a function
  */

解释 –2:
1)同样因为变量晋升机制带 var 的 print 是一开始值是 undefined
2)因为 1),并且此时并未给 printf 赋值成函数,所以 print() 这时还不是一个函数,所以报出 类型谬误 TypeError
等号右边下的变量晋升相干 变量晋升练习题 –3:

  var fn = function sum() {console.log(sum);//=> 函数自身
    console.log(1);//=>1
  }
  // sum();//=>Uncaught ReferenceError: sum is not defined
  fn();
  /* 输入:ƒ sum() {console.log(sum);//=> 函数自身
    console.log(1);//=>1
  }
  
  1
   */

解释 –3:
1)sum()是匿名函数具体化后的名字,只能够在函数上下文中应用,因而蕴含上下文中拜访报 Error;
2)fn 定义晋升并初始化为 undefined 后,执行到 sum()为 fn 从新赋值,在调用 sum 能够失常输入。
2. 条件判断下的变量晋升
以后作用域 不论条件是否成立 都要 进行变量晋升 ,function 在老版本浏览器 (IE10 以下) 的渲染机制下,申明和定义都解决 ,然而为了投合 es6 中的块作用域,新版本浏览器对于 在条件判断中的 function 不论条件是否成立,都只是先申明,没有定义 相似于 var,同时判断体中如果呈现了 let、const、function 会创立一个新的执行上下文即块级上下文。

条件判断下的变量晋升相干 变量晋升练习题(变量和函数晋升的区别)–1:

  console.log(a); // undefined
  console.log(b); // undefined
  if (true) {console.log(a); // undefined
    var a = 1;
    console.log(b); // ƒ b() {console.log("1");}
    function b() {console.log("1");
    }
  }
  console.log(a); // 1
  console.log(b()); // 1,undefined
  /* 输入:undefined
  undefined
  
  ƒ b() {console.log("1");
    }
    
  1
  1
  undefined// 因为函数 b()没有返回值,因而 console.log(b()),b()执行时打印 1,执行结束打印 undefined
  */

解释 –1:
1)在以后作用域下不论条件是否成立都要进行变量晋升,为了投合 es6 中的块作用域,新版本浏览器对于在条件判断中的 function 不论条件是否成立,都只是先申明,没有值。因而,a、b 打印的都是 undifined。
2)在执行到 if(){}语句块中,变量执行到赋值语句才赋值,而函数间接赋值
3)if 执行完结,赋值操作完结,因而能够打印 a,b();
条件判断下的变量晋升相干 变量晋升练习题(变量晋升)–2:

  console.log(a)
  if(true){console.log(a)
    var a = '小白'
  }
  console.log(a)
/* 输入
    undefined
    undefined
    小白
/

解释 –2:
1)无论条件是否成立,都会进行变量晋升,因而打印 undefined;
2)赋值后从新打印 a;
条件判断下的变量晋升相干 变量晋升练习题 –3:

  // console.log(f)//Uncaught ReferenceError: f is not defined
  if(function f(){}){console.log(typeof f)//undefined
  }

解释 –3:
1)if 中 () 内的表达式不会变量晋升;
2)判断的条件没有晋升,所以条件外部的 f 是未定义

认真比照如下代码:

  console.log(globalPrint)//ƒ globalPrint(){}
  console.log(fatherPrint)//undefined
  console.log(childPrint)//undefined
  function globalPrint(){}
  if(true) {console.log(globalPrint)//ƒ globalPrint(){}
    console.log(fatherPrint)//ƒ fatherPrint() {}
    console.log(childPrint)//undefined
    function fatherPrint() {}
    if(false){console.log(globalPrint)//
      console.log(fatherPrint)//
      console.log(childPrint)//
      function childPrint(){console.log(1)//
        return "childPrint()的返回值";}
    }
  }
  console.log(childPrint)//undefined
  console.log(globalPrint)//ƒ globalPrint(){}
  console.log(fatherPrint)//undefined
  console.log(childPrint)//undefined
  function globalPrint(){}
  if(true) {console.log(globalPrint)//ƒ globalPrint(){}
    console.log(fatherPrint)//ƒ fatherPrint() {}
    console.log(childPrint)//undefined
    function fatherPrint() {}
    if(true){console.log(globalPrint)//ƒ globalPrint(){}
      console.log(fatherPrint)//ƒ fatherPrint() {}
      console.log(childPrint)//ƒ childPrint(){console.log(1)return "childPrint()的返回值";}
      function childPrint(){console.log(1)//1
        return "childPrint()的返回值";}
    }
  }
  console.log(childPrint())//childPrint()的返回值

总结:

  • if()判断语句中申明的办法无论在全局上下文或函数的执行上下文中,函数都无奈晋升。
  • if{}语句块中,var 变量和函数都会作用域晋升,然而函数赋值是在进入语句块之后,而变量赋值是在执行赋值语句后。
  • 函数变量会晋升,然而只有代码执行到函数申明的语句块时才会立刻赋值,因而父级上下文拜访子上下文中的函数时为 undefined(缺省值:申明了但未赋值)。

(未完 …)
参考资料:

  • 彻底解决 JS 变量晋升 | 一题一图,超具体包教包会😉
  • JS 中的变量晋升

了解尚浅,望不吝赐教!

退出移动版