面试题记录-2024
Sep 25, 2024
面试篇
一、try...catch
只能捕捉到同步执行代码块中的错误
例子
1、异步方法情况
javascripttry { setTimeout(() => { throw new Error('err') }, 200); } catch (err) { console.log(err); } <--正确方案--> new Promise((resolve, reject) => { setTimeout(() => { try { throw new Error('err'); } catch (err) { reject(err); } }, 200); }) .then(() => { // 正常执行时的处理逻辑 }) .catch((err) => { console.log(err); // 这里会捕捉到错误 });
2、promise链下
javascripttry { Promise.resolve().then(() => { throw new Error('err') }) } catch (err) { console.log(err); } <--正确方案--> // 方法一 Promise.resolve() .then(() => { throw new Error('err'); }) .catch((err) => { console.log(err); // 这里会捕捉到错误 }); // 方法二 async function handleError() { try { await Promise.resolve().then(() => { throw new Error('err'); }); } catch (err) { console.log(err); // 这里会捕捉到错误 } } handleError();
二、在 Vue2.x 中如何检测数组的变化
使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
三、vue2、vue3 diff算法
Vue.js 是一个流行的 JavaScript 框架,用于构建用户界面。在 Vue 的两个主要版本(Vue 2.x 和 Vue 3.x)中,虚拟 DOM(Virtual DOM)和其 diff 算法是非常重要的特性。diff 算法用于高效地更新 DOM,保持界面和状态的一致。下面我们来详细探讨一下这两个版本中的 diff 算法。
Vue 2.x 的 diff 算法
Vue 2.x 使用的是基于 Snabbdom 的 diff 算法,主要特点如下:
- 双端比较:从新旧节点的两端开始进行比较(头头、头尾、尾头、尾尾),尽可能快地找到不需要移动的节点。

- 标记静态节点:在编译阶段,Vue 2.x 会尝试标记出静态节点,这样在更新过程中可以跳过这些不变的节点,从而提高性能。
- SameNode 函数:判断两个节点是否相同的核心函数。节点相同的条件包括:
- key 相同
- 标签相同
- 都是注释节点
- 都是文本节点并且内容相同
- 递归更新:当两个节点被判断为相同时,进行递归更新。对于组件节点,会调用组件的钩子函数进行处理。
具体的算法步骤可以概述为:
- 比较两个节点,如果节点不同,则直接替换。
- 如果节点相同,则递归比较子节点。
- 使用双端比较法减少移动节点的次数。
Vue 3.x 的 diff 算法
Vue 3.x 进行了大量的重构,引入了全新的编译器和运行时,其中 diff 算法也得到了显著改进。主要特点如下:
- 块级优化:在编译阶段,Vue 3.x 会将节点划分为静态和动态的“块”,并使用静态标记(
patchFlag
)来标记哪些部分需要更新,从而减少不必要的比较。 - 缓存节点:Vue 3.x 会缓存一些节点的状态,从而避免不必要的重新创建和销毁。
- 更高效的 SameNode 函数:进一步优化了 SameNode 函数的逻辑,使得比较更为高效。
- 最长递增子序列:使用了最长递增子序列(Longest Increasing Subsequence, LIS)算法来优化节点的移动操作,这样可以在节点移动时,最大限度地减少实际 DOM 操作。
具体的算法步骤可以概述为:
- 比较根节点,如果根节点不同,则直接替换。
- 如果根节点相同,则根据静态标记(
patchFlag
)和节点类型(组件、文本、元素等)进行不同的处理。 - 对于子节点,使用最长递增子序列算法减少移动操作。
- 根据缓存的状态和静态标记,跳过不必要的比较和更新。
举例说明
假设有如下虚拟 DOM 结构:
html<ul> <li key="a">A</li> <li key="b">B</li> <li key="c">C</li> <li key="d">D</li> </ul>
我们将其更新为:
html<ul> <li key="b">B</li> <li key="a">A</li> <li key="d">D</li> <li key="c">C</li> </ul>
Vue 2.x
Vue 2.x 的算法会逐一比较每个节点:
- 比较
<li key="a">
和<li key="b">
,不同。 - 比较
<li key="d">
和<li key="c">
,不同。 - 最终需要移动和重新插入节点。
Vue 3.x
Vue 3.x 的算法会利用静态标记和最长递增子序列算法:
- 比较根节点
<ul>
,相同。 - 比较子节点,发现
<li key="b">
和<li key="a">
需要交换位置,但不需要重新创建。 - 利用最长递增子序列算法,确定最少的 DOM 操作次数。
总结
Vue 2.x 和 Vue 3.x 的 diff 算法在基本原理上相似,但 Vue 3.x 进行了更深入的优化,特别是在静态标记和节点移动方面,大大提高了性能和效率。这使得 Vue 3.x 在处理大型应用时,能够更加高效地更新 DOM,提升用户体验。
四、数组去重
1、原始类型
new Set
2、引用类型
根据对象某个key去重 [...new Map(arr.map(i=>[i.key,i])).values]