2022-高级(资深)前端(一二线大厂)面试题(附答案)

10,702 阅读7分钟

前言

找工作,面试官非常看重个人的技术沉淀,如果说你有自己的博客(记录自己日常的一些技术分析等),开源(针对公司业务开发的组件,插件等,或者个人为了学习新技术而搭建的一套完整的技术产品,如电商系统等),那么这会让面试官眼前一亮,最起码会给人一种你是热爱技术的感觉,第一印象会加很多分,拿本人的开源项目来说:

  1. 基于Taro3的虚拟列表:github.com/tingyuxuan2…
  2. 基于cesium的热力图组件:github.com/cesium-plug…

star虽说没有成千上万,但至少证明你在沉淀,对技术是有追求的!

攻重浩: 攻城师不浪(优秀技术解决方案、前端架构、3D可视化、基础建设等);
绿泡泡:brown_7778(请尊重个人时间);

基建方向

一、如何监控资源加载时长?

获取所有资源:performance.getEntries()
获取静态资源:performance.getEntriesByType("resource")
获取动态资源载入:PerformanceObserver
参考: cloud.tencent.com/developer/a…

二、如何监听白屏、首屏渲染时长?

  1. 白屏

利用performance.timing的API(监控白屏这块网络上始终没有比较全面的介绍,如果有人做过实践,欢迎留言 )

document.addEventListener("DOMContentLoaded", () => {
    console.log('---------333-------', performance.timing.domInteractive - performance.timing.fetchStart)
})

2. 首屏

MutationObserver:监听页面元素抖动频率最大的那一时刻

三、js加载执行:async, defer, preload, prefetch的区别?

  1. async:立即下载(异步,不会阻碍文档解析),异步执行(执行的时候会阻碍文档解析)
  2. defer:立即下载(异步,不会阻碍文档解析),延迟执行,在整个页面都解析完毕后执行
  3. preload:提前下载,需要的时候立即执行,无需再下载
  4. prefetch:提前下载,在未来的某个页面可能会执行,节省下载时间

四、浏览器从输入url到渲染的过程?

参考:juejin.cn/post/684490…

五、浏览器的进程与线程

进程
浏览器进程、GPU进程、网络进程、插件进程、渲染进程
渲染进程中的线程
GUI渲染线程、JS引擎线程、计时器线程、异步http请求线程、事件触发线程 参考:juejin.cn/post/699184…

六、错误捕获

  1. document.addEventListener("unhandledrejection")
  2. document.addEventListener("error")

七、webpack优化手段

  1. exclude、include配置来转译更少的文件
  2. cache-loader缓存编译结果
  3. happypack多核构建,把任务分给多个子进程并发执行
  4. thread-loader把loader放置在单独的worker池中进行
  5. HardSourceWebpackPlugin提供中间缓存,节省二次编译构建时间
  6. DllPlugin和DLLReferencePlugin实现拆分bundles,将不会频繁更新的第三方库(如:react、react-dom等)单独打包
  7. optimization.splitChunks抽离公共代码
  8. webpack-bundle-analyzer分析包体积

参考:juejin.cn/post/684490…

八、如何监听到某个资源加载失败以及要怎么处理?

参考:www.alloyteam.com/2021/01/153…

九、页面长时间停留可能会卡死,从什么角度去排查问题

从内存溢出等方向,看是否有倒计时未清除、是否dom节点在无限递增、打印了(console)大量的size大的对象,大量闭包等方向

十、jsBridge的原理

参考:juejin.cn/post/684490…

十一、浏览器合成层

参考:juejin.cn/post/684490…

十二、css加载会阻塞dom渲染吗?会阻塞js执行吗?

参考:cloud.tencent.com/developer/a…

十三、h5优化手段

参考:juejin.cn/post/699438…

十四、强缓存、协商缓存与cdn缓存的区别

参考:(www.upyun.com/tech/articl… CDN 缓存与浏览器缓存.html)

JS基础

一、变量提升

var a = 2;
// 问题1
(function(){
    console.log(a)
    const a = 1
})()
// 问题2
(function(){
    console.log(a)
    var a = 1
})()

二、原型、原型链

参考:github.com/mqyqingfeng…
注意:
Function._proto_ === Function.prototype
Function.prototype._proto_ === Object.prototype
原型链示意图

三、箭头函数、普通函数的区别

  • this指向
  • 箭头不能new(原因)

四、构造函数如果是箭头函数能不能用new(不能)

五、实现一个功能让构造函数只能new操作,否则报错

if (!this instanceof Constructor) {
    throw new Error()
}

六、class类与es5的构造函数有什么区别

参考:www.1024sou.com/article/596…

七、mutationObserver、requestAnimationFrame

八、闭包的缺点

TS

一、泛型、infer

  1. 泛型:一种可重用的表示,可用类型变量,表示变量而不是值,可约束某些值的类型
  2. infer:表示在 extends 条件语句中待推断的类型变量

网络

一、http缓存

juejin.cn/post/684490…

二、https原理

  1. zhuanlan.zhihu.com/p/43789231
  2. juejin.cn/post/684490…

三、解决跨域请求头都需要设置什么

www.ruanyifeng.com/blog/2016/0…

四、cookie、session、localStorage分别是什么?有什么作用?

zhuanlan.zhihu.com/p/22388743

五、什么情况下会发送预检请求?如何优化?

juejin.cn/post/684490…

React

一、新版生命周期

  • 创建:constructor -> getDerivedStateFromProps -> render -> componentDidMount
  • 更新:getDerivedStateFromProps -> shouldComponentDidUpdate -> render -> getSnapShotBeforeUpdate -> componentDidUpdate
  • 卸载:componentWillUnMount

二、hooks如何模拟didupdate生命周期

const isDidMount = useRef(true)
useEffect(() => {
    if (isDidMount.current) {
        // 只有第一次执行
        isDidMount.current = false
        return
    }
    //didupdate...
})

三、useEffect和useLayoutEffect区别

  • useEffect:回调在组件渲染完成之后的一个延迟函数中执行,不会阻塞视图渲染
  • useLayoutEffect:在render之后执行,执行时机相当于componentDidMount和componentDidUpdate,会同步触发组件重新render,会阻塞视图渲染

四、hooks原理

参考:github.com/brickspert/…

五、diff原理、fiber原理

参考:react.iamkasong.com/me.html

六、hooks和class的区别(hooks解偶)

七、react如何实现的中断可恢复更新

参考: juejin.cn/post/698494…

手写题(编码)

  1. 实现flat
const myFlat = (arr) => {
    return arr.reduce((prev, cur) => {
        return prev.concat(Array.isArray(cur) ? myFlat(cur) : cur)
    }, [])
}

2. 实现promise.all

const promiseAll = (promises) => {
    let res = []
    let count = 0
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            Promise.resolve(promises[i]).then((data) => {
                count++
                // 重点,向对应下标塞数据,保证顺序
                res[i] = data
                if (count === promises.length) {
                    resolve(res)
                }
            }).catch(err => {
                reject(err)
            })
        }
    })
}

3. 实现new

function myNew() {
    const obj = new Object()
    const Constructor = [].shift.call(arguments)
    obj.__proto__ = Constructor.prototype
    const res = Constructor.apply(obj, arguments)
    return typeof res === "object" ? res : obj
}

4. 订阅发布

class Event {
    constructor() {
      // 所有 eventType 监听器回调函数(数组)
      this.listeners = {};
    }
    /**
     * 订阅事件
     * @param {String} eventType 事件类型
     * @param {Function} listener 订阅后发布动作触发的回调函数,参数为发布的数据
     */
    on(eventType, listener) {
      if (!this.listeners[eventType]) {
        this.listeners[eventType] = [];
      }
      this.listeners[eventType].push(listener);
    }
    /**
     * 发布事件
     * @param {String} eventType 事件类型
     * @param {Any} data 发布的内容
     */
    emit(eventType, data) {
      const callbacks = this.listeners[eventType];
      if (callbacks) {
        callbacks.forEach((c) => {
          c(data);
        });
      }
    }
}

5. 实现一个缓存函数

const memorize = (fn) => {
    const obj = {}
    return (...args) => {
        const key = JSON.stringify(args)
        if (obj[key]) {
            return obj[key]
        } else {
            return (obj[key] = fn.apply(this, args))
        }
    }
}

6. 数组去重

const unique = (arr) => {
    const obj = {}
    return arr.filter((item) => {
        const key = typeof item + item
        return obj.hasOwnProperty(key) ? false : (obj[key] = true)
    })
}

7. arrayToTree 将

var arr = [
    { id: 1, name: "1111", pid: 0 },
    { id: 2, name: "2222", pid: 1 },
    { id: 3, name: "3333", pid: 1 },
    { id: 4, name: "4444", pid: 2 },
    { id: 5, name: "5555", pid: 4 },
    { id: 6, name: "5555", pid: 0 },
  ];

转化为

[
        {
          id: 1,
          name: "1111",
          pid: 0,
          children: [
            {
              id: 2,
              name: "2222",
              pid: 1,
              children: [
                {
                  id: 4,
                  name: "4444",
                  pid: 2,
                  children: [
                    {
                      id: 5,
                      name: "5555",
                      pid: 4,
                    },
                  ],
                },
              ],
            },
            {
              id: 3,
              name: "3333",
              pid: 1,
            },
          ],
        },
        {
          id: 6,
          name: "5555",
          pid: 0,
        },
      ];
const arrayToTree = (arr, id, pid, rootVal) => {
    const res = []
    for (let i = 0; i < arr.length; i++) {
        const item = arr[i]
        if (item[pid] === rootVal) {
            res.push(item)
        } else {
            const parent = arr.find(el => el[id] === item[pid])
            if (parent) {
                if (parent.children) {
                    parent.children.push(item)
                } else {
                    parent.children = [item]
                }
            }
        }
    }
    return res
}

8. 实现接口最大并发

// arr -> 接口数组
// max -> 最大并发数
const poll = (arr, max) => {
    const run = () => {
        if (!arr.length) return
        const min = Math.min(arr.length, max)
        for (let i = 0; i < min; i++) {
            max--
            const item = arr.shift()
            Promise.resolve(item).then(res => {
                console.log(res)
            }).catch(err => {
                console.log(err)
            }).finally(() => {
                max++
                run()
            })
        }
    }
    run()
}

9. 防抖

const debounce = (fn, delay) => {
    let timer = null
    return (...args) => {
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, delay)
    }
}

10. 截流

const throttle = (fn, delay) => {
    let timer = null
    let startTime = 0
    return (...args) => {
        timer && clearTimeout(timer)
        const now = Date.now()
        if (now - startTime > delay) {
            fn.apply(this, args)
            startTime = now
        } else {
            timer = setTimeout(() => {
                fn.apply(this, args)
            }, delay)
        }
    }
}

11. reduce

Array.prototype.myReduce = function(callback, initVal) {
  const arr = this
  let res = initVal === undefined ? arr[0] : initVal
  const index = initVal === undefined ? 1 : 0
  for (let i = index; i < arr.length; i++) {
    res = callback(res, arr[i], i, arr)
  }
  return res
};

12. 深拷贝

参考:juejin.cn/post/684490…

  1. promise

参考:juejin.cn/post/686003…

  1. 柯里化
const curry = (fn) => {
    return curried = (...args) => {
        if (args.length >= fn.length) {
            return fn.apply(this, args)
        } else {
            return (...innerArgs) => {
                return curried.apply(this, [...args, ...innerArgs])
            }
        }
    }
}

15. 千分位分隔符

const myLocalStr = (num) => {
    const arr = num.toString().split("").reverse()
    let res = []
    for (let i = 0; i < arr.length; i++) {
        if (i % 3 === 0 && i !== 0) {
            res.push(",")
        }
        res.push(arr[i])
    }
    return res.reverse().join("")
}

16. promisify

const promisify = (fn) => (...args) => {
    return new Promise((resolve, reject) => {
        fn.call(this, ...args, (err, data) => {
            if (err) {
                reject(err)
            } else {
                resolve(data)
            }
        })
    })
}

CSS

一、 什么是BFC(一块独立的渲染区域)

如何触发BFC布局:

  • float的值不为none
  • overflow的值不为visible
  • display的值为inline-block、table-cell、table-caption
  • position的值为absolute或fixed

参考:juejin.cn/post/684490…

二、三角形,以及给加个边框

加边框:两个三角形重叠

三、margin重叠问题

将父元素添加overflow:hidden,变成BFC模块

四、css module原理

参考:www.ruanyifeng.com/blog/2016/0…

五、盒模型

算法、数据结构

1. 判定有效括号

2. 最长回文子串

3. 判断数组中是否有重复元素

4. 链表反转

前端安全

1. 什么是xss攻击,如何防御

参考: zhuanlan.zhihu.com/p/83865185

项目

一、小程序底层实现原理

  1. 双线程原因:管控性和安全性,阻止开发者使用一些页面跳转、dom操作、动态执行脚本的开放性操作,因此小程序提供了一个沙箱,只能进行纯js操作,没有任何浏览器相关接口
  2. 双线程通信:通过native(微信客户端)中转
  1. zhuanlan.zhihu.com/p/81775922
  2. developers.weixin.qq.com/community/d…

二、小程序相对h5有什么优势区别

zhuanlan.zhihu.com/p/43138677