for 和 forEach 哪个更快?
for 更快。 for 直接在当前函数中执行,forEach 每次都要新创建一个函数。 函数有单独的作用域和上下文,创建函数需要额外的开销。
JS 垃圾回收、内存泄漏
正常情况下,一个函数执行完,其中的变量都会是会 JS 垃圾回收。但某些情况下(闭包,循环引用),变量是销毁不了的,因为可能会被再次使用。
垃圾回收算法:
- 引用计数
早起的垃圾回收算法,以“数据是否被引用”来判断要不要回收。但这个算法有一个缺陷 —— 循环引用无法进行垃圾回收。 - 标记清除
现代浏览器使用“标记-清除”算法。根据“是否是否可获得”来判断是否回收。 定期从根(即全局变量)开始向下查找,能找到的即保留,找不到的即回收。循环引用不再是问题。
变量销毁不了,不一定就是内存泄漏,要看是否符合开发者预期。 闭包的变量是无法被垃圾回收的。但闭包不是内存泄漏,因为它是符合开发者预期的,即本身就这么设计的。而内存泄漏是非预期的。 检测内存变化:使用 Chrome devTools Performance。
内存泄漏的场景:
- 被全局变量、函数引用,组件销毁时未清除。
- 被全局事件、全局定时器引用,组件销毁时未清除。
- 被自定义事件引用,组件销毁时未清除。
- 闭包(无定论)
扩展:WeakMap WeakSet 弱引用,不会影响垃圾回收。
js-bridge 原理
JS 无法直接调用 app 的 API ,需要通过一种方式 —— 通称 js-bridge ,它也是一些 JS 代码。 js-bridge实现的两种方式:
- 注册全局API。这种方式一般都是同步的。
- 劫持 url scheme。自造一个协议标准
'my-app-name://api/getVersion' ,让app识别这个协议标准做拦截,不要发送网络请求。
Vue 生命周期
- beforeCreate
初始化一个空的 Vue 实例,data methods 等尚未被初始化,无法调用。 - created
Vue 实例初始化完成,data methods 都已初始化完成,可调用。 但尚未开始渲染模板。 - beforeMount
编译模板,调用 render 函数生成 vdom ,但还没有开始渲染 DOM - mounted
渲染 DOM 完成,页面更新。组件创建完成,开始进入运行阶段。 - beforeUpdate
在数据发生改变后,DOM 被更新之前被调用。这里适合在现有 DOM 将要被更新之前访问它,比如移除手动添加的事件监听器。 - updated
在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。 注意,尽量不要在 updated 中继续修改数据,否则可能会触发死循环。 - onActivated
被 keep-alive 缓存的组件激活时调用。 - onDeactivated
被 keep-alive 缓存的组件停用时调用。 - beforeUnmount
组件进入销毁阶段。 卸载组件实例后调用,在这个阶段,实例仍然是完全正常的。 移除、解绑一些全局事件、自定义事件,可以在此时操作。 - unmounted
卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
连环问:如何正确的操作 DOM mounted 和 updated 都不会保证所有子组件都挂载完成,如果想等待所有视图都渲染完成,需要使用 $nextTick 。
连环问:ajax 放在哪个生命周期合适? 一般有两个选择:created 和 mounted ,建议选择后者 mounted 。
连环问:vue3 Composition API 生命周期有何不同
setup 代替了 beforeCreate 和 created - 生命周期换成了函数的形式,如
mounted -> onMounted
vdom 真的很快吗
Vue React 等框架的价值在于组件化、数据视图分离。 vdom,Virtual DOM,虚拟 DOM ,即用 JS 对象模拟 DOM 数据。
- jquery 时代:直接修改 DOM
- 框架时代:生成 vdom ,进行 diff 运算 --> 修改 DOM
所以,vdom 并不比 DOM 操作更快(反而更慢,它做了 JS 运算),它只是在某个特定的场景下,无法做到精准 DOM 修改时,一个更优的选择。
Vue2、Vue3、React 的diff 算法有什么区别
- React diff 特点 - 仅向右移动
- Vue2 diff 特点 - 双端比较
- Vue3 diff 特点 - 最长递增子序列
Vue React diff 不是对比文字,而是 vdom 树,即 tree diff 。对比虚拟dom。 传统的 tree diff 算法复杂度是 O(n^3) ,算法不可用。
Vue React 都是用于网页开发,基于 DOM 结构,对 diff 算法都进行了优化(或者简化)。最终把时间复杂度降低到 O(n) ,生产环境下可用。这一点 Vue React 都是相同的。
- 只在同一层级比较,不跨层级 (DOM 结构的变化,很少有跨层级移动)
- tag 不同则直接删掉重建,不去对比内部细节(DOM 结构变化,很少有只改外层,不改内层)
- 子节点通过 key 区分
连环问: Vue和React为何循环时必须使用key
- vdom diff算法会根据key判断元素是否要删除
- 匹配了key,则只移动元素-性能较好
- 未匹配key,则删除重建-性能较差
Vue-router 模式
- hash - 使用 url hash 变化记录路由地址
- history - 使用 H5 history API 来改 url 记录路由地址
- MemoryHistory / abstract - 不修改 url ,路由地址在内存中,但页面刷新会重新回到首页。(没法前进后退)
浏览器和 nodejs 事件循环的区别
-
单线程和异步 JS 是单线程的,浏览器中 JS 和 DOM 渲染线程互斥。“异步”是单线程的解决方案。 -
宏任务和微任务 浏览器端异步的 API 有很多
- 宏任务:setTimeout 网络请求
- 微任务:promise、async、await
两者表面的区别: 第一,微任务比宏任务更快执行。 第二,微任务在 DOM 渲染前执行,而宏任务在 DOM 显示后(即真正显示到页面上,肉眼可见)执行。 -
浏览器事件循环
- 执行同步代码
- 将异步函数放入队列
- 执行微任务队列
- 网页渲染
- 执行宏任务队列
-
nodejs 异步 nodejs 也是用了 V8 引擎和 ES 语法,所以也有同步、异步,异步也分宏任务、微任务。但nodejs 的宏任务是分了如下类型,nodejs 事件循环中宏任务需要按照这个顺序来执行。
- timers(计时器) - 执行
setTimeout 以及 setInterval 的回调 - I/O callbacks - 处理网络、流、TCP 的错误回调
- idle, prepare — 闲置阶段 - node 内部使用
- poll(轮循) - 执行 poll 中的 I/O 队列,检查定时器是否到时间
- check(检查) - 存放
setImmediate 回调 - close callbacks - 关闭回调,例如
socket.on('close') -
nodejs 事件循环
- 执行同步代码
- 执行
process.nextTick 和微任务(前者优先级更高) - 按照顺序执行 6 个类型的宏任务
- …
-
总结
- 浏览器和nodejs的事件循环流程基本相同。
- nodejs宏任务和微任务分类型,有优先级。
nodejs 进程之间如何通讯
进程,是操作系统进行资源调度和分配的基本单位,有独立的内存空间。 线程,是操作系统进行运算调度的最小单位。一个进程可以包含多个线程,多线程之间可共用进程的内存数据。 JS 是单线程的,但可以开启多进程执行。 现代服务器都是多核 CPU ,适合同时处理多进程。但操作系统对于一个进程的内存分配是有上限的(2G),所以多进程才能充分利用服务器内存。
- 可使用
child_process.fork 和 cluster.fork 开启子进程 - 使用
send on 传递消息
|