# 前端面试题参考答案

答案与《题目清单.md》按分类和题号一一对应。这里给出面试时可讲清的核心思路；逐题代码与示例结果见《代码示例.md》。手写题应在编辑器中补全边界处理并运行测试。原题缺少输入输出约定时，答案会说明采用的假设。

## 高频手写精选

1. `Promise.all`：将可迭代对象转成数组，空数组立即兑现 `[]`；逐个 `Promise.resolve`，按原下标保存结果，计数归零后兑现；任一拒绝立即拒绝，其他任务不会自动取消。
2. JSON2DOM：递归创建元素；文本节点用 `createTextNode`，元素用 `createElement`，属性需区分 `className`、事件和普通属性，再递归追加 children。React 的 `render` 还包含协调、状态与更新，不能等同于简单 JSON2DOM。
3. 树转列表：深度优先遍历，把节点的 `children` 拆掉，输出 `{...node, parentId}`；记录访问过的节点以防循环引用，保留顺序。
4. 列表转树：先用 `Map(id → 节点副本)` 建索引，再按 `parentId` 挂到父节点 `children`；根节点单独收集。时间 O(n)，需约定孤儿节点和重复 id 的处理。
5. `flat(depth)`：递归或显式栈遍历；遇数组且剩余深度大于 0 则展开，否则加入结果。原生 `flat` 会跳过稀疏数组空位且不修改原数组。
6. `instanceof`：先检查右侧对象的 `Symbol.hasInstance`，若存在可调用方法就由它决定结果；否则右侧必须是函数，沿左侧值的原型链查找该函数的 `prototype`。自定义方法也可能让基本值返回 true。
7. `call/apply/bind`：`call` 逐项传参，`apply` 传数组状参数，`bind` 返回固定 `this` 与前置参数的新函数。手写版还须处理严格模式、构造调用、原型和 `length`，临时属性方案并不完全等价。
8. `map`：遍历 `0..length-1`，只在索引存在时调用回调 `callback.call(thisArg, value, index, array)`，输出等长数组并保留空位；先检查回调可调用。
9. 模板字符串替换：如 `/\{\{\s*([\w.]+)\s*\}\}/g` 匹配 `{{user.name}}`，回调用安全的路径读取函数取值；若插入 HTML，需做上下文转义，不能 `eval`。
10. `lodash.get`：把点号、方括号或数组路径解析为键序列后逐层读取；最终解析值为 `undefined` 时返回默认值，路径不存在也同样返回默认值，不必区分两者。此题只读数据，不应额外执行路径表达式。
11. 深拷贝：普通结构优先 `structuredClone`；手写时用 `WeakMap` 处理循环引用，并针对 `Date`、`RegExp`、`Map`、`Set`、数组等逐类复制。函数、DOM 节点和原型行为须单独约定。
12. 寄生组合继承：`Child.prototype = Object.create(Parent.prototype, {constructor:{value:Child,writable:true,configurable:true}})`；在 `Child` 中 `Parent.call(this, ...)` 初始化实例属性，避免重复调用父构造函数。
13. 发布订阅：维护 `Map<事件, Set<回调>>`；`on` 加入并返回退订函数，`emit` 用快照遍历，`off` 删除。约定回调抛错是否中断后续订阅者。

## 实现 Promise （hot）

1. Promise/A+：实现 `pending/fulfilled/rejected` 三态、只能首次落定、异步执行 `then` 回调和 `then` 返回新 Promise；核心 `resolvePromise` 要处理 thenable、递归吸收、自解析拒绝与防止多次调用。A+ 不规定 `finally/all`，完整原生兼容还需遵循 ECMAScript 微任务语义。
2. `finally`：若 `onFinally` 不是函数，直接透传原结果；否则通过 `then` 在兑现/拒绝两条路径调用它，等待其返回值落定后继续传原值或原错误，清理失败则以新错误覆盖。简化版可用 `Promise.resolve(onFinally())`，完整兼容还须考虑 Promise 子类的 `Symbol.species`。
3. `allSettled`：对每项 `Promise.resolve`，按输入顺序存 `{status:'fulfilled',value}` 或 `{status:'rejected',reason}`，全部完成才兑现；空输入兑现 `[]`。
4. `race`：对每项 `Promise.resolve(item).then(resolve,reject)`；第一个落定结果决定输出。空输入保持 pending。
5. `catch`：等价于 `this.then(undefined, onRejected)`；会返回一个新的 Promise。
6. `resolve`：`Promise.resolve(value)` 吸收 thenable；同构造器的 Promise 可直接返回。手写时防止 thenable 的多次落定和自引用。
7. `reject`：`Promise.reject(reason)` 返回已拒绝的新 Promise，不吸收传入的 Promise。

## Promise 周边场景题（hot）

1. 交通灯：把每个灯封装成 `await sleep(duration)` 后切换状态，外层 `while (!signal.aborted)` 顺序运行；离开页面时取消计时器。
2. 异步 fetch：`async function request(url, options){ const r=await fetch(url,options); if(!r.ok) throw new Error(String(r.status)); return r.json(); }`；调用方用 `try/catch`，并支持 `signal`。
3. `repeat(fn,5,1000)`：循环 5 次，每次 `await sleep(1000)` 后调用 `fn`；明确是否应立即执行第一次，并返回可取消句柄。
4. 一秒超时：`Promise.race([promiseA, timeoutPromise])` 可让等待方超时，但不能中断原任务；若能控制请求，应传 `AbortSignal.timeout(1000)`。
5. 五秒未完成终止：创建 `AbortController`，`setTimeout(() => controller.abort(), 5000)`，传 `signal` 给 `fetch`，`finally` 清除定时器；不要只做 `Promise.race`。
6. `sleep(ms)`：`new Promise(resolve => setTimeout(resolve, ms))`；如需取消，再监听 `AbortSignal` 并清除定时器。
7. 每秒打印 1 到 5：`for(let i=1;i<=5;i++){ await sleep(1000); console.log(i); }`，要立即输出 1 则把打印放到等待之前。
8. `setTimeout` 模拟 `setInterval`：在回调结束后再次 `setTimeout(tick, interval)`；返回取消函数。此方式不会重叠，但间隔包含回调耗时。
9. 图片异步加载：`new Promise((resolve,reject)=>{const img=new Image(); img.onload=()=>resolve(img); img.onerror=reject; img.src=url;})`；先绑事件再设 `src`。
10. Promise 封装 AJAX：创建 `XMLHttpRequest`，注册 `load/error/timeout/abort`，`open/send`；只把允许的 HTTP 状态当成功，错误时拒绝，支持取消与超时。
11. `setInterval` 模拟 `setTimeout`：首次触发时立即 `clearInterval(id)` 再执行回调；可实现近似效果，但计时精度和页面节流仍受环境影响。
12. 三次串行请求：`for (const input of inputs) { const response = await fetch(...); results.push(await response.json()); }`；后一请求需要前一结果时用其结果构造参数。
13. 三秒请求超时：`fetch(url,{signal:AbortSignal.timeout(3000)})`；区分超时、用户取消和 HTTP 非 2xx 响应。
14. Promise 无通用“中断”；Promise 只是结果容器。请求取消要用 `AbortController` 并把 `signal` 传给可取消的底层 API。
15. 测量 API：对每个候选发独立请求，以 `performance.now()` 差值计时，处理超时/错误，再按成功请求耗时选最小；GET 测的是延迟与下载，不等于上传速度。上传速度须用实际 POST/PUT 字节数除以耗时。
16. 补偿计时：记录计划执行时间 `next += interval`，每次使用 `setTimeout(tick, Math.max(0,next-performance.now()))`；后台节流无法完全补偿。
17. `setTimeout` 无法保证“准时”；可按绝对时间重排定时器并在回调中计算漂移，主线程阻塞或浏览器节流时仍会迟到。
18. 并发请求取最先成功/最先完成需区分：`Promise.race` 取最先落定，`Promise.any` 取最先成功；请求完成后可取消其他 `fetch`。
19. 并发发起、顺序输出：先 `const promises=urls.map(request)`，再按顺序 `for (const p of promises) console.log(await p)`；提前为每项附拒绝处理，避免未处理拒绝。
20. Promise 串行：`tasks.reduce((p,task)=>p.then(()=>task()), Promise.resolve())`；`tasks` 必须是函数，不能是已启动的 Promise。
21. 100 项、并发 10：维护最多 10 个 worker，各 worker 从共享下标领取下一任务，完成后继续领取；结果按输入下标存储，最后 `await Promise.all(workers)`。
22. 1、3、4 秒后打印：如果是相对起点，分别设置三个 `setTimeout`；若指相邻间隔，则顺序 `await sleep(1000/3000/4000)`，需先确认题意。
23. 并行限制调度器：队列保存任务函数，`active < limit` 时启动，任务在 `finally` 中递减 `active` 并启动下一项；`add` 返回该任务结果 Promise。
24. Scheduler：与上一题相同，核心是不超过并发上限、每项结果和错误独立传回，并处理空队列与同步抛错。
25. TypeScript 版限流 `all`：签名可用 `function limitAll<T>(tasks: Array<() => Promise<T>>, limit:number): Promise<T[]>`；校验 `limit > 0`，worker 并发领取索引，按原顺序写结果，任一失败拒绝。
26. 忽略后续请求：用 `inFlight` 锁；若已在请求则直接返回既定结果（如 `undefined`），否则 `try { return await request() } finally { inFlight=false }`。需明确“无响应”是忽略还是复用当前 Promise。
27. 每隔三秒输出时间：循环 `await sleep(3000); console.log(new Date())`；提供停止标志或 `AbortSignal` 以释放定时器。
28. 回调地狱：先把每个 Node 风格回调接口包装为 Promise，再用 `async/await` 依赖顺序执行，并在 `try/catch` 统一处理失败。
29. 返回限流请求函数：内部维护 `queue` 和 `active`；调用时把 `{url,resolve,reject}` 入队，空位立即发请求，完成后输出结果、落定调用方 Promise 并继续取队列。
30. 重试与缓存：每次用独立超时信号请求，失败后最多重试 N 次，可指数退避；全部失败时返回事先存在的缓存，否则抛最后错误。非幂等写操作不能盲目重试。
31. 递增间隔：保存次数 `n`，每次执行后安排下一次 `a+n*b` 毫秒；`myClear` 设置停止标志并 `clearTimeout(id)`。首轮是等待 `a` 后执行。

## JavaScript 常考手写题

1. 假设从 `[min,max]` 取 `n` 个不重复整数：验证 `n <= max-min+1`，可先建候选数组再 Fisher–Yates 洗牌并取前 n 项；避免无限随机重抽。
2. `async/await` 可理解为把生成器的每一步包装成 Promise：`next(value)` 得到 `{value,done}`，未结束时 `Promise.resolve(value).then(step,throwStep)`，结束时兑现返回值。
3. `sum(n)=n<=0?0:n+sum(n-1)`，所以 `sum(100)=5050`；实际大范围求和可用 `n*(n+1)/2` 避免栈溢出。
4. 遍历 1 到 10000，把数字转字符串并与反转字符串比较；“对称数”按十进制回文数解释，若 10000 包含与否按题目约定。
5. `indexOf(text,pattern)`：外层枚举起点 `0..text.length-pattern.length`，内层逐字符比较，首次全匹配返回起点；空模式返回 0，无匹配返回 -1。
6. 链式 math：对象持有当前值，`add/minus/times` 修改值并返回 `this`，用 `value()` 取结果；示例需约定 `add(2,4)` 为初始加法，结果 `(2+4-3)*2=6`。
7. `new Proxy(arr,{get(target,key,receiver){if(typeof key==='string' && /^-[1-9]\d*$/.test(key)){const i=target.length+Number(key);return i>=0?Reflect.get(target,String(i),receiver):undefined} return Reflect.get(target,key,receiver)}})`；必须先排除 Symbol 键，数组迭代等操作才不会因 `Number(Symbol)` 抛错。
8. 三组尽量等和属于三路划分，通常是组合优化题。小数组可回溯搜索并剪枝，最小化三组和的最大差；大数组可按数值降序放入当前最小和组作近似，需说明不保证最优。
9. 假设按行“之”字打印：偶数行从左到右、奇数行从右到左；若题目指对角线之字遍历，需另定遍历方向。
10. 非空数组用 `Math.max(...arr)` 或 `reduce((max,x)=>Math.max(max,x))`；大数组避免展开参数过多，空数组需约定返回值。
11. 尾递归 `fib(n,a=0,b=1){return n===0?a:fib(n-1,b,a+b)}`；JS 引擎不普遍保证尾调用优化，大 n 建议改循环或 `BigInt`。
12. 简单路由：监听 `popstate`，调用 `history.pushState` 后主动渲染当前 `location.pathname` 对应视图；直达路径需服务器回退到 `index.html`。
13. `setItem` 用 `JSON.stringify`，`getItem` 用 `JSON.parse`；包装 `try/catch` 处理禁用存储、配额与坏 JSON，并约定缺失值为 `null`。
14. 同第 4 题，限定 1 到 1000：`String(n) === [...String(n)].reverse().join('')`。
15. 若用于口令/令牌，用 `crypto.getRandomValues` 取随机字节，并通过拒绝采样映射到字符表以避免取模偏差；`Math.random` 仅适合非安全用途。
16. 按大写字母提取缩写：`[...s].filter(ch=>/[A-Z]/.test(ch)).join('')===abbr`；`ByteDance` 得 `BD`，`Bytedance` 得 `B`。此定义仅适用于 ASCII 驼峰。
17. 例如游程编码：连续相同字符写成“字符+次数”，`aaabb` → `a3b2`；需约定数字或分隔符出现在原文时如何消歧。
18. 原讨论给出商品 `{name,categories}` 数组，目标是按分类反向归组。用 `Map<分类, 商品名称数组>`，遍历每个商品的每个分类并追加名称，最后转为 `[{name:'热门',categories:['可乐','苹果']}, ...]`；分类首次出现的顺序可由 `Map` 保留。
19. 需要给多字符片段定义分界，否则 `50a6we8y20x` 有歧义。若约定“数字后直到下一段数字前全是重复片段”，用 `/\d+\D+/g` 分段，分别取数量和片段后 `repeat`。
20. 原讨论实际在探讨用 `Object.defineProperty` 实现属性监听：遍历对象自有键，为每个键定义 `get` 收集依赖、`set` 在值变化时通知订阅者，并递归监听新赋入的对象。需说明新增/删除属性和部分数组操作无法仅靠现有属性的 getter/setter 自动捕获；若题意是“复刻原生 defineProperty”，纯 JS 无法完全实现。
21. `String` 原始值按值相等；对象、函数按引用相等。两个不同对象即使结构相同也不相等；同一引用的对象或函数相等，故原题“Object Object 返回 true”要明确是否同一引用。
22. 先删除所有 `b`，再反复删除相邻 `ac`，直到稳定；若删除会产生新邻接，可用栈在线处理，每见 `c` 且栈顶为 `a` 则弹出。
23. `arr.filter(x => typeof x === 'string' || x instanceof String)`；跨 realm 字符串对象时 `instanceof` 不可靠，可用 `Object.prototype.toString.call(x)==='[object String]'`。
24. 按原讨论中的示例，闭包记录次数和最近结果；前 n 次调用执行 `fn` 并保存结果，之后直接返回最近结果。注意这与 lodash 的 `before(n,fn)` 只执行前 n−1 次不同，必须按本题示例实现。
25. 原讨论中的实现是包装调用：`function myLog(...args){ console.log(...args) }`；若要求从零实现浏览器 DevTools 控制台，纯 JS 无法做到，需区分这两种题意。
26. 可给 `Number.prototype` 定义 `add/minus` 返回普通数，但污染全局原型且与装箱有关；更稳妥是 `num(5).add(3).minus(2).value()`，结果 6。
27. 非负安全整数可用 `n.toString(2)`；手写时不断取 `n%2` 并整除 2，逆序拼接，0 返回 `'0'`。负数的补码位宽需另外指定。
28. `child.remove()` 已是标准 DOM API；兼容写法 `child.parentNode?.removeChild(child)`。删除节点前应解除应用自行注册的外部监听或资源。
29. 遍历字符串，用 `Map` 或对象计数；“字母”是否含 Unicode、是否区分大小写需约定。
30. 高度 n 的第 i 行输出 `n-i` 个空格加 `2*i-1` 个星号；用 `repeat` 拼每行。
31. 闭包保存调用次数：`const a=(()=>{let n=0;return ()=>++n%2?1:2})()`；无需全局变量。
32. 给定非空数组 `values` 与目标数 `target`，遍历并保留 `Math.abs(x-target)` 最小的元素；原讨论的例子目标为 37，`[3,56,56,23,7,76,-2,345,45,76,3]` 的结果是 45。距离相同保留先出现者，空数组需另定返回或抛错。
33. 若求 1 到 n，直接 `n*(n+1)/2`；若是任意数组且禁用循环语句，可用递归或 `reduce`，但底层仍会遍历。
34. `a=b=c=1` 从右向左赋值；`a.x = a = {}` 会先确定左侧引用，再计算右侧赋值，可能导致旧对象的 `x` 指向新对象。需给出具体表达式才能唯一推导。
35. 遍历字符串计频并返回对象，例 `aab` → `{a:2,b:1}`；“数字为 1 可省略”暗示输出可能是编码字符串，原题格式不完整。
36. `Array(10).fill(1)`、`Array.from({length:10},()=>1)`、`[...Array(10)].map(()=>1)`；注意 `Array(10).map(()=>1)` 因空位不会执行回调。
37. 遍历字符串，以首字母为键收集到对象或 `Map`，再对每个分组调用 `sort()`；示例输出 `{b:['b'],k:['k'],l:['lm','ln'],z:['za','zm']}`。原讨论已补充这一目标。
38. 输入必须是升序整数数组：扫描每段连续的 `+1` 区间，记录起点和终点；单个数输出自身字符串，连续段输出 `起点->终点`。给定输入应得到题目所示五段；空数组返回 `[]`，重复数如何处理需另定。
39. 先计每个值总出现次数，再遍历维护已出现次数；仅重复的值追加序号，得到题示结果。
40. 过滤对象条目；“空”需定义。常见仅删 `null/undefined/''`：`Object.fromEntries(Object.entries(obj).filter(([,v])=>v!==null&&v!==undefined&&v!==''))`。
41. 先定义浅比较还是深比较。深比较需处理类型、键集合、数组顺序、循环引用、`Date/Map/Set` 和原型；通常用配对 `WeakMap` 避免无限递归。
42. 单调递减栈保存未找到答案的下标；遍历新元素时反复弹出比它小的下标并填当前下标，剩余填 -1；时间 O(n)。
43. 用 `Map` 计频，再按次数降序、平局按字典序排序取前 3；数据量大可用容量 3 的最小堆。
44. `n>1` 且不存在 `2..sqrt(n)` 的整数因子即素数；可跳过偶数，生成区间素数可用埃氏筛。
45. 用 `Date` 取年月日时分秒，`padStart(2,'0')` 补齐，按令牌替换；明确使用本地时区还是 UTC，避免把月份从 0 开始当成 1。
46. JSONP 仅能 GET：创建唯一全局回调名，拼入 URL 查询参数，插入 `<script>`；回调或出错/超时后清理脚本和全局函数。只能向可信源使用，响应会执行脚本。
47. 原讨论要求反转点号分隔的域名片段，而不是反转字符或 URL 路径：`www.baidu.taobao.com` → `com.taobao.baidu.www`。可用 `split('.').reverse().join('.')`；若禁用这些方法，就从右向左扫描片段并拼接。
48. `new URL(url).searchParams` 解析，重复键可用 `getAll` 保留数组；`URLSearchParams` 会处理百分号解码。不能把 `?a=1&a=2` 默默丢成单值。
49. 闭包持有计数，`count()` 增加并返回值，`count.reset()` 将其设回初值；不暴露可随意修改的全局状态。
50. 原题未说明颜色格式。若生成随机 `#RRGGBB`，从 `crypto.getRandomValues(new Uint8Array(3))` 取 3 字节，各自转两位十六进制后拼接；若仅装饰用途也可用 `Math.random`。
51. `arr.length=0` 会原地清空并影响同一数组的其他引用；`arr=[]` 只是让当前变量指向新数组；`splice(0)` 也会原地清空。
52. 用计数 `Map` 比较每个值出现次数。两边计数完全相同返回 0；A 计数均不大于 B 返回 1；B 属于 A 返回 2；否则 -1。对象元素按引用比较。
53. 浅合并用 `{...a,...b}` 或 `Object.assign({},a,b)`，后者覆盖同名键；深合并需单独规定数组、空值和循环引用策略，避免原型污染。
54. 若题意是按需得到无限序列，可用生成器 `function* numbers(){let i=0;while(true) yield i++}`，逐次 `next()` 而不一次存入内存；原讨论也有用 Proxy 为越界索引提供默认值的不同理解。JS 数组本身不能真正占用无限内存，仍需明确目标接口。
55. 原讨论的输入是以换行分行、连续空白分列的数字文本，可用 `s.trim().split(/\r?\n/).map(line=>line.trim().split(/\s+/))` 得到字符串二维数组；若要数字，再对每项用 `Number` 转换。空输入需单独返回 `[]`。
56. JS 已有 `[a,b]=iterable`；手写通用解构要遵守迭代协议、默认值、剩余元素和迭代器关闭规则。仅针对数组可按下标取值，但不等价于通用解构。
57. 浅合并 `[...a,...b]` 或 `a.concat(b)`；是否去重、是否深拷贝元素另行约定。
58. 集合语义先用 `const sa=new Set(A), sb=new Set(B)` 去重；交集 `new Set([...sa].filter(x=>sb.has(x)))`，并集 `new Set([...sa,...sb])`，差集 `new Set([...sa].filter(x=>!sb.has(x)))`。若题目要求保留重复次数，则改用计数 `Map`。
59. 多维数组笛卡尔积：`arrays.reduce((acc,arr)=>acc.flatMap(prefix=>arr.map(x=>[...prefix,x])),[[]])`；任一维为空则结果为空。
60. DFS 用“当前递归路径”`WeakSet` 检测回边；退出节点时删除。仅用全局 visited 会把共享引用误判为循环。
61. 先取最小 k 个数的多重计数，再原顺序遍历数组，遇到仍需的值就加入并减计数；排序阈值法时间 O(n log n)，可用选择算法优化。
62. 假设把点路径键还原为嵌套对象：按 `.` 分割路径逐层创建对象并赋值；要处理数组索引、路径冲突并拒绝 `__proto__/constructor/prototype`。
63. DFS 遍历对象并把路径连接成 `a.b` 键；数组可选择索引路径。循环引用、点号本身出现在键中、空对象都需约定表示法。
64. `once(fn)` 用闭包存 `called` 与 `result`，第一次 `fn.apply(this,args)`，之后返回缓存值；若函数抛错，通常不标记成功，以便下次重试。
65. 方法返回 `this` 或新包装对象以继续调用，最后用 `value()` 取结果；需明确是可变链还是不可变链。
66. 偏函数先保存部分参数，调用时合并后续参数并以调用时 `this` 调原函数；如 `partial(fn,...bound){return function(...rest){return fn.apply(this,[...bound,...rest])}}`。
67. 管道函数按从左到右执行：`fns.reduce((value,fn)=>fn(value),initial)`；要支持异步时用 Promise 链。
68. 在共同父节点监听事件，使用 `event.target.closest(selector)` 找匹配子元素，并确认它仍属于父节点；适用于动态子元素，卸载时移除监听。
69. `typeof` 识别基本值与函数，但 `typeof null==='object'`；对象具体类型可用 `Object.prototype.toString.call(value)`，跨 realm 和 `Symbol.toStringTag` 需注意。
70. `Array.from(arrayLike)` 或 `Array.prototype.slice.call(arrayLike)`；前者同时支持可迭代对象并可映射。
71. 图片预加载可创建 `Image` 并设 `src`，关键资源可用 `<link rel="preload">`；仅预加载确实马上会用的资源，避免争抢带宽。
72. 优先原生 `<img loading="lazy">`；需自定义进入视口行为时用 `IntersectionObserver`，加载后取消观察并处理失败占位。
73. 基本值去重 `Array.from(new Set(arr))`；对象按引用去重，按字段去重用 `Map(key → item)` 并明确保留首项还是末项。
74. 防抖在连续触发停止一段时间后执行；节流在窗口内最多执行一次。实现时保存计时器/上次时间，提供 `cancel`，并处理首次与末次执行需求。
75. 普通 `compose(f,g)(x)=f(g(x))` 从右往左；Koa 洋葱模型是中间件 `await next()` 前后包裹下游，需防止 `next` 被调用两次。
76. 可让 `sum(x,y)` 在第二参存在时直接返回和，否则返回 `y=>x+y`；判断参数个数用 `arguments.length`，避免把合法的 `0` 当作缺失。
77. 柯里化把多参数函数拆成逐步接收参数的函数，收集到原函数形参个数后执行；默认参数、剩余参数会影响 `fn.length`，需约定完成条件。

## 设计模式相关

1. 观察者模式：被观察者持有观察者集合，状态变化时逐个通知；与发布订阅相比，观察者通常直接订阅特定对象，发布订阅常通过事件中心解耦双方。退订和通知中抛错策略要明确。

## 树-场景题（hot）

1. DOM2JSON：递归读取 `nodeType`；文本节点输出文本，元素节点输出小写标签、选定属性和 `childNodes`。避免读取事件处理器、私有数据或过大的 DOM 子树。
2. 树深度：空树为 0，叶子为 1，`depth(node)=1+max(depth(child))`；极深树可用显式栈避免调用栈溢出。
3. 路径名：DFS 时传入祖先路径数组，到目标节点返回 `[...path,node.name]`；需要返回所有匹配时不要找到首项就终止。
4. 对象树遍历：DFS 可用栈，BFS 可用队列；对每个节点处理后遍历 `children`，用 `WeakSet` 防循环。
5. “获取树对象属性”缺少目标结构。若按指定键收集属性，遍历节点并 `result.push(node[key])`，未定义值按需求跳过。
6. 查找 `children` 路径：递归访问子节点，维护索引或名称路径；命中目标后返回路径，重复目标时需决定返回一个还是全部。
7. 对象字符串转树：若输入是 JSON 字符串，先 `JSON.parse`，再验证节点结构；若是缩进文本，按缩进层级用栈构树。原题缺少样例，无法确定格式。
8. DFS 累加沿途数值，命中目标和时输出当前路径；负数存在时不能仅凭当前和大于目标剪枝。
9. DFS 遍历每个节点，收集 `name`；若仅取目标节点名称，应先说明按 id、路径还是其他条件查找。

## 实现 JS 原生方法

1. `forEach`：转换接收者为对象，取长度快照，只访问存在的索引，调用 `callback.call(thisArg,value,index,object)`；返回 `undefined`。
2. `filter`：同样跳过空位，回调为真时将原值追加到新数组；结果数组是密集的。
3. `reduce`：有初始值从索引 0 开始；无初始值时查找首个存在的索引作为累加器，空数组且无初始值抛 `TypeError`。
4. `fill(value,start,end)`：把负索引换算成从末尾计，范围截在 `[0,length]`，逐个写入原数组并返回原数组；会填补空位。
5. `includes`：按 `SameValueZero` 比较，因此 `NaN` 可找到，`+0/-0` 相等；空位按 `undefined` 处理，支持负起始索引。
6. `push(...items)`：从当前 `length` 起写入各项，更新长度并返回新长度；数组接近最大长度时可能抛错。
7. `unshift(...items)`：从后向前移动旧元素，再写入新元素，返回新长度；需保留原有空位。
8. 标准数组原型没有 `copy` 方法；可能指 `copyWithin`：把数组内区间复制到目标位置，重叠时选择正确方向，原地修改并返回数组。
9. 标准数组原型没有 `getLevel`；若指数组嵌套深度，可递归求 `1+max(子数组深度)`，空数组/非数组的基准层数先约定。
10. Set 可用 `Map` 模拟增删查与迭代，值比较需用 `SameValueZero` 且维持插入顺序；直接使用对象键会把不同对象混为一谈。
11. Map 可用原生 `Map` 作为说明其行为的参考；纯 JS 完整复刻需对象键身份、插入顺序、迭代器与删除再插入语义，不能只用普通对象代替。
12. `zpadStart` 非标准方法；若意为左补零，`String(value).padStart(targetLength,'0')`，超过长度保持原字符串。
13. `Object.assign` 逐个源对象拷贝自身可枚举的字符串与 Symbol 键到目标，会触发 getter/setter，浅拷贝并返回目标；`null/undefined` 源跳过。
14. `Object.is` 类似严格相等，但 `NaN` 与自身相等，`+0` 与 `-0` 不相等；可用 `x===y ? x!==0 || 1/x===1/y : x!==x && y!==y`。
15. `JSON.stringify` 要处理转义、数组和对象、`toJSON`、replacer、缩进、循环引用及 `undefined/BigInt` 等规则；不宜声称简单递归版等价于原生。
16. `JSON.parse` 需词法分析与递归下降解析字符串、数字、布尔、null、数组和对象，并拒绝非法语法；绝不使用 `eval` 解析不可信 JSON。
17. `trim`：删除字符串两端的 ECMAScript 空白和换行字符，不改变中间内容；`/^\s+|\s+$/g` 是常见近似实现。
18. `new`：创建以 `Constructor.prototype` 为原型的对象，用该对象调用构造函数；若构造函数返回对象/函数就采用返回值，否则返回新对象。箭头函数不能被 `new`。
19. `repeat(n)`：验证 n 为非负有限数并转整数，重复字符串 n 次；可用倍增拼接减少次数，过大输出应抛范围错误。
20. 字符串 `includes(search,position)` 可用 `indexOf(search,position)!==-1`，但需拒绝正则入参，并处理位置转换与空串。

## JS 库函数实现

1. `chunk(array,size)`：要求 `size` 为正整数，循环 `i += size` 并 `slice(i,i+size)`；空数组返回 `[]`，不修改输入。

## JS 工具函数

1. `isPlainObject`：先排除 `null` 和非对象，再检查原型为 `Object.prototype` 或 `null`；跨 realm 对象需要更谨慎，不能仅看 `constructor === Object`。

## 手写 nodejs 模块

1. `promisify(fn)`：返回函数，在原参数后追加 Node 风格 `(err,value)=>err?reject(err):resolve(value)` 回调；调用原函数时保留 `this`，同步抛错也应拒绝。

## 正则相关

1. 手机号、QQ、颜色、邮箱只能按明确业务规则写正则；例如 CSS 六位十六进制色 `^#[0-9a-fA-F]{6}$`。手机号号段会变化，邮箱完整规范不适合单个简短正则，优先格式初筛加实际验证。
2. 遍历字符，将小写字母转大写、大写转小写，其他不变；只针对 ASCII 可用 `/[a-zA-Z]/g`。
3. `^\d|\d$` 测首或尾为数字；若要求首尾都为数字，用 `^\d.*\d$` 并单独处理单字符数字。
4. 只去两端空格用 `trim()`；删除全部空白可用 `replace(/\s/g,'')`，仅普通空格用 `/ /g`。
5. ASCII 字母用 `replace(/[A-Za-z]/g,'')`；Unicode 字母可考虑 `/\p{L}/gu`，需目标环境支持。
6. 驼峰转下划线：先处理连续大写缩写与单词边界，再 `/([a-z0-9])([A-Z])/g` 插 `_` 并转小写；仅简单正则会把 `XMLHttp` 拆错。
7. 递归转换对象键，同时保留数组结构；只处理自身属性，遇循环引用用 `WeakMap`，并约定键冲突时如何处理。
8. 若指三个连续数字字符，用 `/\d{3}/`；若指数值连续递增如 `123`，需逐位比较相邻数字差是否为 1。
9. 用栈匹配左右括号，记录每对括号对应的原串范围，从内到外抽取；只用正则难以处理任意嵌套层数，输出的空格格式另定。

## 排序算法

1. 冒泡：反复比较相邻元素，逆序则交换；一轮无交换可提前结束。平均/最坏 O(n²)，稳定。
2. 选择：每轮找到剩余元素最小值并与当前位置交换；O(n²)，通常不稳定。
3. 快排：选枢轴，把小于与大于枢轴的元素分区，再递归两边；平均 O(n log n)，最坏 O(n²)，原地版通常不稳定。
4. 插入：依次把当前元素插入前面已排序部分；O(n²)，近乎有序时接近 O(n)，稳定。
5. 归并：二分递归排序后合并两个有序序列；O(n log n)，额外 O(n) 空间，可稳定。
6. 基数：按个位、十位等从低到高做稳定分桶；适用于整数或固定编码键，时间与位数及基数有关，负数需分开处理。
7. 计数：统计有限整数范围内各值频次，前缀和可用于稳定输出；时间 O(n+值域)，值域大时不适合。
8. 希尔：按递减 gap 对各组做插入排序，最后 gap=1；性能依赖 gap 序列，通常不稳定。
9. 桶：把值映射到若干桶，桶内排序后依次拼接；分布均匀时较快，偏斜时可能退化，需明确桶边界。
10. 堆：建最大堆后反复把堆顶换到末尾并下沉；O(n log n)、额外 O(1) 空间、不稳定。

## 自定义 Hooks 手写题

1. 手势 Hook 在 `pointerdown/move/up/cancel` 记录起点、位移和时间，计算方向/速度，设阈值避免误触；在 effect 清理监听，并考虑触屏滚动冲突与键盘替代操作。
2. 轮播 Hook 管理当前索引、拖动位移和动画状态；手势结束按距离/速度切页，索引循环，自动播放计时器在悬停、失焦及卸载时暂停/清理。
3. `useEffect(()=>{ setup(); return cleanup; },[])` 对应挂载后的副作用和清理；开发模式 Strict Mode 可能执行“建立→清理→建立”检查，不能假设只运行一次。
4. 弹窗打开时在 effect 保存 `body.style.overflow` 原值并设 `hidden`，清理时恢复；多个弹窗并存需共享锁计数，移动端还要处理滚动穿透与焦点管理。
5. `useInterval(callback,delay)` 用 ref 保存最新回调，effect 随 `delay` 创建/清除 interval；`delay=null` 暂停，避免闭包读到旧状态。
6. `useQuery` 需先定义是读取 URL 参数还是数据查询。URL 场景可订阅路由变化并从 `URLSearchParams` 派生值；数据场景需缓存键、请求状态和失效规则。
7. `useRequest` 至少返回 `{data,error,loading,run,cancel}`；每次请求持有 `AbortController` 或版本号，卸载/新请求时防止旧结果覆盖，处理错误与重复提交。

## 组件设计题（Vue/React/JS均可）

1. 全选：源状态是已选 id 集合；全选取当前可选项 id，反选移除它们；“部分选择”状态从数量派生，并设置原生 checkbox 的 `indeterminate`。
2. 轮播图：索引、上一张/下一张、自动播放和交互暂停；按钮可键盘操作，有可访问名称；动画尊重减少动态效果偏好。
3. 表格：校验响应结构，列配置决定渲染，稳定 id 作 key；提供加载、空、错误、分页状态，不将不可信字段当 HTML 注入。
4. 歌词滚动：解析时间戳并按时间排序，用二分查找当前播放时间对应行；只在行变化时滚到可视区域，用户手动滚动时暂停自动跟随。
5. 搜索提示：输入防抖，给每次请求版本号或取消前次请求，确保只展示最新结果；键盘上下/回车/Escape、空结果与 `combobox/listbox` 语义要完整。
6. Todos：任务 id、文本、完成状态为核心状态；支持增删改查与过滤，持久化时处理坏数据；列表项用稳定 id。
7. 倒计时：记录结束时刻，用 `Date.now()` 差值计算剩余，而不是每秒减 1；卸载清理 timer，切后台后回来仍能校准。
8. 入驻提交页：把字段分组、必填与格式验证、保存草稿、提交中禁重复、失败可恢复；上传材料和隐私告知依业务要求设计。
9. 不同尺寸图片：保留原始宽高比，可用 CSS Grid/Masonry 或按原尺寸计算布局；图片预留尺寸避免 CLS，懒加载并限制单张最大宽度。
10. 反转列表顺序：若只改变显示顺序用 `[...items].reverse()`，不要原地改 props；交互和屏幕阅读顺序应与视觉顺序一致，谨慎使用 `flex-direction:row-reverse`。
11. 树组件：递归节点组件或扁平可见节点列表，按 id 管理展开/选中；大树用虚拟列表，键盘上下/左右导航和 `aria-expanded` 要支持。
12. 选项卡：管理 active id，按钮用 `role=tab`、内容用 `tabpanel`，箭头键切换焦点并支持 Home/End；切换时决定是否保留面板状态。
13. 拖拽：原生 Drag and Drop 或 Pointer Events；更新列表必须依据稳定 id，提供键盘移动方式、拖拽预览、取消和越界处理。

## HTML CSS 手写题

1. 环形进度条可用 SVG `<circle>` 的 `stroke-dasharray`/`stroke-dashoffset` 按比例绘制；提供文本百分比与 `role="progressbar"`、`aria-valuenow/min/max`，不要只靠颜色表达进度。

## JavaScript 与浏览器原理

1. 一轮事件循环取一个任务执行，随后清空微任务队列，再有机会更新渲染；实际调度由宿主环境决定，不能认为每个宏任务之后必定绘制。
2. 微任务会在渲染前持续清空，递归追加可能使输入和绘制饿死；把大工作分片并主动让出主线程，可用任务队列、`scheduler.yield()`（有兼容性处理）或 Worker。
3. 函数保留创建处词法环境即形成闭包；内存排查用 Heap Snapshot 比较保留对象与引用链，检查未清理监听、计时器和长生命周期缓存。
4. `class` 是基于原型的语法；实例沿 `[[Prototype]]` 链查找方法，`extends` 建立实例和构造器继承链，方法通常放在原型而非每个实例上。
5. 箭头函数词法绑定 `this`，无自己的 `arguments`，不能作构造函数；普通函数的 `this` 由调用方式决定，可用 `new`，有自己的 `arguments`。
6. ESM 静态声明、实时绑定且支持异步模块；CommonJS 运行时 `require`、导出对象，循环依赖可能得到尚未初始化的部分导出。两者互操作依运行环境。
7. URL 解析后检查缓存/解析 DNS，建立连接与 TLS，发请求收响应；浏览器解析 HTML/CSS、加载关键资源、布局绘制并执行影响交互的脚本。真实顺序含并行与缓存分支。
8. HTML 构建 DOM、CSS 构建 CSSOM，再形成渲染树；计算样式与布局、绘制图层并合成。样式/几何变化可能触发重算或重排，transform 常只需合成。
9. Performance 面板看长任务和 Layout，Memory Heap Snapshot 看保留链，Performance Monitor 看节点/堆增长；用可复现步骤和前后对比确认原因。
10. Cookie 随请求发送，适合少量会话数据并设安全属性；localStorage 持久同步，sessionStorage 按标签页会话，IndexedDB 异步且适合较大结构化数据。敏感令牌避免暴露给脚本。

## TypeScript

1. `any` 跳过检查，仅边界迁移时谨慎用；`unknown` 是安全的未知值，使用前须收窄；`never` 表示不可能值，可做穷尽检查。
2. 用 `typeof`、`in`、自定义谓词或判别字段收窄联合；判别联合给每种分支不同字面量 `kind`，避免直接断言类型。
3. `switch(value.kind)` 的 `default` 中写 `const unreachable: never = value`；新增联合成员时编译器会指出遗漏分支。
4. `T extends Constraint` 限制泛型能力；条件类型按类型关系选择分支，`infer` 从匹配形状提取内部类型，例如从函数提取返回值。
5. 例如 `type MyPartial<T>={[K in keyof T]?:T[K]}`；`type MyPick<T,K extends keyof T>={[P in K]:T[P]}`。原生工具类型还涉及一些细节，面试应说明边界。
6. `interface` 可声明合并且方便扩展对象形状；`type` 可表达联合、交叉、元组、条件类型等，不能同名声明合并。二者多数对象类型场景可互用。
7. 用判别联合：`{status:'loading'} | {status:'success',data:T} | {status:'error',error:Error}`；只有成功分支才有 data，避免多个独立布尔量形成非法组合。
8. Vite 开发时用转译器去掉 TS 类型以求速度，不执行完整语义类型检查；CI 仍需 `tsc --noEmit` 或 `vue-tsc --noEmit`。

## HTML、CSS 与无障碍

1. Flex 适合单轴排布，Grid 适合二维行列；响应式卡片可用 `grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr))`。
2. BFC 是独立的块格式化上下文，内部浮动参与高度、与外部浮动布局隔离，内部外边距与外部不折叠；可用 `display:flow-root` 创建。
3. 图片声明宽高或 `aspect-ratio`，字体预加载并考虑替换指标，异步区域预留空间；用真实 CLS 数据找移动元素。
4. 用 `prefers-color-scheme` 作默认值、用户设置作覆盖并持久化；保证对比度，更新 `color-scheme` 让原生控件匹配。
5. 优先原生 `<dialog>`；打开时聚焦合适元素，限制焦点在模态内容内，Escape 可关闭，关闭后回到触发按钮，并标注标题。
6. 原生按钮、链接、输入自带键盘和语义，优先使用；只有原生语义无法表达时补 ARIA，ARIA 不会自动添加交互行为。
7. 标签关联控件，错误文本用 `aria-describedby` 关联，提交后聚焦错误摘要或首个错误；仅颜色变化不足够，用键盘和屏幕阅读器实测。
8. 用内容驱动的 Grid/Flex、相对单位与容器查询，让内容自然换行；窄视口、缩放到 200% 和长文案都需检查溢出。

## 网络、缓存与安全

1. `Cache-Control` 指定可缓存性与新鲜期，命中强缓存可不发请求；过期后带 `If-None-Match` 与 ETag 协商，未变则 304。带哈希的静态资源适合长期缓存。
2. CORS 是浏览器同源策略的一部分，服务器通过响应头授予跨源读取权限；非简单方法/头或某些内容类型会先发 OPTIONS 预检，服务器间请求不受浏览器 CORS 限制。
3. 先解析 DNS、建立传输连接（TCP 或 QUIC）、TLS 验证证书并协商密钥，然后发送 HTTP 请求与接收响应；可复用连接并有缓存/CDN 分支。
4. HTTP/2 多路复用和头压缩减少请求开销，但单 TCP 丢包仍可能影响流；HTTP/3 基于 QUIC，可减少部分队头阻塞及连接迁移成本。仍应控制资源大小和优先级。
5. 普通 HTTP 适合一次性请求；SSE 适合服务端单向事件流与自动重连；WebSocket 适合持续双向低延迟通信，需处理心跳与断线恢复。
6. 用单个 `refreshPromise` 让并发 401 共享刷新；刷新成功后重放可安全重试的请求并防无限循环，失败则清理登录态。请求体不可重放时需特殊处理。
7. 默认对不可信文本做上下文编码，避免 `innerHTML`，对允许的 HTML 用可信清洗库；CSP 是纵深防御，不能替代正确编码和 DOM API 使用。
8. `HttpOnly` 阻止 JS 读取 Cookie，`Secure` 要求 HTTPS 传输，`SameSite` 限制跨站请求携带；仍需考虑 CSRF、XSS 与会话过期。
9. 站外页面诱导浏览器携带受害者凭据发送有副作用请求，即可形成 CSRF；用 SameSite、CSRF token、Origin/Referer 检查及避免 GET 改数据共同防护。
10. 客户端切块并记录上传 id、分片索引和校验值，服务端幂等接收、查缺补漏与最终合并校验；失败只重传缺失块，限制并发并支持取消。

## React 与 React 19

1. render 阶段计算下一棵 UI，可被中断/重试；commit 阶段把变化提交到 DOM 并运行相关 effect。状态更新安排重新渲染，同值更新可能被跳过。
2. key 标识同级列表项身份，重排时帮助保留正确组件状态；用索引作 key 在插入/删除/排序时可能让输入状态错位。
3. effect 应列全读取的响应式依赖，返回清理函数；闭包过期可通过正确依赖、函数式更新或 ref 解决，避免靠禁用 lint 掩盖问题。
4. 只有计算昂贵、引用稳定性确实影响子组件，且测量证明收益时才用；记忆化本身也有成本，先用 React Profiler 定位。
5. `useTransition` 把特定状态更新标为非紧急并给 pending 状态；`useDeferredValue` 让某个值的消费延后，适合输入即时响应、结果稍后更新。
6. 提交时禁重复或标 pending，显示可恢复错误；乐观更新需在失败时回滚或重新同步，尤其注意并发提交的顺序。
7. `useActionState` 管理 action 结果与 pending，`useFormStatus` 读取所属表单提交状态，`useOptimistic` 在异步期间提供临时乐观视图。
8. 局部状态用 `useState`；跨层共享少量稳定值可用 Context；复杂跨页面共享或高频更新再评估外部状态库，避免把所有状态集中。
9. 用 React Profiler 找提交次数和耗时，确定导致更新的 state/props，改动后对同一交互量化比较；开发模式数据不等于生产模式。
10. Hook 在 effect 中创建 `AbortController`，请求开始设 loading，完成时只让当前版本写入 data/error，清理时 abort；给调用方 `retry` 与取消入口。
11. React Compiler 可自动记忆化组件和 Hook 中部分计算；手动 `useMemo/useCallback` 仍可精确控制特定值的稳定性，但不能把记忆化当作程序正确性的前提。检查构建配置与编译诊断，用 Profiler 对比提交次数和耗时。

## Vue 3

1. `reactive` 通过 Proxy 跟踪对象属性读写，`ref` 通过 `.value` 跟踪读写；effect 读取时收集依赖，写入触发调度更新。
2. `computed` 缓存派生值；`watch` 监控指定源并可取新旧值；`watchEffect` 自动追踪同步读取的依赖。默认 watcher 回调通常在父更新后、组件 DOM 更新前运行，需 DOM 更新后用 `flush:'post'`。
3. 父子数据用 props/emit，深层依赖注入用 provide/inject，跨页面共享业务状态可用 Pinia；避免把局部临时状态全局化。
4. 原生表单 `v-model` 映射值属性和输入事件；自定义组件默认用 `modelValue` prop 和 `update:modelValue` 事件，也可声明命名模型。
5. `nextTick` 等待 Vue 批量 DOM 更新完成，适合更新数据后读取新 DOM；不应用它代替正确的数据流或异步业务完成通知。
6. 先升级依赖并运行迁移构建，处理不兼容 API/插件；可逐模块迁移组件，覆盖关键交互测试，再切换应用入口。
7. 直接解构普通变量会失去 Proxy 属性读取链；使用 `toRefs`/`toRef` 保留关联，或在消费处继续读取 `state.key`。
8. key 帮助 Vue 在列表变动时识别节点与组件身份；用稳定 id，不在重排列表中用索引作 key。

## Next.js 与服务端渲染

1. CSR 适合强交互且 SEO 要求低的界面；SSR 适合请求时个性化内容；SSG 适合变化少的公开内容；按需重新生成适合可容忍短时间旧内容的公开页面。
2. Server Component 负责服务端数据读取、保密逻辑和减少客户端 JS；需要 state、事件、浏览器 API 的边界用 Client Component，尽量让客户端边界靠近叶子。
3. 服务端与客户端首屏输出不一致会报错，常见于时间、随机数、浏览器状态或非法 HTML 嵌套；定位具体节点，保证首次输出一致，浏览器专有值放 effect 后处理。
4. 密钥、数据库读取只在服务端；交互、DOM、localStorage 在客户端。服务端返回给客户端的 props 必须可序列化且不含秘密。
5. 用 Suspense 为慢数据区块设 fallback，让外层内容先到达；流式响应逐步发送边界内容，仍需有错误边界和可用的加载状态。
6. 公共数据可按明确标签/时效缓存；含用户身份的数据按请求隔离或不缓存，缓存键必须包含影响输出的身份/权限维度，防止串用。
7. 服务端输出可抓取的标题、正文与元数据，客户端对需要的区域进行 hydration；减少阻塞脚本并确保交互在加载前有可理解的状态。
8. Cache Components 让可缓存区域显式使用 `use cache` 并设置生命周期/失效策略；依请求变化的区域保持动态，使用 Suspense 边界。具体行为随 Next.js 版本和配置变化，应以项目版本验证。

## 性能、工程化与测试

1. LCP 衡量最大内容渲染，INP 衡量交互响应，CLS 衡量意外布局偏移；按设备/页面分组看真实用户分位数，再用实验室工具找根因。
2. 图片用合适尺寸/格式并优先加载 LCP 图，字体减少字重并控制交换，脚本按需拆分/延后；用同口径真实用户指标和网络瀑布图验证。
3. 只渲染视口及缓冲区项目；动态高度需测量并维护位置索引，插入历史消息时保持滚动锚点，避免跳动。
4. Vite 开发时按需通过原生 ESM 提供模块并快速 HMR；生产构建打包、优化、拆分和压缩资源。TypeScript 类型检查需另跑。
5. 副作用标记错误、CommonJS、不支持静态分析的动态访问或整个库导入可影响 tree shaking；用构建分析报告和产物确认实际体积。
6. 纯逻辑用单元测试，组件行为用用户交互测试，跨页面关键流程用 E2E；避免三层重复测试同一实现细节。
7. 用可控的请求 mock 验证 loading→success/error、取消后不更新、重复提交行为；断言用户可见状态与请求次数。
8. 共享包只放跨应用稳定能力，明确包之间依赖方向与发布版本；避免应用互相引用内部源码，CI 按变更范围构建测试。
9. 采集错误与发布版本、匿名会话线索；Source Map 私有上传并限权限；发布采用可回退产物/开关，白屏率异常时快速回滚。
10. 明确目标指标与样本，固定页面、设备和网络条件比较实验室数据，同时看线上同口径 p75/p95 与错误率，避免只凭一次 Lighthouse 分数。

## AI 应用前端与流式交互

1. `fetch` 后从 `response.body.getReader()` 循环读块，用 `TextDecoder.decode(value,{stream:true})` 保留跨块字符；SSE 还需缓冲到完整事件边界再解析，不能假定一块就是一条消息。
2. 维护连接状态与最后事件 id；断线按可重放协议重连并去重，用户取消用 `AbortController` 且不自动重连；服务端不支持重放时不能假称续传。
3. 对不完整 Markdown 可先按文本或增量解析展示，完成后再稳定渲染；禁止直接注入模型生成 HTML，链接与代码展示也要做安全处理。
4. 长列表虚拟化或分段，流式文本按帧/批次更新；仅当用户停留底部时自动跟随，用户向上阅读则显示“跳到底部”按钮。
5. 每次会话/请求分配版本 id 并取消旧流；收到片段时先核对当前 id，再写入对应消息，切换会话时清理 reader。
6. 明确“排队/运行/等待工具/完成/失败/已取消”状态，显示可理解的进度与重试入口；取消要传到底层并处理服务端仍在执行的情况。
7. 先核对需求与数据边界，再审查输入校验、依赖、授权、错误处理与复杂度；运行测试和真实交互，对关键逻辑保留人工复核。
8. 可以描述具体任务、给 AI 的上下文范围、所采用与拒绝的建议，以及测试/审查证据；面试重点是你如何验证，而非仅说“用 AI 提效”。

## 系统设计与项目深挖

1. 输入防抖并设最小长度，取消旧请求或用版本号丢弃旧结果；缓存短期热门查询，限制服务端速率，完整处理键盘操作、加载和空态。
2. 前端路由/按钮权限仅控制体验，后端对每个资源和操作强制鉴权；权限模型可用角色加资源动作，并避免把敏感数据先发给无权用户。
3. 按业务领域划分组件与状态，局部状态留局部，跨域状态经明确接口共享；区分服务端缓存数据与 UI 临时状态，防止全局 store 成为耦合中心。
4. 用“场景与基线→证据→改动→前后指标→取舍”叙述真实案例，例如 p75 LCP 从多少降到多少；没有实测数据就如实说明。
5. 分片上传、进度计算、断点查询和幂等完成接口；网络失败可重试单片，重复提交用上传 id/幂等键防止重复文件，最终校验完整性。
6. 消息用服务端序号或时间加稳定 id 排序，发送中临时 id 与回执对账；断线按游标补消息，历史分页向上插入并保持滚动位置。
7. JS Bridge 采用白名单方法、版本化请求/响应 id、来源校验与权限控制；每次调用有超时和结构化错误，敏感能力必须由原生端复核授权。
8. 先确认影响范围并回滚/切流止损，再查发布版本、静态资源加载、JS 错误、接口和 CDN；复现根因、补监控与回归测试，记录恢复时间和预防措施。
