前言

变量是前端日常中应用的最频繁的语法。在开发过程,尽管晓得怎么用,然而却不晓得为什么,比方变量晋升。

变量晋升和函数晋升在前端面试中常常会考查到,上面介绍它们是如何造成的以及有什么区别。

前置常识

在理解变量晋升前,须要先理解一下js的执行过程,js执行分为两个过程:

  1. 词法分析阶段:词法剖析次要包含:剖析形参、剖析变量申明、剖析函数申明三个局部。通过词法剖析将咱们写的js代码转成能够执行的代码,接下来才是执行。
  2. 执行阶段

一般来说变量和函数会在应用之前就会申明,然而也能够在应用之后再进行申明,这是因为函数申明和变量申明总是会被解释器悄悄地被"晋升"到办法体的最顶部。被晋升的操作就处于第一步的词法分析阶段。

变量晋升(Hoisting)

  • 只有申明被晋升,初始化不会被晋升
  • 申明会被晋升到以后作用域的顶端

例子1

console.log(num); // undefinedvar num;num = 6;

预编译后

var num;console.log(num); // undefinednum = 6; // 初始化不会被晋升

例子2

num = 6;console.log(num); // 6 =》 晋升了 var sumvar num;

预编译后

var num;num = 6;console.log(num); // 6

例子3

console.log(num); // undefinedvar num = 6;

预编译后

var num;console.log(num);num = 6;

例子4

function hoistVariable() {  if (!foo) {    var foo = 5;  }  console.log(foo); // 5}hoistVariable();

预编译后

function hoistVariable() {  var foo     // 将if语句内的申明晋升  if (!foo) { // !undefined = true    foo = 5;  }  console.log(foo); // 5}hoistVariable();

例子5

var foo = 3;function hoistVariable() {  var foo = foo || 5;  console.log(foo); // 5}hoistVariable();

预编译后

var foo = 3;function hoistVariable() {  var foo  foo = foo || 5; // 此时 等号右侧 foo 为 undefined  console.log(foo); // 5}hoistVariable();

函数晋升

  • 函数申明和初始化都会被晋升
  • 函数表达式不会被晋升

例子1:函数申明可被晋升

console.log(square(5)); // 25function square(n) {  return n * n;}

预编译后

function square = (n) {  return n * n;}console.log(square(5)); // 25

例子2:函数表达式不可被晋升

console.log(square); // undefinedconsole.log(square(5)); // square is not a function =》 初始化并未晋升,此时 square 值为 undefinedvar square = function (n) {   return n * n; }

预编译后

var squareconsole.log(square); // undefined =》赋值没有被晋升console.log(square(5)); // square is not a function =》 square 值为 undefined 故报错square = function (n) {   return n * n; }

例子3

function hoistFunction() {  foo(); // 2  var foo = function() {    console.log(1);  };  foo(); // 1  function foo() {    console.log(2);  }  foo(); // 1}hoistFunction();

预编译后

function hoistFunction() {  var foo  foo = function foo() {    console.log(2);  }  foo(); // 2  foo = function() {      console.log(1);  };  foo(); // 1  foo(); // 1}hoistFunction();

优先级

  • 函数晋升在变量晋升之前
  1. 变量的问题,莫过于申明和赋值两个步骤,而这两个步骤是离开的。
  2. 函数申明被晋升时,申明和赋值两个步骤都会被晋升,而一般变量却只能晋升申明步骤,而不能晋升赋值步骤。
  3. 变量被晋升过后,先对晋升上来的所有对象对立执行一遍申明步骤,而后再对变量执行一次赋值步骤。而执行赋值步骤时,会优先执行函数变量的赋值步骤,再执行一般变量的赋值步骤
先解析,再按程序执行

例子1

typeof a; // functionfunction a () {}var a;// typeof a; // function  =》无论放在后面还是前面,解析后执行程序都是一样

预编译后

function a  // => 申明一个function avar a       // =》 申明一个变量 aa = () {}   // => function a 初始化typeof a;   // function

例子2

function b(){};var b = 11;typeof b; // number

预编译后

function b;  // => 申明一个function bvar b;       // =》 申明一个变量 bb = (){};    // =》 function b 初始化b = 11;      // =》 变量 b 初始化 =》变量初始化没有被晋升,还在原位typeof b;    // number

例子3:联合自执行函数

var foo = 'hello';(function(foo){  console.log(foo);  var foo = foo || 'world';  console.log(foo);})(foo);console.log(foo);// 顺次输入 hello hello hello

预编译后

var foo = 'hello';(function (foo) {    var foo;  // undefined;    foo= 'hello'; //传入的foo的值    console.log(foo); // hello    foo = foo || 'world';// 因为foo有值所以没有赋值world    console.log(foo); //hello})(foo);console.log(foo);// hello,打印的是var foo = 'hello' 的值(变量作用域)