内容提要
作者分享多家公司前端面试经历,考察手写深克隆、transform、reduce、Monkey异步调度、栈实现队列、parseHtml、循环依赖检测等,并点评structuredClone与JSON克隆差异、Vue2数组监听原理,同时吐槽部分面试官不专业、流程拖沓。
延伸解读
深克隆:structuredClone 与 JSON 方案的取舍
文章指出 JSON.parse(JSON.stringify(value)) 无法处理循环引用,且会丢失 Date、Set、Map 等类型。structuredClone 能解决这些问题,但性能并非绝对占优:小对象 JSON 更快,中等对象因 JS 引擎而异,超大对象 structuredClone 更快。实际选择时需权衡数据类型支持与性能,不能一概认为 structuredClone 全面胜出。
面试手写题:思路与代码风格同样重要
从 transform、reduce 到 Monkey 调度,这些题目本身难度不高,但面试官往往借此观察代码风格和边界处理。例如 reduce 不传初始值时应取第一个元素,Monkey 题需用队列保证异步顺序。文章提醒,平时写代码习惯写初始值的人,可能忽略这些边界,面试前应有意识补全。
Vue2 数组监听:原型链中间层的设计
作者最初误答为直接改 Array.prototype,实际 Vue2 是在数组原型与 Array.prototype 之间插入一个中间原型,重写 push、pop 等七个方法,并在其中调用 ob.dep.notify() 通知更新。这样既避免污染原生原型,又能让被 Vue 管理的数组实例通过原型链继承重写方法,是响应式实现的关键细节。
面试体验:专业度影响发挥与判断
文章多次提到面试官专业度差异:有的会指出代码问题并引导修改,有的只笼统说“有问题”却不指明位置,导致候选人无法 debug 而陷入尴尬。流程拖沓、面评影响其他部门机会等情况也有出现。这提示求职者,面试是双向选择,遇到不专业的流程不必过度自我怀疑,保持手感与心态更重要。
Q&A
前端面试中手写深克隆时,structuredClone和JSON.parse(JSON.stringify())有什么区别?
structuredClone能处理循环引用和Date、Set、Map等类型,而JSON方法不支持循环引用,且只能处理JSON支持的类型(object、array、number、string、boolean、null)。性能上,小对象JSON更快,超大对象structuredClone更快。
如何实现一个transform函数,将带点号的键转换为嵌套对象?
可以使用set函数逐层创建嵌套对象。遍历输入对象的每个键值对,将键按点号分割成路径数组,然后从根对象开始,逐层检查并创建不存在的对象,最后在路径末端赋值。
如何用栈实现队列,并支持常数时间获取最小值?
使用一个队列作为底层存储。push时入队并更新最小值;pop时通过循环将队列元素重新入队,同时记录最小值,最后一个元素出队作为栈顶;printMin直接返回维护的最小值。
Vue2中如何监听数组的变化?
Vue2通过创建一个中间原型对象,该对象继承自Array.prototype,并重写push、pop、shift、unshift、splice、sort、reverse等方法。在重写的方法中调用原始方法后,通过__ob__通知依赖更新,然后将数组实例的原型指向这个中间原型。
如何实现一个异步求和函数,使用回调风格的add方法?
先将add方法promise化,然后使用async函数和for...of循环,依次await每个参数的累加结果。例如:const _add = (a, b) => new Promise(resolve => add(a, b, resolve)); async function sum(...args) { let result = 0; for (const arg of args) { result = await _add(result, arg); } return result; }
如何检测依赖树中的循环依赖并去重?
使用深度优先搜索(DFS),维护当前路径的Set。当遇到已在路径中的包时,记录循环。为避免重复,使用一个全局Set记录已发现循环中的包,遍历时跳过这些包。