0415美团面试题目详解
基础知识型,基础知识!!!
margin-top:100%(基于父元素宽度
)
“margin-top: 100% 表示元素的上外边距为父元素宽度的 100%。例如,若父元素宽 300px,则上边距为300px。需注意,CSS 中垂直方向的百分比边距(如 margin-top/margin-bottom)均基于
父元素宽度计算
,而非高度
。这在响应式布局中可用于动态调整间距,但需确保父元素宽度明确
,避免布局错乱
。”
该属性会使当前元素相对于父容器向下偏移其宽度的完整值,可能导致元素被“顶出”父容器外,甚至影响布局流(如重叠或溢出
在 React 中,不建议将数组索引(index
)作为列表渲染的 key
值,但在某些特定场景下可以谨慎使用。以下是原理分析、问题总结及适用场景的对比:
React中key为啥不能为索引
一、为什么不推荐使用索引作为 key
?
1. 性能问题
当列表发生增删或顺序调整时,索引会动态变化。React 通过 key
识别新旧元素,若使用索引:
• 重新渲染浪费:元素的实际内容未变,但索引变化会导致 React 误判为新增/删除元素,触发不必要的 DOM 操作。
• 组件实例重建:React 无法复用原有组件实例,需重新创建,影响性能(如输入框组件会被重置)。
2. 状态错误
若列表项包含状态(如输入框、复选框):
• 索引变化导致状态错乱:例如删除列表第一项后,原第二项的索引变为 1,其状态会被错误地保留给新位置的元素。
• 动画/过渡失效:React 无法正确追踪元素移动,导致动画效果异常。
3. 源码层面的协调问题
React 的协调算法(Reconciliation)通过 key
匹配新旧元素:
• 索引不唯一:同一层级下的兄弟元素必须具有唯一 key
,但索引仅保证局部唯一,跨层级可能重复。
• 虚拟 DOM 对比失效:索引变化会破坏 React 的复用逻辑,触发全量更新而非最小化更新。
二、哪些场景不适用索引作为 key
?
1. 动态列表
• 列表项可能被增删、排序或过滤(如待办事项列表)。
• 后果:索引动态变化,导致性能下降和状态错误。
2. 含状态的列表项
• 列表项包含输入框、复选框、动画等需要保持状态的组件。
• 后果:状态与错误元素关联,用户体验受损。
3. 复杂数据结构
• 列表项为嵌套组件,或数据源可能包含重复项。
• 后果:索引无法唯一标识元素,导致渲染错误。
三、哪些场景可以谨慎使用索引作为 key
?
1. 静态不可变列表
• 列表项顺序固定且无增删操作(如展示静态配置项)。
• 前提:数据无唯一标识符(如 ID),且列表项不含状态。
2. 纯展示型列表
• 列表项仅为纯文本或无需状态保留的简单元素。
• 示例:渲染一组固定的标签或描述项。
3. 临时测试或原型开发
• 快速实现功能原型,且无需考虑长期维护。
• 风险:未来扩展为动态列表时需重构代码。
四、替代方案:如何选择正确的 key
?
-
唯一标识符
• 使用数据中的唯一字段(如id
、uuid
),确保全局唯一且稳定。{items.map(item => (<li key={item.id}>{item.name}</li> ))}
-
复合键(Compound Key)
• 当数据无唯一字段时,组合多个字段生成唯一键(如name+timestamp
)。 -
哈希生成
• 对数据内容生成哈希值(如md5(content)
),避免随机数(如Math.random()
)导致频繁重渲染。
五、总结对比表
场景 | 是否适用索引作为 key | 替代方案 | 关键风险/优势 |
---|---|---|---|
动态列表(增删、排序) | ❌ 不适用 | 唯一标识符(如 id ) | 避免状态错乱和性能问题 |
含状态的列表项(如输入框) | ❌ 不适用 | 复合键或哈希生成 | 防止状态丢失 |
静态不可变列表(纯展示) | ✅ 谨慎使用 | 无(直接使用索引) | 简化代码,但需确保数据不变 |
临时原型开发 | ✅ 谨慎使用 | 无(快速实现) | 未来扩展需重构 |
六、最佳实践
- 优先使用数据唯一标识符,如数据库主键或 API 返回的
id
。 - 避免动态生成不稳定
key
(如随机数或时间戳)。 - 使用 ESLint 规则检测
key
问题(如react/jsx-key
)。 - 对复杂列表进行性能分析,利用 React DevTools 检查渲染次数。
通过合理选择 key
,可显著提升 React 应用的渲染性能和稳定性。在动态场景中,唯一标识符始终是最优解,而索引仅作为静态场景的临时方案。
forEach可以中断遍历吗
forEach 方法本身
不支持
直接中断遍历
在 JavaScript 中,forEach
方法本身不支持直接中断遍历,但可以通过其他技术手段实现类似效果。以下是详细分析及替代方案:
一、forEach
无法直接中断的原因
-
设计机制限制
forEach
的回调函数为每个元素独立执行,无法通过传统控制语句(如break
、return
)终止循环。
• 示例:[1,2,3].forEach(num => {if (num === 2) return; // 仅跳过当前迭代,继续执行下一个元素console.log(num); // 输出:1,3 });
-
底层实现原理
伪代码模拟forEach
的实现可发现,其回调函数被封装在独立的函数作用域中,无法操作外层的循环体:Array.prototype.myForEach = funct