2020面试题(1)

182 阅读6分钟

最近由于一些原因,从公司离职开始我自己的面试之旅了,下面就是我在最近的面试过程中遇到的一些经典类型的面试题了

JS

event loop

eventLoop是分为浏览器的事件循环和nodejs的事件循环,首先我们先来说一下浏览器的事件循环

事件循环的过程
  1. 首先,我们都知道的是,浏览器的单线程的
  2. 代码有同步代码和异步代码之分,同时异步代码分为两种,宏任务微任务,执行过程中,如果遇到衣不带吗,则将其放入到相应的队列(宏任务或者微任务队列)中
  3. 宏任务主要有setTimeout, setImmediate,setInterval,微任务有promise, mutationObserver, process.nextTick,遇到相应的任务则放到相应的队列中

注意: 两个队列都是先进先出的

  1. 执行过程 顺序执行代码,遇到同步代码则直接执行,遇到异步代码则放入相应的队列,当同步代码执行完毕后会去查看微任务队列中是否有任务存在,如果有则清空微任务队列,如果没有则会去查看宏任务队列中是否存在尚未执行的代码,如果有则执行,如果没有则结束

注意: 浏览器的事件循环是每执行一个宏任务,就会去清空微任务队列

下面我们就看最近我的一道面试题,废话不多说,先上代码啦!!!

console.log(1);
setTimeout(() => {
    console.log(2);
    Promise.resolve()
        .then(data => {
            console.log(3);
        })
})
new Promise(resolve => {
    resolve()
    console.log(4);
}).then(() => {
    console.log(5);
    setTimeout(() => {
        console.log(6);
    })
}).then(() => {
    console.log(7);
})
console.log(8);

本题主要考验的是事件循环的,我们公布一下答案吧

1 4 8 5 7 2 3 6 

不知道小伙伴们有没有做对呢,下面我们来解释一下,

第一轮
  1. 首先按照浏览器的执行过程,会去执行同步代码,首先会执行log(1),这个时候就会打印1
  2. 执行setTimeout1,setTimeout1是宏任务,所以将其回调函数丢到宏任务队列中,继续往下执行
  3. 执行promise,promise是同步代码,所以执行其回调,然后log(4),此时已经打印出1、4
  4. 因为promise已经resolve了,所以其两个then函数会相继放到微任务队列中,然后继续往下执行
  5. 然后是同步任务log(8), 此时已经打印出1、4、8,至此第一轮已经结束
第二轮
  1. 同步代码执行完毕之后,此时会去读取微任务队列,我们可以看到只有第四部我们将两个then放到微任务队列中了,所以我们相继执行这两个then函数,
  2. 执行then1,打印log(5),此时已打印1、4、8、5,后面是settimeout2,放入宏任务队列
  3. 然后执行第二个then的回调函数,打印7,此时已打印1、4、8、5、7,微任务队列清空,去执行宏任务队列
第三轮
  1. 此时宏任务队列中有setTimeout1,和setTimeout2,首先我们取出setTimeout1的回调去执行,先log(2),此时已打印,1、4、8、5、7、2,然后promise.resolve().then()是一个微任务,将then3其放入到微任务队列中, 至此宏任务执行完毕

注意: 浏览器的eventloop执行过程是每执行一个宏任务,就去查看微任务队列中是否有任务,有则清空所有的微任务,这个node11之前是不同的,nodejs环境下11之后的事件循环和浏览器相同

第四轮
  1. 准备清空微任务队列,微任务队列中此时有then3,我们执行then3,会log(3),此时已打印1、4、8、5、7、2、3,至此微任务队列清空
第五轮
  1. 我们继续来看宏任务队列中,还有setTimeout2未执行,此时我们取出并执行其回调函数,log(6),此时已打印1、4、8、5、7、2、3、6,然后去看微任务队列,并没有任务task,至此结束,答案就是1、4、8、5、7、2、3、6

变量和函数的声明提升

变量的声明提升

首先我们来说下变量的声明提升,什么是变量的声明提升呢,变量的声明提升就是在变量声明的时候,该变量会被提升到当前作用域的最顶层去声明(只是变量的声明,不是变量的赋值),所以在我们程序的执行过程中可能会出现以下这种情况

console.log(a); //undefined
var a;

上面的结果我们可以看到,打印的是undefined,并没有报错,此时var a;这条语句已经被提升到当前作用域最顶部了,所以不会报错

出现的时机

简单来说,只有使用var声明的变量才会存在变量的声明提升。

函数的声明提升

函数的声明是有多种类型的,比如说正常的函数声明,又或者函数表达式声明,或者立即执行函数,但是具有只有通过普通的函数声明才具备函数的声明提升

function a () {}; // 提升
let a = function () {} // 不会提升
优先级问题

**函数的声明提升的优先级比变量的声明提升的优先级要高,即使你先声明变量,在声明函数,此时在当前作用于最顶层的仍然是函数,然后才是变量,而且当函数名和变量名相同时,函数名会将变量名覆盖掉 **

function a() {}
var a 
console.log(a) // func a
面试题

下面我们来看一道面试题

console.log(fish1, fish2, fish3);
var fish1 = function() {
    console.log('fish1');
}
function fish2 () {
    console.log('fish2');
}

var fish3 = 'fish3'

var fish1, fish2, fish3
console.log(fish1, fish2, fish3);

这是我上次面试的时候碰到的一道面试题,还算比较经典,同时有变量的声明提升和函数的声明提升,以及函数表达式的处理方式,下面我们先整体来看代码

  1. 第一句log(); 先跳过
  2. 第二句是一个函数的表达式,左侧是var fish1 是在声明一个变量,存在变量提升,右侧是一个函数,这属于一个函数表达式,所以函数并不会提升,所以当前作用域的代码顺序为
var fish1;
console.log(fish1, fish2, fish3);
  1. 第三句是一个函数的声明,此时会存在函数的声明提升,而且由于函数提升的优先级比变量高,所以函数会排在变量的前方,所以当前作用域的代码顺序为
function fish2 () {
    console.log('fish2');
}
var fish1;
console.log(fish1, fish2, fish3);
fish1 = function() {
    console.log('fish1');
}
  1. 第四句是一个变量的赋值语句,会存在变量的声明提升,赋值语句的位置不会改变,所以当前作用域的代码顺序为
function fish2 () {
    console.log('fish2');
}
var fish1;
var fish3;
console.log(fish1, fish2, fish3);
fish1 = function() {
    console.log('fish1');
}
fish3 = 'fish3'
  1. 第五句代码将三个变量全部重新声明了,所以会存在声明提升,此时fish1和fish3会将前面声明的变量覆盖掉,但是fish2因为是一个函数,函数的优先级会比变量高,所以并不会被覆盖掉
function fish2 () {
    console.log('fish2');
}
var fish1;
var fish3;
console.log(fish1, fish2, fish3);
fish1 = function() {
    console.log('fish1');
}
fish3 = 'fish3'
console.log(fish1, fish2, fish3);

至此,我们就可以看出console.log(fish1, fish2, fish3)打印的值了,第一句console.log(fish1, fish2, fish3) undefined [Function: fish2] undefined 第二句console打印的时候,因为fish1被赋值为一个函数,fish3会赋值为一个字符串,所以此时打印的是 [Function: fish1] [Function: fish2] fish3

不知道小伙伴们懂了吗,如果懂了的话,记得点个赞哦,十分感谢!!!后续还会更新其他的面试题,