# 2021面试题整理

# part 1

# 1. 你了解浏览器的事件循环吗

# 1.1 为什么js在浏览器中有事件循环机制

js是单线程,为了执行异步任务,不阻塞主流程

# 1.2 两种任务有什么?

宏任务:整个代码块,setTimeout,setInterval,I/O操作

微任务:new Promise().then()

# 1.3 为什么引入微任务概念, 只有宏任务可以吗?

宏任务是先进先出,当急任务需要执行的时候,只能从后面添加,最后执行

引入微任务,当一个宏任务执行完成,先检查是否有微任务,先执行微任务,这样就能保证紧急任务优先执行

# 1.4了解node中的事件循环吗,和浏览器的事件循环有什么区别?

node v10及以前:

  1. 执行一个阶段的所有任务
  2. 执行nextTick队列的内容
  3. 执行微任务队列的内容

node v10以后和浏览器统一:

  1. 执行完一次宏任务
  2. 清空微任务

# 2. 事件的捕获和冒泡

# 2.1 基本概念

捕获:从外向内触发

冒泡:从内向外触发

# 2.2 window.addEventLister 监听的是什么阶段的事件

根据第三个参数判断

该事件依次有三个参数:事件名,函数,默认false(监听阶段true: 捕获, false: 冒泡)

# 2.3 哪些场景会用到这个机制

事件委托

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>
1
2
3
4
5
6

# 3 防抖和节流

# 3.1 概念

防抖:频发触发事件,从最后一次触发的事件开始计时,n秒后执行

节流:n秒内触发事件,只执行一次

# 3.2 使用场景

防抖:输入框频发触发接口

节流:resize sroll

# 手写

  • 节流
funtion throllte(fn, num) {
    return function() {
        let timer = setTimeout(() =>{

        }, num)
    }
}
1
2
3
4
5
6
7

# promise

# promise缓存使用

一个接口多次调用,每次都发请求比较浪费

# part 2

# 前端性能优化的工作,都做过哪些能力

注意点

不要直接将做了哪些优化,要先说做性能优化的目的的是什么?

比如:因为项目有什么问题,为了解决什么,提升什么,做了哪些优化,最终结果是什么

任何的问题都可以代理star原则

# 优化点的点,有以下几个方面:

  1. 首屏时间
  2. 首次可交互时间
  3. 首次有意义内容的渲染时间

# 各种实现:

  1. 只请求当前需要的资源

异步加载、懒加载、polyfill(针对浏览器加载处理不兼容的方法:按需加载相应的处理方法)

  1. 缩减资源体积

打包压缩(webpack4已经内置该功能:代码js或者css等) 图片格式的优化、压缩 尽量控制cookie的大小

  1. 时序优化

promise.all(): 对于一些不强关联的请求,并发请求

ssr: 服务端渲染,也方便seo

prefetch、prerender、preload

  1. 合理利用缓存

cdn: cdn预热、cdn刷新

什么叫cdn预热

主动从源站推送到CDN,让用户访问到CDN时不用回客户的源站命中。 某网站首次发布,如果没有预热,第一批用户在访问官网时,由于是第一次访问,CDN中缓存也没有资源,因此需要回源站去获取。

什么叫cdn刷新

CDN节点缓存的资源没有过期,但是基于客户的业务要求,需要更新CDN节点上缓存资源。遇到这种场景我们应该怎么办?刷新功能就登场了,刷新就是强制删除CDN节点缓存内容。用户请求这些资源时,CDN节点需要重新回源拉取资源,保证响应的资源与源站一致。

源站是指内部服务器,大批量访问,可能会挂

# 如果一段js代码执行时间非常长,怎么去分析

通过写一个装饰器来统计函数的运行时间

# 如果页面有巨量图片需要展示,处理懒加载模式,有没有其他方法限制同时加载的图片的数量

思路:写一个函数,最多同时并发加载n个,比如先加载了n个,当其中一个加载完后,立即从剩余的队列取一个加载,保证高并发且最多n个

注意:promise.all是等所有参数都执行完,才取剩余队列加载,和要实现的函数是只要有一个加载完就立即取剩余队列的一个数加载

已知代码:

function limitLoad(arr, handler, limit) {
    
}

const urls = [
    {link: 'url1', timer: 2000},
    {link: 'url1', timer: 2000},
    {link: 'url1', timer: 2000},
    ....
]

// 要高并发执行的任务,此处场景为模拟加载图片
function loadImg(url) {
    return new Promise((resolvem reject) => {
        setTimeout(() => {
            console.log(url.link)
            resolve()
        }, url.timer)
    })
}

limitLoad(urls, loadImg, 3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

实现limitLoad函数

/**
 * arr: 图片列表
 * handler:要处理的函数(加载图片)
 * limit: 高并发个数的限制
*/
function limitLoad(arr, handler, limit) {
    const resouce = [].concat(arr) // 做一层拷贝

    let curLoadArr = []

    // splice截取数组前limit个数据,并且改变原数组
    curLoadArr = resouce.splice(0, limit).map((url, index) => {
        return handler(url).then(() => {
            return index // 返回下标,是为了知道是哪个图片加载完了
        })
    })

    // Promise.race:第一个函数返回时,就resolve,并且返回第一个执行结果
    let p = Promise.race(curLoadArr)

    for(let i = 0; i < resouce.length; i ++) {
        p = p.then(index => {{
            curLoadArr[index] = handler(resouce[i]).then(() => {
                return index // 返回下标,是为了知道是哪个图片加载完了
            })
            return Promise.race(curLoadArr)
        }})
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#