0


记一次没有准备的前端笔面

题:写出这段程序的打印顺序
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('asnyc1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(() => {
    console.log('setTimeOut');
}, 0);
async1();
new Promise(function (reslove) {
    console.log('promise1');
    reslove();
}).then(function () {
    console.log('promise2');
})
console.log('script end');

Js的两种任务:宏任务、微任务;

宏任务包括:整体代码script、setTimeout、setInterval、setImmediate和I/O操作等。

微任务包括:process、nextTick、Promise、async/await和MutaitionObserver等。

JS执行顺序遵循一个事件循环机制,在每一次循环中,会执行一个宏任务,并执行它产生的微任务(所有),然后进入下一次循环。一般来说,一个宏任务执行过程中会产生若干个微任务,它们会被加入任务列表,等待当前宏任务执行完毕后,执行微任务队列中的所有任务。

具体JS代码执行顺序如下:

  1. 执行全局代码,初始化全局环境。
  2. 从宏任务(macrotask)队列中取出一个最先进入队列的任务,执行它;在执行过程中,该宏任务产生的微任务(microtask)会被加入到微任务队列中。
  3. 当前宏任务和所有微任务执行完毕后,进入下一轮事件循环。取出一个最先进入任务队列的宏任务,执行它,如果此时宏任务队列中没有任务,则等待新的宏任务加入队列。

ATTENTION:JS代码执行期间,只有一个宏任务在执行,也只有一个微任务队列。因此如果当前宏任务未执行完毕,新的宏任务不会被执行,直到当前宏任务执行完毕才会执行下一个宏任务。

宏任务>微任务的Event Queue>宏任务的Event Queue

而后执行顺序:

js对setTimeOut的处理方式

setTimeout并不是直接的把回调函数放进上述的异步队列中,而是在定时器的时间到了之后,把回调函数放进执行异步队列中去,排在已经在队列中的任务之后。这也解释了为什么setTimeOut不能精准执行的问题。

setTimeout执行需要满足两个条件:

  1. 主进程必须是空闲状态,注意到时间后主进程不会执行回调函数
  2. 回调函数需要等到插入异步队列时前面的异步函数都执行完才会执行。
js对promise、async/await的处理方式

首先,new Promise是同步的任务,会被放到主进程中去立即执行。而.then()函数是异步任务,当promise状态结束的时候,.then()会立即放到异步队列中去。

带async关键字的函数会返回一个Promise对象,如果里面没有await,执行起来等同于普通函数。

await关键字会等待右侧表达式完成,此时await会让出线程,阻塞async内后续的代码,先去执行async外的代码。等async函数外的同步代码执行完毕,才会执行里面的后续代码。就算await的不是promise对象而是一个同步函数,也会挂起等待。

根据上述将题目分析:

  1. *首先打印“script start*”,表示脚本开始执行。 **** **
  2. 继续执行,调用async1()函数,在async1()函数中,打印"async1 start"。
  3. 调用async2(),在async2中打印"async2" ③
  4. 回到async1()中,因为await async2()是一个异步操作,所以async1()函数会暂时挂起,等待async2()函数执行完成。
  5. 接着执行new Promise()的构造函数,打印"promise1"。
  6. 执行resolve(),Promise的状态变为resolved。
  7. 调用then()方法注册回调函数,加入异步任务队列中。
  8. 继续执行,打印"script end"。
  9. 此时宏任务中所有同步任务执行完毕,开始依次执行微任务队列中的任务。
  10. 回到async1()函数中,await async2()操作完成,继续执行后续代码,打印"async1 end"。
  11. 排在第二的.then()执行,打印“promise2”。
  12. 最后执行setTimeout(),由于设置了0ms延迟,所以会尽快执行,打印"setTimeOut"

题:数组A[5,1,5,3,3],数组B[5,3],找出数组A中数组B相同元素的下标存入新数组。

我的解答:

const A=[5,1,5,3,3];
const B=[5,3];
function findIdx(){
    let result=[];
    A.forEach(item,index=>{
        if(B.indexOf(item)!=-1){
            result.push(index)
        }
    })
    return result;
}
let final=findIdx();

面试官提问:

我这样的写法时间复杂度是多少?

数组forEach()方法时间复杂度O(n),indexOf()方法时间复杂度O(n),所以该写法时间复杂度为O(n²)量级,但因为A、B两数组长度不一,具体俺也不知道。

怎样优化代码降低时间复杂度?

可以把B数组转化为对象,查找A数组在B对象中是否有值来判断,降低时间复杂度为O(n)

const A=[5,1,5,3,3];
const B=[1,3];
function findIdx(){
    let objB=f(B);
    let result=[];
    A.forEach(item,index=>{
        if(B[item]!=undefined){
            result.push(index);
        }
    })
}
function f(arr) {
  return arr.reduce((obj, item) => (obj[item.value] = "",obj), {})
}

Promise.all()方法接受一个promise对象数组作为参数,返回一个新的Promise对象。该Promise对象在所有的Promise对象都成功时才会成功,其中一个Promise对象失败时,则该Promise对象立即失败。

Promise.all=function (promises){
    let results=[];
    let length=promises.length;
    let promiseCount=0;
    return new Promise((resolve,reject)=>{
        for(let i=0;i<length;i++){
            Promise.resolve(promises[i]).then(res=>{
                results[i]=res;
                promiseCount++;
                if(promiseCount===length){
                    resolve(results);
                }
            }),err=>{
                reject(err);
            }
        }
    })
}

题:before(num,fn)接受两个参数,第一个参数是数字,第二个参数是函数,调用 before 函数 num 次数以内,返回与 fn 执行相同的结果,超过 num 次数返回最后一次 fn 的执行结果,使用闭包实现。

闭包概念:如果一个函数访问了此函数的父级及父级以上的作用域变量,那么这个函数就是一个闭包。本质上,JS中的每个函数都是一个闭包,因为每个函数都能访问全局变量。

function before(num,fn){
    let res;
    return function(...args){
        if(num-->0){
            res=fn(...args);
        }
        return res;
    }

}

方法一:使用sort排序

const arr=[10,5,1,12,7,8,8]
function findSecond(){
    let cleanArr=Array.from(new Set(arr));//先给数组去重
    let newArr=cleanArr.sort((a,b)=>{return a-b});//将数组从小到大排序
    return newArr[newArr.length-2];
}
let res=findSecond();

方法二:

function findSecond(){
    let max,min;
    if(arr[0]<arr[1]){
        max=arr[1];
        min=arr[0];
    }else{
        max=arr[0];
        min=arr[1];
    }
    for(let i=2;i<arr.length<i++){
        if(arr[i]>min){
            if(arr[i]>max){
                min=max;
                max=arr[i];
            }else{
                min=arr[i];
            }
        }
    }
    return min;
}
const res=findSecond();

方法三:

function findSecond(){ //从大到小对数组进行排序
    let temp;
    for(let i=0;i<arr.length-1;i++){
        for(let j=i+1;j<arr.length;j++){
            if(arr[j]>arr[i]){
                temp=arr[i];
                arr[i]=arr[j];
                arr[j]=temp;
            }
        }
    }
}
const res=arr[1];//从大到小排序的数组中,第二项就是第二大值

小插曲

如何用set给数组去重

1.Array.from

Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

const newArr=Array.from(new Set(arr))

2.扩展运算符...

const newArr=[...(new Set(arr)]

参考:【手撕代码系列】JS手写实现Promise.all_js 手写一个promise.all方法-CSDN博客

        关于async/await、promise和setTimeout执行顺序_js await执行顺序-CSDN博客

本文转载自: https://blog.csdn.net/qq_45246562/article/details/135859751
版权归原作者 前端小趴菜13 所有, 如有侵权,请联系我们删除。

“记一次没有准备的前端笔面”的评论:

还没有评论