前端面经整理【1】

注:纯手打,如有错误欢迎评论区交流!

转载请注明出处:https://blog.csdn.net/testleaf/article/details/148628299

编写此文是为了更好地学习前端知识,如果损害了有关人的利益,请联系删除!

本文章将不定时更新,敬请期待!!!

欢迎点赞、收藏、转发、关注,多谢!!!

目录

【1】数组转树

数组转树是前端面试中的高频手写题,通常用于处理扁平化的数据结构(如菜单列表、评论嵌套等)。

输入示例​​:

javascript 复制代码
const arr = [
  { id: 1, name: '部门A', parentId: 0 },
  { id: 4, name: '部门D', parentId: 2 },
  { id: 2, name: '部门B', parentId: 1 },
  { id: 3, name: '部门C', parentId: 1 },
];

输出示例:

javascript 复制代码
[
  {
    id: 1,
    name: '部门A',
    children: [
      { 
        id: 2, 
        name: '部门B',
        children: [{ id: 4, name: '部门D', children: [] }]
      },
      { id: 3, name: '部门C', children: [] }
    ]
  }
]

实现方法1【递归法(O(n²))​】:

javascript 复制代码
// 原始数据:扁平化的部门列表,每个部门有 id、name 和 parentId
const arr = [
  { id: 1, name: '部门A', parentId: 0 },  // 根节点(parentId=0)
  { id: 4, name: '部门D', parentId: 2 },  // 部门D 的父节点是部门B(id=2)
  { id: 2, name: '部门B', parentId: 1 },  // 部门B 的父节点是部门A(id=1)
  { id: 3, name: '部门C', parentId: 1 },  // 部门C 的父节点是部门A(id=1)
];
/**
 * 将扁平数组转换为树形结构
 * @param {Array} items - 扁平数组
 * @param {number} parentId - 当前层级的父节点ID(默认0表示根节点)
 * @returns {Array} - 树形结构数组
 */
const change = (items, parentId = 0) => {
  let result = []; // 当前层级的节点数组
  // 遍历所有部门,找出属于当前 parentId 的子部门
  items.forEach(item => {
    if (item.parentId == parentId) {
      // 递归查找当前部门的子部门(以当前部门的 id 作为父ID)
      item.children = change(items, item.id);
      // 删除 parentId 字段(可选,树形结构不再需要该字段)
      delete item.parentId;
      // 将当前部门添加到结果中
      result.push(item);
    }
  });
  return result; // 返回当前层级的部门树
};
// 打印完整的树形结构({depth:null} 表示展开所有层级)
console.dir(change(arr), { depth: null });

实现方法2【哈希表 + 一次遍历(O(n))​​】:

javascript 复制代码
// 原始数据:扁平化的部门列表,每个部门有 id、name 和 parentId
const arr = [
  { id: 1, name: '部门A', parentId: 0 },  // 根节点(parentId=0)
  { id: 4, name: '部门D', parentId: 2 },  // 部门D 的父节点是部门B(id=2)
  { id: 2, name: '部门B', parentId: 1 },  // 部门B 的父节点是部门A(id=1)
  { id: 3, name: '部门C', parentId: 1 },  // 部门C 的父节点是部门A(id=1)
];
/**
 * 使用哈希表将扁平数组转换为树形结构(O(n) 时间复杂度)
 * @param {Array} items - 扁平数组
 * @returns {Array} - 树形结构数组
 */
const change = (items) => {
  // 1. 初始化哈希表:存储所有节点的引用,方便后续通过 id 快速查找
  const map = {};
  // 2. 结果数组:存储所有根节点(parentId=0 的节点)
  const result = [];
  // 第一次遍历:将所有节点存入哈希表,并初始化 children 数组
  for (const item of items) {
    map[item.id] = {
      ...item,          // 浅拷贝原对象(避免直接修改原数据)
      children: []      // 初始化子节点数组
    };
  }
  // 第二次遍历:构建树形结构
  for (const item of items) {
    // 获取当前节点在哈希表中的引用
    const node = map[item.id];
    if (item.parentId === 0) {
      // 如果是根节点(parentId=0),直接加入结果数组
      delete node.parentId;  // 可选:删除冗余的 parentId 字段
      result.push(node);
    } else {
      // 如果不是根节点,找到父节点并挂载到父节点的 children 中
      if (map[item.parentId]) {
        delete node.parentId;  // 可选:删除冗余的 parentId 字段
        map[item.parentId].children.push(node);
      }
      // 注:如果父节点不存在(数据错误),这里会静默忽略,实际业务中可以报警告
    }
  }
  return result;
};
// 打印完整的树形结构({depth:null} 表示展开所有嵌套层级)
console.dir(change(arr), { depth: null });

思考:

为什么 map[item.parentId].children.push(node) 会影响所有相关节点?

  • JavaScript 的对象是引用类型
    • map[item.id] = {...item, children: []} 创建的是 浅拷贝(只拷贝第一层属性)。
    • map 中存储的是对节点的引用,nodemap[item.id] 指向同一个对象。
  • ​​children 数组的共享
    • 当执行 map[item.parentId].children.push(node) 时:
      • map[item.parentId] 是父节点的引用。
      • node 是子节点的引用。
      • 父子节点通过引用关联,修改会同步反映到所有引用该对象的地方。
相关推荐
LYFlied2 分钟前
TypeScript 常见面试问题
ubuntu·面试·typescript
努力学算法的蒟蒻16 分钟前
day35(12.16)——leetcode面试经典150
算法·leetcode·面试
二两锅巴34 分钟前
📺 无需Electron!前端实现多显示器浏览器窗口精准控制与通信
前端
炸土豆38 分钟前
防抖节流里的this传递
前端·javascript
用户40993225021239 分钟前
Vue3中动态样式数组的后项覆盖规则如何与计算属性结合实现复杂状态样式管理?
前端·ai编程·trae
山璞41 分钟前
Flutter3.32 中使用 webview4.13 与 vue3 项目的 h5 页面通信,以及如何调试
前端·flutter
努力早日退休44 分钟前
Antd Image标签父元素会比图片本身高几个像素的原因
前端
林希_Rachel_傻希希44 分钟前
手写Promise--教学版本
前端·javascript·面试
ETA81 小时前
`console.log([1,2,3].map(parseInt))` 深入理解 JavaScript 中的高阶函数与类型机制
前端·javascript
呼叫69451 小时前
图片列表滚动掉帧的原因分析与解决方案
前端