(大厂面试题)进入JS 的世界,理解“提升”机制

2,605 阅读5分钟

前言

现实世界中,我们对大部分事物都是处于一种知其然不知其所以然的一种生活状态。这是我们世界的常态,我们不需要去了解他们是怎么完成这个功能的,我只需要去了解他们可以完成什么样的功能。但是如果想要深入了解他的本质,我们就必须要达到知其所以然程度。现在就跟着我的节奏进入JS的世界,一层一层的拨开迷雾,了解她的内心。

代码的“提升”机制

变量提升

我们先看看下面的一段代码

//例1
var a;
a = 1;
console.log(a);//输出 1

在JS中代码的运行就是至上而下的,所以输出的结果就是1

//例2
a = 1;
var a;
console.log(a);//输出 1

为什么这个输出的结果是1了,不是应该是undefined

//例3
console.log(b);
var b = 2;  //输出 undefined

我们现在深度解析一下这几个例子。第一个例子就不用多说了,声明一个变量并赋值,然后输出结果,这是非常简单的。现在看第二和第三个例子。他里面就包括了我们今天要讲的内容,变量提升。

  • 变量提升,通俗来讲就是将变量的声明(只提升变量的声明,赋值不提升)提升到当前作用域的最顶端,并将undefined赋值给做个变量 说道这里,你肯定就疑惑什么叫做作用域了,这里我就做个简单的介绍。
  • 作用域通俗来讲就是限定做个名字的可用性的代码范围就是他的作用域。他分为全局作用域、局部作用域和块级作用域这三种。我今天说讲的都是在全局作用域下进行的。 所以上面的例2和例3可以看成是一下代码
//例2
var a;
a = 1;
console.log(a);//输出1
//例3
var b;
sonsole.log(b);//输出undefined
b = 2;

所以你现在来试试下面的一个题目吧

console.log(a);
var a = 1;
var b  = a
console.log(b);

结果为多少,你知道了吗。对的结果就是undefined 1 ,所以你现在熟悉了变量的提升吧

  • 结论:在JS中代码的运行过程中,一共经历了两个阶段:
  1. 预编译阶段
  2. 执行阶段,
  • 代码的预编译阶段主要是在内存中开辟一些空间以此来存放变量、函数等。预编译时,JS会搜集所有的变量声明并且变量声明提升,而其他的语句都保持顺序不变,并且,变量声明提升后,会给变量设置默认值undefined
  • 代码的执行阶段就是按照代码的顺序从上到下一步一步执行的

函数提升

上面我们了解了变量的提升,可是我们JS中不是只有变量呀,还有函数,所以我们现在来看一下下面的一段代码

//例4
a();
function a() {
    console.log('a');
}   //输出a

为什么输出的是a了,我们之前讲了变量提升,难道函数也有提升吗?没错,函数也有提升,函数的提升和变量的提升是差不多的,都是提升到当前作用域的最顶端,但是函数还会把他的函数体也提升到最顶端。并为函数赋值函数体。所以例4的代码相当于下面的代码

function a() {
    console.log('a');
}   
a();  //输出a

OK,现在来继续看下一题

var a;
function a() {
    console.log('a');
}
a();

不仔细思考,你会不会觉得这结果肯定是undefined啊!但是它还是输出了a。为什么了?究其原因,就是我们不了解变量声明和函数声明他们之间的某种关系。当有多个同名变量声明时,函数声明高于一切,会覆盖其他声明,因为函数是JS的“第一公民”。所以输出结果为a

  • 我们结合变量提升和函数提升解析这一道题
// 例4
showName()
var showName =function(){
    console.log(2);
}
function showName(){
    console.log(1);
}
showName()  

结果是

1
2

我们现在来分析一下先把他的声明提升到当前作用域的最顶端

// 变量和函数提升部分
var = showName = undefined  //变量声明 并为 showName 赋值 undefined
//函数声明  把变量声明覆盖了
function showName(){  //为函数赋值函数体
    console.log(1);
}

然后再看执行部分

//执行部分
showName()
showName =function(){   //变量被赋值
    console.log(2);
}
showName()

执行部分的理解:代码执行部分是从上到下的顺序执行的。先调用第一个showName()函数,输出结果为1。然后在执行第二段代码,将函数function()赋值给showName,然后执行第三段代码,调用函数showName(),输出结果为2

总结

从上面这些例子分析啊,我们可以总结3个小tips:

  1. JS中的代码执行共经历了两个阶段,预编译阶段和执行阶段。
  2. 所谓的变量提升(函数提升),是指在JS代码执行中, JS引擎(V8)把变量的声明部分和函数的声明部分提升到当前作用域开头的行为,变量提升后,会给变量设置默认值undefined,给函数赋值函数体。
  3. 当有多个同名变量声明时,函数声明会覆盖其他的声明。如果有多个同名函数声明,则是由最后的一个函数声明覆盖之前所有的声明。