前端深度面试题
- Q: JS的内存泄露如何检测?场景有哪些?
- Q: 浏览器和 nodejs 的事件循环有什么区别
- Q: vdom真的很快吗?
- Q: 遍历一个数组用 for 和 forEach 哪个更快?
- Q: nodejs 如何开启进程,进程如何通讯?
- Q: js-bridge的实现原理
- Q: requestIdleCallback 和 requestAnimationFrame 有什么区别?
- Q: Vue 每个生命周期都做了什么?
- Q: Vue2 Vue3 和 React 三者的 diff 算法有什么区别?
- Q: Vue-router 的 MemoryHistory是什么?
- Q: React 事件和 DOM 事件的区别?
- Q: Vue 和 React 函数组件的区别?
- Q: Vue Composition API 和 React Hooks 的区别?
Q: JS的内存泄露如何检测?场景有哪些?
垃圾回收(GC)
- 函数执行完之后,函数内部的变量会变成垃圾被回收掉
- 如果变量被外部引用,就不是垃圾不会被回收
- 闭包中的数据不会被回收
- 内存泄露是非预期的垃圾回收不了
垃圾回收的算法
- 引用计数(之前)
- 标记清除(现在)
定期从JS的根(window)去遍历各个属性,能得到的数据将被保留,得不到就删除
检测内存变化
- performance,勾选 Memory,观察 HEAP 的内存变化
- 一直上升状态则是内存泄露,是锯齿状态则不是内存泄露
内存泄露场景
- 被全局变量、函数引用,组件销毁时未清除
- 被全局事件、定时器引用,组件销毁时未清除
- 被自定义事件引用,组件销毁时未清除
WeakMap 和 WeakSet
- 弱引用
- key 只能是引用类型,value 可以是值类型
- 通过 get 获取数据,没有 foreach 和 size,无法控制自己内部的数据,随时可能被清除
Q: 浏览器和 nodejs 的事件循环有什么区别
浏览器-宏任务和微任务
- 宏任务,如 setTimeout setInterval 网络请求
- 微任务,如 promise async/await
- 微任务在下一轮DOM渲染之前执行,宏任务在之后执行
渲染是指DOM绘制到页面上,肉眼可见,并不是指js对象(dom操作还是正常执行)
所以,不管是宏任务还是微任务,都不会干预js对dom结构的操作
nodejs 的异步
- 与浏览器很像
- 区别是,它的宏任务和微任务,分不同的类型,有不同的优先级
nodejs 宏任务类型和优先级 - 由高到低
- Timers - setTimeout setInterval
- I/O callbacks - 处理网络、流、TCP 的错误回调
- Idle,prepare - 闲置状态(nodejs内部使用)
- Poll 轮询 - 执行 poll 中的 I/O 队列
- Check 检查 - 存储 setTmmediate 回调
- Close callbacks - 关闭回调,如 socket.on(‘close’)
nodejs 微任务类型和优先级 - 由高到低
- promise、async/await、process.nextTick
- process.nextTick 优先级最高
nodejs event loop
- 执行同步代码
- 执行微任务
- 按顺序执行6个类型的宏任务(每当宏任务开始执行之前,先执行当前的微任务)
注意事项
- node 新版本推荐使用 setImmediate 代替 process.nextTick
- process.nextTick 优先级太高,尽量使用低优先级的api去执行异步
Q: vdom真的很快吗?
vdom
- Virtual DOM,虚拟 DOM
- 用 JS 对象模拟 DOM 节点数据
- 由 React 最先推广使用
答案
- vdom 并不快,JS直接操作DOM才是最快的
- 但“数据驱动视图”要有合适的技术方案,不能全部 DOM 重建
- vdom 就是目前最合适的技术方案(并不是因为它快,而是合适)
Q: 遍历一个数组用 for 和 forEach 哪个更快?
- for 更快
- forEach 每次都要创建一个函数来调用,而 for 不会创建函数
- 函数需要独立的作用域,会有额外的开销
- 越“低级”的代码,性能往往越好
Q: nodejs 如何开启进程,进程如何通讯?
进程 process VS 线程 thread
- 进程,OS 进行资源分配和调度的最小单位,有独立的内存空间
- 线程,OS 进行运算调度的最小单位,共享进程内存空间
- JS 是单线程的,但可以开启多进程执行,如 WebWorker
为何需要多进程?
- 多核 CPU,更适合处理多进程
- 内存较大,多个进程才能更好的利用(单进程有内存上限)
- “压榨”机器资源,更快,更节省
使用 child_process.fork 方式
主进程:
子进程:
使用 cluster.fork 方式
child_process.fork VS cluster.fork
- child_process:用于计算量比较大的情况,可以开启子进程去单独处理
- cluster:用于开启多个服务的场景
Q: js-bridge的实现原理
什么是 JS Bridge
- JS 无法直接调用 native API
- 需要通过一些特定的“格式”来调用
- 这些“格式”统称 JS-Bridge,例如微信 JSSDK
JS Bridge 的常见实现方式
- 注册全局 API
App 往 webview 的 window 上直接注册API,不适合处理异步的情况 - URL Scheme (自造协议)
处理异步:h5 页面利用 iframe 的src发起请求,app拦截到请求,如果是http(https)协议正常请求,如果是 URL Scheme ,app 识别到是内部协议,则由app层处理返回,触发iframe onload
Q: requestIdleCallback 和 requestAnimationFrame 有什么区别?
由 React fiber 引起的关注
- 组件树(vdom)转换为链表,可分段渲染
- 遇到高优任务时暂停渲染,去执行其他高优任务,空闲时再继续渲染
- 如何判断空闲?—— requestIdleCallback
区别
- requestAnimationFrame 每次渲染完都会执行,高优
- requestIdleCallback 空闲时执行,低优
宏任务还是微任务?
- 两者都是宏任务
- 要待dom渲染完才执行,肯定是宏任务
Q: Vue 每个生命周期都做了什么?
- beforeCreate
创建一个空白的 Vue 实例
data method 尚未被初始化,不可使用 - created
Vue 实例初始化完成,完成响应式绑定
data method 都已经初始化完成,可调用 - beforeMount
编译模板,调用 render 生成 vdom - mounted
完成dom渲染
组件创建完成
开始由“创建阶段”进入“运行阶段” - beforeUpdate
data 发生变化之后
准备更新dom - updated
data 发生变化之后,且dom更新完成 - beforeUnmount
组件进入销毁阶段(尚未销毁,可正常使用)
可移除、解绑一些全局事件、自定义事件 - unmounted
组件被销毁了
所有子组件也被销毁了
keep-alive 组件
- activated 缓存组件被激活
- deactivated 缓存组件被隐藏
vue 什么时候操作 DOM 比较合适?
- mounted 和 updated 都不能保证子组件全部挂载完成
- 使用 $nextTick 操作 DOM
ajax 应该在哪个生命周期?
- 两个选择:created 和 mounted
- 推荐:mounted
Vue3 Composition API 生命周期有何区别?
- 用 setup 代替了 beforeCreate 和created
- 使用 hooks 函数的形式,如 mounted 改为 onMounted
Q: Vue2 Vue3 和 React 三者的 diff 算法有什么区别?
- React diff - 仅右移
- Vue2 双端比较
- Vue3 最长递增子序列
Q: Vue-router 的 MemoryHistory是什么?
Vue-router 的三种模式
- Hash
- WebHistory
- MemoryHistory(v4 之前叫做 abstract history)
没有路由变化、也不刷新页面、没有前进后退
Q: React 事件和 DOM 事件的区别?
- React 事件统一挂载到 root 节点(React 17之前是 document)
- React 使用”合成事件“来模拟 DOM 事件(捕获、冒泡等)
- 合成事件,可以跨平台,不仅仅用于 DOM
Q: Vue 和 React 函数组件的区别?
- Vue 函数组件没有实例和生命周期,比较简单
- React 函数组件 + Hooks 可以实现完备的功能(代码组织和代码维护也比类组件更好)
- Vue Composition API不能用于 Vue 函数组件
Q: Vue Composition API 和 React Hooks 的区别?
Vue Composition API
- 有组件实例,依赖组件实例,生命周期只会执行一次
React Hooks
- 无组件实例,纯函数,每次更新生命周期都会执行
- 底层通过链表实现的,不能在for if 中使用hooks