🤫 嘘 —— 其实是一篇面经...
本人面试前状态:18届/裸辞/三年B端SaaS/没有跳槽经验/休息了3个月,期间没有特意刷过题,就边休息边回顾了下项目经历相关的内容,以及之前攒的一些个人笔记整理,面试可能得摩擦摩擦了
review进度
共 69 题,已完成 16 题,进度 23%
09/03 周五
近两周面试体会是,有些玄学,我觉得还ok的没过,不太ok的却过了,心态也是两周来起起伏伏的,还是得端平心态,毕竟找工作是个说长不长说短不短的事情,持久战!心态很重要。面试也有很大部分讲究的是缘分,不要去计较面试结果(我在对我自己说),只要这场面试你有收获就是好的,把自己没有考虑过的问题、会用但说的磕磕巴巴的问题都记录下来,花点时间review,多体验几次自然会孰能生巧一点。
⛳️ 立个flag,这周末把近两周的面试问题答案总结一下(能理解的部分,不太熟悉或者压根没接触过的就不额外学习了),求小伙伴们监督!! 近期有在面试或是准备面试的小伙伴@SH可以留言,一起交流下呀~
09/05 周日
这两天review了一下react相关的内容,主要包括Fiber架构、hooks实现、dom-diff原理、合成事件(react对事件系统做的一层封装,像理解setState是同步异步的问题就需要先理解react中事件系统的实现)等,笔记还在梳理中,待陆续更新,以及第一次开始刷算法题(慢慢积累中,有兴趣一起刷题的可以看这篇内容)。
09/06 周一
还有蛮多题目没有review的,再接再厉
1
✅ 1. 垂直居中有哪些方案
// 说自己常用的就行,没必要深究
// 1 flex
{
display: flex;
align-items: center;
}
// 2 margin
{
margin: o auto;
}
✅ 2. 一行水平布局三个元素等分如何实现
我当时回答了flex+设置等分宽度,面试官就问了width:33.33%会有空间多余的情况,有没有更好的方法 —— flex:1
举一反三:讲一下flex常用的属性有哪些(包括flex:1说明)
3. 输入URL到gateway中间的过程
浏览器相关的内容可以参考我的这篇文章
// 这里是到gateway,就是网关部分,不涉及渲染相关
3.1 TCP和HTTP有什么区别
3.2 对https有什么了解么(ssl连接在整个耗时上占用多么)
3.3 对 ws 和 wss 有了解么
4. 介绍下最近看了什么文章或者书籍(开放问题)
5. 前端其他优化经验(开放问题)
2
✅ 1. 能介绍下你做过的比较复杂的项目么?(具体复杂在什么地方,你是如何实现的)
// 开放题,按照项目实际经历准备就行
2. antd大表单性能是如何优化的
3. ES6 class static 用ES5怎么写
思路:static 有什么特点
4. 定义一个function fn,操作fn.a = 1会报错么,为什么
✅ 5. WeakSet 和 WeakMap 有了解么
使用 WeakSet 和 WeakMap 引用的变量是弱引用,也就是在内存分析阶段,不会认为这里存在一个引用,如果没有其他引用则会在使用完毕后自动释放内存。
参考文章:阮一峰ES6教程,介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
6. 如何实现B继承A(ES5)
✅ 7. 你对浏览器缓存了解么
关键点:强缓存和协商缓存哪个优先(对应状态码),Cache-control设置值的区别
先查找强缓存(Cache-control>Expires => 200 OK),再查找协商缓存(Etag + If-None-Match > Last-Modified + If-Modified-Sine => 304 Not Modified)
缓存命中过程
强缓存/协商缓存
// no-store和no-cache的区别,max-age=0是什么意思
no-store 直接不做缓存
max-age=0 客户端:"我有缓存,但是我还是想确认下我的缓存是否能用"
no-cache 客户端:"不管我有没有缓存,我都想重新拿一次文件"
8. DNS缓存了解么
9. CDN缓存有了解么?前端如何实现更新缓存?
10. 有了解过 HTTPS 么,如何实现的?
HTTPS = HTTP 应用层传输协议 + SSL 运输层协议
// 我还没理清,先不放解答,提供下可参考的文章
参考文章:图解HTTPS建立过程](www.cnblogs.com/softidea/p/…)
11. CSS3新特性有哪些了解?动画有写过么?grid布局原理?flex布局原理?
✅ 12. 如果从A页面跳转到B页面,菜单是如何做到高亮的?你有什么实现思路
思路:首先页面内分为菜单和内容两块,本质上他们就是两个组件,那么这道题就是路由更新后如何让组件得到感知,做出相应更新。这里就可以参考react-router的实现原理说一下自己的理解。
react-router实现原理分析
13. webpack优化可以从哪些方面入手
14. 交给你一个优化webpack开发构建速度的任务,你有什么思路
思路:构建速度会受什么影响
15. webpack3能直接升级webpack4么?会遇到什么问题?
16. 你对 webpack5 有了解么?
17. 在不改动页面代码的情况下可以实现webpack3升webpack5么,为什么呢
18. 有了解过webpack源码么?有自己实现过loader和plugin么?
19. 你是怎么理解高阶函数的?平时有使用的场景么?
参考文章:高阶函数与高阶组件
// 可以参考下react-router源码中,withRouter的实现(相对还不是那么复杂,或者用习惯redux的同学可以看下connect方法的实现)。withRouter让我们可以
✅ 20. 说说你对TS泛型的了解?装饰器有用过么?
泛型:宽泛的类型,在声明时不指定具体类型,使用时才会传入具体类型。
// 装饰器没用过,可以自行搜索
21. React页面在一次交互中发生了预期外的多次重绘,你会如何解决?
✅ 22. 你了解柯里化么?
参考文章:高阶函数与高阶组件
// 几乎没有写过
3
✅ 1. 五个div 如何实现两行+三栏布局
实现重点:width: clac(100% / 3)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
.container {
display: flex;
flex-wrap: wrap;
}
.container div {
background-color: blanchedalmond;
border: 1px solid black;
width: calc((100% - 2px * 3) / 3);
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="1">1</div>
<div class="2">2</div>
<div class="3">3</div>
<div class="4">4</div>
<div class="5">5</div>
</div>
</body>
</html>
效果如下:
✅ 2. useEffect 里闭包 + setTimeout
function Test() {
const [a, setA] = useState('testA')
useEffect(() => {
setA('testB')
setTimeout(() => {
console.log(a)
}, 300)
}, [])
}
问1:console.log(a)输出什么
输出testA
问2:如何让console.log(a)输出testB
useEffect中每次拿到的props和state都是当次渲染时的state和props,所以之前在打印时即使前面有一步setName的操作,但这个新name也是下一次useEffect中可以拿到的,当前次useEffect中永远拿到的是didmount时的那个name值(因为deps依赖是个空数组=didmount=初次渲染后=initialState='A')
沙盒模拟:codesandbox.io/s/ecstatic-…
✅ 3. eventLoop promise 题目输出结果
题目可以参考我的promise文章
eventloop相关理解可以参考我的这篇文章
4. 手写url解析 + cache
5. 鉴权系统更好的实现
6. 前端怎么做登录流程
// cookie和localstorage实现有什么不同
7. 高频题:class组件和function组件有什么区别
// 这是个常规问题,几乎每次都被问到了
8. hooks是用来做什么的
✅ 9. 状态码
// 301,302,304,401... 只需要梳理一些常用的状态码
301:永久重定向,浏览器会缓存重定向结果(重定向地址在response headers 中的 Location 字段上)
302:临时重定向(重定向地址如上),浏览器接收到302响应之后,会立即读取Location字段值,向该地址跳转并做后续请求
304:资源未修改(缓存校验后返回响应)
401:无权限(未登录状态下请求服务)
10. 粘性布局
// sticky
11. webpack优化做了哪些
12. treeshaking 了解么
13. 如何清理unused代码
14. ts unused 怎么配置
15. 泛型了解么,keyof是怎么用的
16. 右击事件菜单如何改写默认的菜单行为以及样式
4
1. ts keyof 和 typeof 的区别
这个点也是我常用但是从来没考虑过的,面试官有给解释keyof=>联合类型,typeof就是个对象,之后也要review一下
2. context 和 redux 区别
关键点:context发生更新后,所有子组件都会rerender,redux则需要手动订阅需要更新的逻辑
3. hooks 生命周期如何模拟
关键点:useEffect的第二个参数的三种情况
追问1:useEffect 返回一个函数时,这个函数会在何时执行
追问2:useEffect 返回一个函数时,这个函数会在何时执行
4. webpack 热更新实现原理,以及如何做到keep状态下更新的
关键点:webpack-dev-server + ajax
5. 介绍前端路由,react-router 实现原理
关键点:两种路由实现,路由监听的api,react-router实现路由监听的过程(内部将路由相关的东西存储在state里,通过context将state传递给下游组件,当state更新时,通知下游组件做视图更新)
✅ 6. coding:数组转树
function arrayToTree(array) {
if (!Array.isArray(array)) return
// 借用Map可以存储任何数据类型的特点,以id为key,value存储该id对应的children们
// 实现的关键是借用引用传递的原理,当我们遍历改动时,引用到的地方也会自动更新
// 最后做一次过滤,只输出是顶级父节点的元素就可以
const treeMap = new Map(
array.map((item) => [item.id, { ...item, children: [] }])
)
for (let i in array) {
// 有父元素
if (array[i].pid) {
// 当前元素(重点:这里的item是个引用值)
const item = treeMap.get(array[i].id)
// 从treeMap中取得父元素(这里是一定会有值的,因为我们在初始化时给所有元素都塞进了treeMap中)
const pItem = treeMap.get(array[i].pid)
// 将当前元素塞入父元素的children中
pItem.children = pItem.children || []
// 重点:因为塞入的item是个引用值,所以后续如果item更新了,这里的children也会自动更新
pItem.children.push(item)
}
}
return [...treeMap]
.filter(([id, item]) => !item.pid)
.map(([id, item]) => item)
}
const sampleArray = [
{ id: 1, name: 'a1' },
{ id: 2, name: 'a2', pid: 1 },
{ id: 3, name: 'a3', pid: 2 },
{ id: 4, name: 'a4', pid: 2 },
{ id: 5, name: 'a5', pid: 4 },
{ id: 6, name: 'a6', pid: 3 },
]
const tree = arrayToTree(sampleArray)
console.log(tree)
7. coding:模拟react-router路由匹配
test/:id/detail/:name => test/14/detail/Marry => { id: 14, name: 'Marry' }
8. coding:判断字符串是否是回文
9. 闭包是如何实现的
这题着实有点懵
10. 解释一下什么是作用域
11. tcp 和 udp 的区别
12. PureComponent使用场景分析
关键点:自动在shouldComponentUpdate中做了一层props的浅比较,源码中为shallowEqual(props, nextProps)
p.s. 上面有个关键字浅比较,顺便可以准备一下深拷贝的代码以防万一面试官的思维刚好从这里发散(附上充分可以理解的深拷贝代码)
13. setState更新过程
关键点:收集多次改动,批量更新
14. useCallback和useMemo使用场景分析
15. 怎么理解 async/await,和promise的区别
✅ 16. coding: 手写promise.all
我有一个手写代码仓库,可以仅供参考一下
17. coding: 合并两个数组,且从小到大排序(不能用api)
18. ts 基于antd封装的组件props如何声明(不引入antd props依赖包)
这个问题在原来写代码的过程中一直没有想过,面试官有给解释React.Component有一个自动读取props的功能(好像是这个意思),之后查阅了解一下,也算get了一个知识盲区了
// class xxx extends React.Components 做了什么
19. hooks 有哪些使用限制,会有这些限制的原因
限制:只能在React函数组件顶层使用hooks,不要在循环、条件或嵌套函数中使用hooks。
思路:hooks是Fiber架构下的产物,可以先理解Fiber架构下react更新及渲染的过程,再去理解hooks是如何工作的。
看源码提问:useReducer 是什么?(useState是useReducer的语法糖)
参考文章:React Hooks 原理,为什么顺序调用对 React Hooks 很重要?,ReactFiberHooks源码
react更新及渲染过程图解(Fiber架构下React和浏览器之间是如何协作的)
key设置成索引不好的原因(涉及 dom-diff 原理)
5
✅ 1. coding: 完成一个函数, 从给定字符串中找出最长回环子串并打印出来
/**
* 完成一个函数, 从给定字符串中找出最长回环子串并打印出来
* abcbdef -> bcb
* a -> a
*/
function func(str) {
if (!str || !str.length) return null
if (str && str.length === 1) {
return str
}
let result = ''
// 是否是回环串
function testMatch(text) {
let result = true
let i = 0
let j = text.length - 1
if (i === j) return true
while (i < j) {
// start === end
if (text[i++] !== text[j--]) {
result = false
break
}
}
return result
}
let strArr = str.split('') // [a,b,c,b,d,e,f]
while (strArr.length) {
let substring = ''
for (let i = 0; i < strArr.length; i++) {
substring += strArr[i]
if (testMatch(substring) && substring.length > result.length) {
result = substring
}
}
strArr.splice(0, 1)
}
return result
}
console.log(func('abcbdef'))
console.log(func('a'))
console.log(func('aaaa'))
console.log(func('ababaab'))
2. 介绍一下ref以及有哪些使用场景
3. 前端做登录流程,你觉得登录信息存在哪里比较安全?
思路:redux state 管理(即时刷新也不会被清空)
// 和2的第6题想问的内容差不多
6
✅ 1. coding: 实现compose函数
// task: 输出字符串的大写倒序字符串,我们把步骤分为以下四步
// 转大写字母
const toUpper = (str) => {
return str.toUpperCase()
}
// 拆分字母
const splitWords = (str) => {
return str.split('')
}
// 倒序数组
const reverseArr = (arr) => {
return arr.reverse()
}
// 拼接字母输出字符串
const joinWords = (str) => {
return str.join('')
}
let str = 'hello world'
// 1
str = toUpper(str)
// 2
str = splitWords(str)
// 3
str = reverseArr(str)
// 4
str = joinWords(str)
console.log('str: ', str)
// 使用compose函数将这个四个步骤连起来
// 思路:这些步骤函数是对同一target在不同阶段进行加工, 每一阶段需要返回当前步骤处理好的target供下一个阶段使用,并且顺序从右往左,先执行的放右边
// compose(fn1, fn2, fn3, fn4)(x) => fn1(fn2(fn3(fn4(x))))
function compose(...fnList) {
return function () {
// 借用reduceRight实现将每次函数执行返回的内容得到累加,传递给下一个步骤函数
// [joinWords, reverseArr, splitWords, toUpper]
return fnList.reduceRight((params, currFn) => {
return currFn(params)
}, ...arguments)
}
}
console.log(
'compose: ',
compose(joinWords, reverseArr, splitWords, toUpper)('hello world')
)
✅ 2. ts 又被问到了泛型
function func(obj, key) {
return obj[key]
}
如何定义func的类型,支持多种数据类型
实现关键:obj: 泛型, key: obj的某一个key => obj: 泛型 => T, obj的某个key: keyof obj => keyof T => K extends keyof T
extends实现继承接口(interface)的方式,即快速复制一个接口。
p.s. 上图写的“自动读取”措辞不太严谨,按照ts的说法应该叫“类型推论”比较准确。
✅ 3. ts 如何得到一个函数的返回类型
使用 ReturnType
type Bar = string;
type foo = () => Bar;
type baz = ReturnType<foo>;
4. css层叠样式规则
5. 高频题:你对 class component 和 function component 的理解
// 使用function component状态管理变得更清晰
6. 有自己写过 custom hooks 么?具体实现了什么?
这家公司的面试官有提到对函数式编程的理解,我个人了解的还不多,后期可以学习一下
7
1. 受控组件和非受控组件的理解
2. setState 和 useState 有什么区别
3. 介绍下 requestIdlecallback 和 requestAnimationframe
4. coding: 找dom(使用querySelectAll + :nth-child(x))
5. coding: 简单的HOC编写
6. coding: 封装一个addEventListener custom hooks
7. 开放题:如果需要做前端监控系统,你有什么想法?
// tips: 类似 sentry 之类的
8. eslint 和 prettier 的区别
9. react-hot-loader 原理
8
1. redux 内部实现时 为什么需要拷贝一份原store(Object.assign)
2. coding: 手写柯里化
3. 多tab间如何通信,状态同步?
4. 开放题:你觉得遇到过最有挑战的问题是什么,如何解决的?
手写代码的一些小tip
首先我没有特意复习过什么算法、数据结构之类的,所以一般都是就临场想到的方法实现的,多半是比较"朴素"的实现方式,但出错几率相对也要低一点。
在我几次手写代码过程中,我发现有时候会临时想不起来api,或是一些简单的类型转换(代码提示yyds),所以这边列一些我觉得使用机率还蛮大的api。
- 遍历
for (let i in target) {...}: 将target用可迭代的形式遍历,i一般为下标,当target是对象时,i就是keyName。在一些判断时可以用break/continue提前跳出判断,这个功能是array.forEach没有的。
for...in和for...of的区别:in是遍历键,of是遍历值,in会将原型链上的属性也遍历(所以最好不要用他遍历对象)
for (let i = 0; i < len; i++) {...}:简单的数组遍历就用这个,万无一失
- 字符串转数组
array = str.split(''):将字符串单字母分割成数组'hello'=>['h','e','l','l','o']
- 数组中是否存在
array.includes(e => ...):返回true or false
array.find(e => ...):返回第一个满足条件的元素
array.findIndex(e => ...):返回第一个满足条件的元素下标,没有找到则返回-1
array.indexOf('a', startIndex):首次找到a字符的位置下标,不等于-1则代表包含,startIndex可传入从哪个下标开始查找
- 排序
array.sort(function(a, b) {
return a - b;
})
从大到小:a - b
从小到大:b - a
- 字符串中是否包含
str.indexOf('a', startIndex):首次出现a字符的位置,不等于-1则代表存在,startIndex可传入从哪个下标开始查找
str.search('a'):str中是否包含a字符,不等于-1则代表存在
- 截取数组
array.slice(startIndex, endIndex):前闭后开[0, 3)只会截取下标0,1,2的元素
- 数组倒序
array.reverse():[1,2,3] => [3,2,1]
- 数组分割
array.split('/'):在实现路由匹配coding中有使用到test/:id/detail/:name
- 数组过滤
array.filter(e => ...):是过滤出条件为true的集合
- 数组替换
array.splice(startIndex, deleteNum, newItem1, newItem2, ...)
- 字符串下标为n的字符
str.charAt(n):'HELLO'.chart(2)=> L
- 数组内元素校验
arr.every(e => ...):校验数组中每个元素是否都通过function校验,全部通过返回true,反之返回false。
- 数组累加器
arr.reduce():从左往右遍历累加
arr.reduceRight():从右往左遍历累加(在实现compose函数的时候可以用) mdn
- 截取数组的最后一个,删除数组的最后一个
const lastItem = arr.pop():删除数组的最后一个并返回(会改变原数组)
const lastItem = arr.slice(-1):截取数组的最后一个
- 常用数据结构
// 待补充
持更...