浅窥前端技能知识深度
本章总结前端技术深度相关面试题。包括 JS 内存相关,Vue 相关原理。 大厂的技术评级时,技术深度是重要的参考标准。
JS内存泄漏如何检测?场景有哪些?
垃圾回收机制
什么是垃圾回收机制?
有些数据不再被引用时,JS的垃圾回收机制会清除该数据。
引用树机制(之前的算法)
每当数据被引用时,引用树就会添加一条引用信息,接下来只需要判断引用树的长度即可。
但引用树机制有个问题,当出现循环引用,如
function fn(){
const obj1 = {}
const obj2 = {}
obj1.a = obj2
obj2.a = obj1
}
函数执行完引用树也不会归零,产生内存泄漏。
标记清除(现在的算法)
定期从window遍历所有属性,如果能得到保留,不能得到删除。
闭包是内存泄漏么?
内存泄漏是指产生了意料之外的不回收的垃圾。
闭包严格意义上不是内存泄漏,是人为指定的意料之中的。
如何检测内存泄漏?
chrome调试工具中的profromance中可以录制内存快照,包括内存,CPU,FPS等信息,观察曲线变化就可以检查是否有大量的内存泄漏。
内存泄漏的场景(以VUE为例)
- 被全局变量,函数引用,组件销毁时未清除
- 被全局事件,定时器引用,组件销毁时未清除
- 被自定义事件引用,组件销毁时未清除
在beforeUnmount删除所有window的监听器,删除所有的定时器,计时器,自定义事件。
WeakMap WeakSet弱引用
先看个使用普通对象的例子
const data = {}
function fn1 () {
const obj = { x: 100 }
data.obj = obj
}
fn1()
在data中可以查找到obj的引用,根据标记清除法,不会回收obj
如果使用weakMap,里面存储的内容有引用也可能被回收。
const wMap = new WeakMap()
function fn1 () {
const obj = { x: 100 }
wMap.set(obj, 100) //WeakMap的key只能是引用类型
}
fn1()
- WeakMap的key只能是引用类型
- weakMap不能被循环,没有size(它自己也不确定自己的内容是否被回收了
- 当某个场景,你想要回收某个变量,但又不想被其他引用干扰,可以使用weakMap建立引用关系。
浏览器和nodejs的事件循环有什么区别?
单线程和异步
- JS是单线程的(无论是浏览器还是node)
- 浏览器JS和DOM渲染公用一个线程
宏任务和微任务
- 宏任务:如setTImeout setInterval 网络请求
- 微任务:如promise async/await
- 微任务在下一轮DOM渲染前执行,宏任务在之后进行
Nodejs的异步
- nodejs同样使用ES语法,也是单线程,也需要异步
- 异步也分为:宏任务+微任务
- 但是,它的宏任务和微任务,分不同类型,有不同优先级。
Nodejs宏任务的优先级
- Timer -setTImeout,setInterveal
- I/O callback - 处理网络 流 TCP的错误回调
- Idle,prepare - 闲置状态
- Poll轮询,执行poll中的I/O队列
- Ckeck检查,存储SetImmediate回调
- CloseCallbacks 关闭回调
Nodejs微任务优先级
- process.nextTick优先级最高
- 其他都相同
VODM真的很快么?
- Virtual DOM 虚拟DOM
- 用JS对象模拟DOM节点的数据
为什么要使用前端框架?
- 组件化
- 数据视图分离,数据驱动视图
- 只关注业务数据,而不在关心DOM变化
VODM真的比JS操作DOM快么?
- VDOM : 1. data变化 2.vnode diff 3.更新DOM
- JS:直接操纵
VOMD快只是相较于全局更新DOM,它节省了一些无需更新的视图。比JS直接操纵慢。
但他实现了数据视图分离。
for和foreach谁更快 为什么?
- for更快
- forEach每次都要创建一个函数来调用,而for不会
- 函数需要独立的作用域,会有额外开销
Vue的生命周期每一步都做了什么?
beforeCreate
- 创建一个空白的Vue实例
- data method尚未被初始化,不可使用
created
- vue实例初始化完成,完成响应式绑定
- data method都已经初始化完成,可调用
- 尚未开始渲染模版
beforeMount
- 编译模版,调用render生成vdom
- 还没有开始DOM渲染
mounted
- 完成DOM渲染
- 组件创建完成
- 开始由“创建阶段”进行运行阶段
beforeUpdate
- 数据更新
- DOM还未更新
updated
- data变化,DOM更新完成
- 不要再uptaded修改data,会造成死循环
beforeUnmount
- 组件进入销毁阶段
- 可以移除,解绑一些全局事件,自定义事件
unmounted
- 组件被销毁了
- 所有子组件也被销毁了
在使用keepalive也有两个钩子函数,在被激活和被隐藏触发。(avticed,deacticed)
Vue什么时候操作DOM比较合适?
- mounted和updated都不能保证子组件全部挂载完成
- 使用$nextTick渲染DOM
Vue2 Vue3 React的diff算法有什么不同?
介绍diff算法
- diff算法很早就有
- diff算法应用广泛,如github上的pull request中的代码diff
- 如果要严格diff两棵树,时间复杂度O(n*3) 不可用
Tree diff的优化
- 只比较同一层级,不跨级比较
- tag 不同则删掉重建(不再去比较内部的细节)
- 子节点通过 key 区分( key 的重要性)
- 时间复杂度O(n)
React diff - 仅右移
- 如果比较后发现新节点在左面,不移动,新节点在右面就右移
Vue2 双端比较
维护四个指针,双端向中心移动。
Vue3 最长递增子序列
记录下不同部分的节点的序列,选取最长递增子序列,子序列不做操作,操作其他序列。
Vue路由的第三种模式?
Vue-router实际上支持三种模式
- hash
- WebHsitory
- MemoryHsitory(abstract history)
区别于其他两种模式,MemoryHsitory不支持前进后退,也不会改变地址栏,也不会刷新,只会改变页面的内容。