初学JavaScript,浅谈变量提升

ES6之前我们一般使用var来声明变量,提升简单来说就是把我们所写的类似于var a = 123;这样的代码,声明提升到它所在作用域的顶端去执行,到我们代码所在的位置来赋值。

function showName () {
    console.log(myName);  //undefined
    var myName = 'xzp'; 
};
showName();
复制代码

上述代码的结果是undefined,我对上面代码的抽象理解如下:

function showName () {
    var myName;
    console.log(myName);
    myName = xzp;
}
showName();
复制代码

另外的一个例子

money = 1;
var money;
console.log(money); //1
复制代码

这里输出的并不是undifined,因为变量会提升到作用域最顶端

下面来看一道经典面试题:

console.log(v1);
var v1 = 100;
function foo() {
    console.log(v1);
    var v1 = 200;
    console.log(v1);
}
foo();
console.log(v1);
复制代码

输出的结果:

//undefined
//undefined
//200
//100
复制代码

2、函数提升

javascript中不仅仅是变量声明有提升的现象,函数的声明也是一样;具名函数的声明有两种方式:

1. 函数声明式    2. 函数字面量式
复制代码
//函数声明式
function bar () {}
//函数字面量式 
var foo = function () {}
复制代码

函数字面量式的声明与变量提升的结果是一样的,函数只是一个具体的值;
但是函数声明式的提升现象和变量提升现象又不尽相同

先来个例子

console.log(bar);
function bar () {
  console.log(1);
}
//打印结果:ƒ bar () {
//  console.log(1);
//}
复制代码

执行顺序相当于:

function bar () {
  console.log(1);
}
console.log(bar);
复制代码

函数提升是整个代码块提升到它所在的作用域的最开始执行
思考下面的代码

shoWName()   // 1
var shoWName = function () {
    console.log(2)
}
function shoWName() {
    console.log(1)
}
shoWName()   // 2
复制代码
  • 编译阶段:(由编译器执行)
  1. 变量和函数声明提升到全局执行上下文的最顶端:scope对象里面的数据:(小结会解释什么是scope对象)
{
    shoWName                        undifined   // var shoWName
    shoWName(){console.log(1)}      function (同时声明了一个块级作用域)
}
复制代码
  • 执行阶段:(由引擎执行,我是这么理解的)
  • 第一个shoWName()执行时:此时引擎会询问编译器是否见过shoWName()函数,由于此时第2行代码中的shoWName还没有值(undifined),所以编译器会返回5行代码所声明的函数,并输出 1 (函数优先)
  • 第二个shoWName()执行时scope变量发生改变{shoWName:function () {console.log(2)}},因为按照代码从上到下的执行顺序,会对变量showName赋值(第二个showName()出现在赋值代码后面),接收一个函数表达式,并且把之前声明的同名函数表达式覆盖

此时调用shoWName()会执行覆盖后的通过函数字面量声明的shoWName()函数执行console.log(2)这句代码,输出 2。

小结:

  1. 只有声明本身会被提升,而赋值操作不会被提升。
  2. 函数声明会被提升,但函数表达式不会被提升。
  3. scope 对象,存放当前代码执行上下文中的所有变量的引用
  4. 代码一定是逐行运行的
  5. 分为编译阶段和执行阶段
  6. 变量提升后,会给变量设置默认值undefined