二进制与函数式编程

共 48 题
📑 题目列表 48 题
#
★★★

1. TextDecoderStream/TextEncoderStream 在 Streams 中文本转换的工程取舍

TextDecoderStream/TextEncoderStream 在 Streams 管道中如何做文本转换?工程上如何取舍?

  • 基于 TransformStream 的文本编解码
  • 多字节字符跨 chunk 边界的处理
  • 与整块 decode 的对比取舍

TextDecoderStream 与 TextEncoderStream 是基于 TransformStream 的文本转换器:TextEncoderStream 把字符串流转换为 Uint8Array 流(编码),TextDecoderStream 把二进制流解码为字符串流(含流式解码器,自动处理多字节字符跨 chunk 边界的问题——UTF-8 序列被拆到两个 chunk 时能正确缓冲续接)。工程价值:接入 Streams 管道(fetch 响应体流、文件流、WebSocket 流)做"边下边解码边处理",如流式解析 JSON/CSV、SSR 流式渲染的分块文本处理、日志流的实时展示。取舍:整块 fetch 后用 text()/arrayBuffer() 一次性解码简单直接但内存与延迟高(需全部到达);流式解码内存 O(1)、首字节延迟低,适合大文件与流式场景;TextDecoderStream 的 decoder 配置(fatal 模式、ignoreBOM)在损坏数据时行为明确;注意兼容性(需 TransformStream 支持环境)与编码错误处理。

本题考察流式文本转换的机制。核心是"TransformStream 形态 + 跨 chunk 边界缓冲"两大特性,与一次性解码的对比是取舍落点;能说明流式 JSON 解析场景即展示工程应用。

#
★★★

2. Blob 与 File 的 MIME 类型与字节级切片的应用

Blob 与 File 的 MIME 类型与字节级切片有哪些应用?使用时注意什么?

  • Blob 的不可变二进制容器与 type 属性
  • File 继承 Blob 并含元数据
  • slice 的分片上传、预览与类型嗅探

Blob 是不可变的二进制数据容器,type 属性记录 MIME 类型(如 "image/png"),size 为字节数;File 继承 Blob 并增加 name、lastModified 等文件元数据。字节级切片 Blob.slice(start, end, contentType) 返回新的 Blob 视图(不可变、不复制底层数据,仅引用区间),应用广泛:大文件分片上传(每片 slice 后 FormData 提交或 ReadableStream 发送,支持断点续传)、缩略图/音频视频预览(slice 局部数据 + URL.createObjectURL)、按类型嗅探(读文件头字节判断真实格式)、流式处理大文件的局部读取。注意:type 由用户代理/文件系统提供,不可信(服务端仍需校验);slice 的 end 为负值/越界时按规范裁剪;切片 Blob 仍持原数据引用(切片小但原对象大时内存不释放,需用 ArrayBuffer 复制或 transfer 才能真正释放)。

本题考察二进制容器的工程应用。核心是"不可变 + slice 视图 + type 元数据"三特性及其在分片上传、预览、嗅探中的应用,type 不可信与内存引用是细节考点;能结合断点续传说明切片价值即完整。

#
★★★

3. Immer 不可变更新(produce + draft + patches)

Immer 的不可变更新(produce + draft + patches)是如何工作的?

  • produce 的 draft 修改与结构共享提交
  • patches 增量记录变更
  • 撤销/重做与协同场景

produce(baseState, recipe) 中 recipe 收到 draft(baseState 的 Proxy),用户直接修改 draft;Immer 追踪修改路径,提交时产出新状态:被修改的对象深拷贝(写时复制)、未修改的子树复用原引用(结构共享),保证新状态不可变且尽量复用内存。enablePatches 后 produce 返回 [nextState, patches, inversePatches]:patches 以 JSON Patch 风格(replace/add/remove 操作数组)描述"本次变更",inversePatches 为逆操作,用于撤销/重做(undo/redo 栈只需保存补丁)、多端状态同步(协同编辑 diff)、审计日志。工程实践:react 状态更新(setState 传 produce)、redux toolkit 的 createReducer/immer 集成、撤销重做的画布与表单编辑器;注意 draft 不可逃逸(异步回调中修改 draft 无效,需再 produce)、patches 只记录 recipe 内的修改、频繁小修改时补丁序列化有开销。

本题考察不可变更新的完整机制。核心是"draft 写时复制 + 结构共享 + patches 增量"的三层能力,撤销/重做与协同是 patches 的价值场景;能说明 draft 逃逸边界即展示工程严谨性。

#
★★★

4. Array.from/Array.of 在类数组与可迭代对象转换的边界

Array.from 与 Array.of 在类数组与可迭代对象转换上有哪些边界?

  • Array.from 的类数组/可迭代双通道与 mapFn
  • Array.of 的变长参数语义
  • 稀疏与自定义迭代的边界

Array.from(arrayLikeOrIterable, mapFn?, thisArg?) 把"类数组对象"(有 length 与数字索引,如 arguments、NodeList、字符串)或"可迭代对象"(有 Symbol.iterator,如 Set、Map、生成器)转换为真数组;第二个参数 mapFn 相当于先转换后 map(且该 map 能正确处理空洞——Array.from 会填充 undefined 而非保留稀疏,与数组 map 跳过空洞不同)。Array.of(...items) 则是"变长参数构造数组",解决 new Array(n) 的歧义(一个数字参数被解释为长度而非元素):Array.of(1) 得到 [1] 而非长度为 1 的空洞数组。边界:Array.from 对非类数组非可迭代的普通对象返回空数组(依赖 length 缺失)或按 length 生成稀疏填充;可迭代对象只消费其迭代器(一次性);mapFn 内修改源对象可能导致不可预期结果。工程上:类数组转真数组统一用 Array.from,定长数值序列用 Array.of 或展开。

本题考察数组构造 API 的边界语义。核心是"from 双通道 + mapFn 空洞填充 + of 消歧义"三点,稀疏差异是易错点;能说明 new Array(n) 的坑即展示实践认知。

#
★★★

5. flatMap 在链式异步操作与 Promise 串联的工程价值

flatMap 在链式异步操作与 Promise 串联中有什么工程价值?

  • 数组 flatMap 的一对多映射
  • Promise 的 then 扁平化语义
  • 嵌套展开与扁平化流程编排

数组的 flatMap(fn) 等价于 map 后 flat(1):映射函数返回数组时自动展开一层,用于"一对多"展开(把分组展开为元素、树节点的子节点展平、字符串拆分后合并处理),且不产生中间嵌套数组。Promise 层面的"flatMap"则是 then 的扁平化语义:then 回调返回 Promise 时自动吸收(flatten),避免手动 then 嵌套,形成线性链。二者结合的工程价值:异步流程中"一个输入产生多个异步任务再聚合"可用 flatMap 表达——如并发展开子任务后 Promise.all 聚合、把上游 chunk 流映射为多个请求再扁平化结果;链式异步操作(先查配置、再按配置拉取列表、再逐项获取详情)用 await/then 的扁平化保持可读,配合 flatMap 的数组展开减少中间变量。注意:flatMap 只展开一层,深层嵌套需多次或递归展开;避免在 flatMap 中做有副作用的操作(难以调试)。

本题考察扁平化语义的双域应用。核心是"数组展开一层 vs then 自动吸收"的两种扁平化,及它们组合编排异步流程的价值;能说明 flatMap 替代 map+concat 的写法即展示实用掌握。

#
★★★

6. map/filter/reduce 与惰性迭代的取舍

map/filter/reduce 与惰性迭代的取舍是什么?

  • 数组方法的一次性遍历与中间数组
  • 惰性迭代的按需计算
  • 数据规模与链式复杂度权衡

数组的 map/filter/reduce 每次调用都完整遍历并产生新数组(map/filter),多步链式操作(filter→map→reduce)会多次遍历并分配多个中间数组,小数据量下简洁直观、可读性好;数据规模大或链过长时,多次遍历与中间分配带来可观的 CPU/内存开销。惰性迭代(Iterator Helpers、生成器、lodash 链)则构建"按需计算"的管道:filter/map 不立即执行,消费时才逐个元素流过各阶段,一次遍历完成全部变换且无中间数组(O(1) 额外内存),适合大数据集、无限序列与流式处理。取舍要点:数据量小(< 万级)用数组方法(清晰、可调试);数据量大/链长/流式用惰性管道(性能与内存);需要多次消费结果时惰性管道要重新构建(一次性),而数组可反复使用;reduce 属于"消费型"操作,两种风格下都是终点。可读性与性能冲突时,先量化为度量数据再决策。

本题考察遍历策略的性能取舍。核心是"多次遍历+中间数组 vs 一次遍历+按需计算"的对比,规模阈值与一次性是决策点;能给出工程判断流程(数据规模→链长→消费次数)即展示实战思维。

#
★★★

7. 高阶函数、柯里化、偏函数与函数组合

高阶函数、柯里化、偏函数与函数组合之间是什么关系?

  • 高阶函数的定义与价值
  • 柯里化与偏函数的分工差异
  • 组合的管道语义

高阶函数是"接收函数或返回函数"的函数(map/filter、debounce、withLogging),是函数式编程的组织单位。柯里化(currying)把多参函数改写为"一次一参"的嵌套函数链(f(a)(b)(c)),固定前置参数后得到新函数;偏函数(partial application)则一次固定任意位置的若干参数(f.bind(null, a) 固定第一个),剩余参数后续传入——柯里化是偏函数的特例(每次只固定一个且从左到右),二者都用于"参数预设"。函数组合(compose/pipe)把多个单参函数串成新函数:pipe(f, g, h)(x) = h(g(f(x)))(左到右执行),compose 则从右到左;组合的前提是函数单参化,因此常与柯里化配合(参数先固定后组合)。工程价值:复用与可组合性(把"日志+重试+限流"组合成新请求函数)、可测试性(每段独立测试)、声明式管道(数据流可读)。注意:过度柯里化降低可读性,JS 中按场景混合使用。

本题考察函数式基础概念的体系。核心是"高阶函数为容器、柯里化/偏函数为参数预设、组合为结构组织"的分工,pipe/compose 方向是易错点;能给出三者协作的工程示例即展示体系化理解。

#
★★★

8. 纯函数、不可变更新与组合的副作用隔离

纯函数、不可变更新与组合如何实现副作用隔离?

  • 纯函数三性(确定性、无副作用、引用透明)
  • 不可变更新的结构共享
  • 副作用集中在边界的设计

纯函数满足:相同输入恒有相同输出(确定性)、不修改外部状态(无副作用)、不依赖外部可变状态(引用透明),其可测试性、可缓存(memoization)、可并行性均源于此。不可变更新是纯函数的数据基础:更新状态时产出新对象而非修改原对象(配合结构共享控制复制成本),使"输入不变则输出不变"成立、历史状态可追溯、比较(Object.is)可安全用于优化。副作用隔离的设计模式:把计算(纯)与效果(I/O、DOM、日志、网络)分离——业务逻辑写成纯函数,副作用集中在应用边界(事件处理器、effect、提交层),组合管道中只有"计算节点",边界统一执行效果。工程价值:状态管理(Redux reducer、Vue store)强制纯 reducer 保证可预测;副作用注入(依赖参数化、Context 传递)使纯函数可测试;debug 时通过时间旅行与日志重放(纯更新)定位问题。

本题考察函数式设计的工程落地。核心是"纯函数定义→不可变数据支撑→副作用边界化"的递进关系,reducer 模式是典型例证;能说明结构共享如何缓解不可变更新成本即展示深度。

#
★★★

9. structuredClone 与深拷贝在函数式状态更新的取舍

structuredClone 与手写深拷贝在函数式状态更新中如何取舍?

  • structuredClone 的原生深拷贝能力与限制
  • 手写深拷贝的定制与风险
  • 函数式更新中"浅拷贝+结构共享"的优先性

structuredClone(value) 是原生结构化克隆:支持循环引用、TypedArray、Map/Set、Date、RegExp、Blob、Transferable 转移等,克隆与源完全独立;限制:无法克隆函数、Symbol、DOM 节点(抛 DataCloneError),原型链只保留默认、getter/setter 结果被复制为数据。手写深拷贝(递归)可定制(保留原型、处理特殊对象、忽略字段),但正确性风险高(循环引用、代理、不可枚举、稀疏数组、内部槽对象等边界)。函数式状态更新中的取舍:完整深拷贝通常不必要且昂贵——函数式更新应优先"浅拷贝 + 结构共享"(只复制变化路径,如 {...state, items: newItems}、Immer 写时复制),深拷贝仅用于"彻底脱离原对象"的隔离场景(跨系统传值、快照备份、防篡改);structuredClone 适合已知数据形态的可靠隔离,手写深拷贝仅在需要自定义语义(过滤敏感字段、保留原型)时使用。工程上先分析是否真的需要"深",大部分更新场景浅拷贝即够。

本题考察深拷贝工具的定位。核心是"structuredClone 原生可靠但有类型限制"与"函数式更新应优先结构共享而非深拷贝"的工程判断,手写深拷贝只在定制场景使用;能说明浅拷贝+结构共享的成本优势即展示性能思维。

#
★★★

10. Object.freeze 在 React/Vue 不可变数据优化的语义价值

Object.freeze 在 React/Vue 不可变数据优化中有何语义价值?

  • freeze 标记不可变与深层一致性
  • 跳过响应式代理与依赖追踪
  • 渲染优化的比较基础

Object.freeze 把对象标记为不可变(属性只读、不可增删),语义价值分两层:数据层面——保证"不会被意外修改",与不可变更新策略一致,使共享引用安全(多组件/模块共读同一配置);框架层面——Vue 3 的 reactive() 会跳过已冻结对象(Vue 2 的 defineProperty 同样跳过),冻结数据不进入响应式代理与依赖追踪,避免大型静态数据(长列表字典、常量配置、国际化表)的代理开销与更新检查成本;React 中冻结的 props/state 数据可安全用于浅比较(memo 依赖 Object.is 判定),且能防止误改导致的渲染不一致。工程价值:静态大表数据 freeze 后作为"只读快照"传入组件;reducer 输出的不可变状态用 freeze 防御误改(开发模式);注意 freeze 是浅冻结,深结构需递归或配合库(如 immutable 数据生产端冻结),且 freeze 不阻止代理层(对冻结对象再包 Proxy 的 set 会受不变量限制)。

本题考察不可变标记与框架优化的协同。核心是"语义不可变 + 跳过响应式 + 浅比较安全"三重价值,浅冻结边界是易错点;能说明 Vue 跳过冻结对象的机制即展示框架原理认知。

#
★★★

11. Web Streams API(ReadableStream/TransformStream/WritableStream)

Web Streams API 的 ReadableStream、TransformStream、WritableStream 如何协同处理数据流?

  • 三类流的职责与管道协作
  • 背压(backpressure)传播机制
  • 流式处理的应用场景

Web Streams API 由三类流构成:ReadableStream 是数据源(可读端,controller.enqueue 产出数据、close/error 结束,也可从 pull 源按需产出);WritableStream 是数据目的地(可写端,write 写入、close 关闭);TransformStream 是转换器(可读可写,内部通过 Transformer 的 transform 方法把写入的数据变换后输出到可读端)。协作机制:pipeThrough(transform) 把可读流接入转换器、pipeTo(writable) 把流写入目的地,形成"读→转→写"管道;背压自动传播——消费者读取慢时(读端队列满),管道的可写端写入被阻塞/放慢,数据源端得到反向压力,防止内存无限增长。工程应用:fetch 响应体(response.body 是 ReadableStream)流式解析、大文件分片上传(请求体流)、CompressionStream 压缩管道、SSE 事件流处理。注意:流是单消费者、按需拉取(pull-based),必须消费才会产生数据;错误沿管道传播,需在末端 catch。

本题考察流式处理的基础架构。核心是"三流职责 + 管道组合 + 背压传播"的模型,背压是流的灵魂;能结合 fetch 流式下载场景说明即展示应用能力。

#
★★★

12. 函数组合(compose/pipe)的执行顺序与调试栈可读性

compose 与 pipe 的执行顺序有何不同?组合管道如何保证调试栈可读性?

  • pipe 左到右、compose 右到左
  • 错误定位与命名函数
  • 管道中间值调试策略

pipe(f, g, h) 按"数据流动方向"从左到右执行:x → f → g → h,与阅读顺序一致(先做什么后做什么直观);compose(f, g, h) 从右到左执行(数学复合 h∘g∘f 的书写习惯),在数学/函数式风格(ramda/lodash fp)中常见。工程上优先 pipe(可读性好),在 DSL 或既有代码风格中用 compose。调试栈可读性:匿名箭头函数组合后,栈帧显示为匿名位置,难以定位出错环节;对策——给函数命名(debug 时用具名函数或包装 {fn.name})、错误处理在管道中插入"哨兵函数"(打印当前值后透传)、用 tap 风格函数做观测点;pipe 中抛错时栈信息通常保留到调用点,配合 source map 可还原;大型管道拆分为具名小组件(每个命名函数一段逻辑),错误信息直接指向命名函数。工具层面:ramda 的 pipeWith/lodash fp 的 flow 提供调试插入点。

本题考察组合的执行语义与可维护性。核心是"方向差异"与"具名化/观测点"两大调试策略,栈可读性是函数式代码落地的关键痛点;能给出 tap 观测写法即展示实战技巧。

#
★★★

13. 结构共享的不可变数据(Immer 风格)的更新性能

结构共享的不可变数据在更新性能上有何特点?何时比深拷贝更优?

  • 写时复制与路径克隆
  • 未变子树的引用复用
  • 更新频率与状态规模权衡

结构共享的不可变更新(Immer、immutable.js)在修改时只克隆"变化路径"上的节点:根对象一定新、被修改的子树新、未修改的子树复用原引用,因此更新成本与"变化的规模"成正比而非"状态总规模"——对大状态做小修改,开销远小于深拷贝(深拷贝 O(N),结构共享 O(变化路径深度))。收益还包括:比较成本低(Object.is 根引用即可判断是否变化)、历史状态共享内存(undo 栈持有引用不重复存储)、并发读安全。何时更优:状态大且修改局部(组件树状态、编辑器文档)、更新频繁、依赖引用比较的优化(memo)存在时;何时不优:状态小(复制开销可忽略,直接浅拷贝更简单)、变化面大(接近全量更新时路径克隆叠加代理开销可能比直接重建慢)、对性能极敏感的热路径(Proxy 拦截与克隆检查有常数开销)。工程判断:默认"浅拷贝 + 结构共享"(手写展开),变化复杂时用 Immer,并用基准度量验证。

本题考察不可变更新的性能模型。核心是"克隆代价正比于变化路径而非总规模"的比较优势,以及小状态/全量变化场景的边界;能说出 Object.is 引用比较的联动收益即展示系统思维。

#
★★★

14. 记忆化(Memoization)的缓存键与容量控制

记忆化的缓存键如何设计?容量控制与失效策略有哪些?

  • 参数序列化与引用键
  • 缓存容量与淘汰策略(LRU)
  • 失效条件与内存边界

记忆化(memoize)用缓存键把"参数组合→结果"保存:键设计要点——简单值用序列化字符串(JSON.stringify,注意键序与循环引用)、对象/复杂参数用引用本身(Map 键)或显式 key 函数(业务主键),避免隐式序列化导致的键冲突与误命中;函数纯度是前提(依赖外部状态/时间/随机数不能记忆化)。容量控制:无界缓存会累积内存(尤其参数组合爆炸的场景),需限制容量并配淘汰策略——LRU(最近最少使用,Map 的插入序模拟或专用 LRU 库)、LFU 或按时间 TTL 失效;框架层(React useMemo)以依赖数组为键、单槽缓存并随组件生命周期释放。失效策略:参数变化自然换键;外部数据变化需显式清缓存(版本号/依赖订阅);调试与生产可配置开关。工程实践:纯计算(重格式化、复杂推导)用记忆化,注意缓存键的规范化(排序、类型归一)与容量上限。

本题考察记忆化的工程化要点。核心是"键设计(值/引用/业务键)+ 容量淘汰(LRU/TTL)+ 失效时机"三位一体,纯度前提是判断基础;能给出缓存键冲突的真实案例即展示实战认知。

#
★★★

15. SharedArrayBuffer + Atomics 在跨 Worker 高吞吐并行计算的应用边界

SharedArrayBuffer + Atomics 在跨 Worker 高吞吐并行计算中有哪些应用边界?

  • 共享内存与原子操作的配合
  • 跨隔离域(COOP/COEP)前提
  • 与 postMessage 拷贝通信的对比

SharedArrayBuffer(SAB)让多个 Worker(及主线程)共享同一块内存,数据零拷贝;Atomics 提供原子操作(add、exchange、wait/notify、load/store)保证共享访问的同步与内存序,二者配合实现无锁/轻量锁的高吞吐并行:图像/音频处理、WebAssembly 计算、数值模拟等场景把数据分块交给多 Worker 并行写同一缓冲区,主线程经 Atomics.wait/notify 同步结果。边界:安全前提——SAB 要求跨源隔离(COOP/COEP 响应头),否则 API 不可用(页面需配置 "Cross-Origin-Opener-Policy: same-origin" 与 "Cross-Origin-Embedder-Policy: require-corp");内存管理与生命周期需自行约定(大小固定、分配释放、缓冲区 transfer 语义);原子操作粒度有限(非全部并发模式可用,需避免数据竞争与死锁,自旋等待用 Atomics.waitAsync/通知唤醒而非忙等);相比 postMessage 的结构化克隆(数据拷贝、简单安全),SAB 快但易错。工程上:先评估是否真需共享内存(拷贝开销 vs 并发复杂度),需要时把共享逻辑封装为专用模块并配套内存协议文档。

本题考察共享内存并行的能力与代价。核心是"SAB 零拷贝 + Atomics 同步"的组合模型、跨源隔离前提与"共享即危险"的边界,对比 postMessage 的取舍是落点;能说明 COOP/COEP 配置即展示上线思维。

#
★★★

16. structuredClone 与 postMessage 的 Transferable 在跨 Worker 通信的边界

structuredClone 与 postMessage 的 Transferable 在跨 Worker 通信上有何边界?

  • 结构化克隆的复制语义
  • Transferable 的所有权转移
  • 大数据的传输策略选型

postMessage 与 structuredClone 底层都是结构化克隆算法:postMessage(msg, targetOrigin, [transfer]) 把消息克隆后跨上下文传递,Transferable 列表(ArrayBuffer、MessagePort、ImageBitmap 等)则把对象所有权转移而非复制——原上下文中的缓冲区被 detach(长度变 0),数据零拷贝直达接收方,适合大数据块(视频帧、大二进制)的高吞吐传输。边界:结构化克隆支持循环引用、TypedArray、Map/Set、Date 等,但函数、DOM 节点、Symbol 不可克隆(抛 DataCloneError);Transferable 转移后原端不可再用(需重新申请),转移对象必须是可转移类型且一次转移一个接收方。选型:数据量小/频率低用克隆(简单安全、原端可复用);大块高频数据用 Transferable(零拷贝)或 SharedArrayBuffer(多写者共享);注意 structuredClone 也可独立用于同上下文深拷贝(含 transfer 参数),主线程克隆 UI 相关对象(如大量节点数据)可能阻塞主线程,应分批或转移。

本题考察跨上下文数据传输的机制边界。核心是"克隆复制 vs 转移零拷贝"的语义差异与可转移类型约束,按数据规模选型是工程落点;能说明 detach 后原端行为即展示细节掌握。

#
★★★

17. SharedArrayBuffer 在多 Worker 共享数据时的同步原语(Atomics.wait/notify)

SharedArrayBuffer 多 Worker 共享数据时,Atomics.wait/notify 如何实现同步?

  • wait/notify 的阻塞与唤醒协议
  • 条件变量式同步模式
  • 忙等与死锁风险

Atomics.wait(int32Array, index, expectedValue, timeout?) 在指定位置的值等于 expectedValue 时阻塞当前线程(仅限 Worker,主线程阻塞会冻结页面),直到 Atomics.notify 唤醒或超时返回;notify(int32Array, index, count) 唤醒等待中的 Worker。二者构成"条件变量"式同步:生产 Worker 写共享数据后写入状态标志并 notify,消费 Worker 在条件不满足时 wait 挂起、被唤醒后检查条件再消费,配合内存屏障保证写入可见。模式要点:wait 的 expectedValue 用于"条件未满足才等待"的原子检查(防止丢失唤醒——检查与挂起是原子的);循环条件判断(唤醒后需复查,防止假唤醒);多个等待者用 notify 的 count 控制唤醒数量。风险:忙等(不用 wait 而用循环 Atomics.load 自旋)浪费 CPU,应 wait/notify 或 Atomics.waitAsync;wait 超时需重新检查条件;等待与唤醒位置错配造成死锁;主线程只能 waitAsync(异步等待)。工程上把同步协议封装为信号量/队列原语,避免裸用。

本题考察共享内存的同步原语。核心是"wait 原子检查挂起 + notify 唤醒"的条件变量模型与防丢失唤醒机制,忙等/死锁是工程风险;能说明主线程 waitAsync 的限制即展示边界认知。

#
★★★

18. ArrayBuffer.transfer/ArrayBuffer.transferToFixedLength 在所有权转移与 GC 协作

ArrayBuffer.transfer 与 ArrayBuffer.transferToFixedLength 的所有权转移如何与 GC 协作?

  • transfer 移动缓冲区所有权(原缓冲 detach)
  • transferToFixedLength 的不可调整固定长度
  • 大内存的释放与复用

ArrayBuffer.transfer(oldBuffer) 创建新缓冲区并转移数据,原缓冲区被 detach(长度变 0、内容不可访问),新缓冲区可调整大小(resizable);transferToFixedLength 转移后得到固定长度(不可 resize)的缓冲区。所有权转移的价值:把"迁移/重组"表达为一次底层内存移动(可能零拷贝或系统级 realloc),避免手动复制再释放的中间态;与 GC 协作——旧缓冲区立即失效可被回收,若支持"内存回收"语义(如 Wasm 内存增长后旧内存归还),则大内存的释放时机更可控(不依赖 GC 的延迟回收),配合 shared(resizable 共享)与 grow 能力管理弹性内存池。应用:WebAssembly 内存扩容、音频/视频缓冲重组、流式积累的缓冲合并。注意:transfer 是"移动"语义——转移后原引用不可再用;transferToFixedLength 适合已知最终大小的场景;缓冲的 GC 释放仍由引擎调度,transfer 只保证"旧对象失效",真正归还操作系统内存仍需零引用后引擎回收。

本题考察缓冲区所有权转移机制。核心是"transfer 移动+detach 旧缓冲+新缓冲可调整"的语义及其对释放时机/复用策略的改善,GC 协作的边界(引擎回收仍存在)是深度考点;能结合 Wasm 内存增长场景即完整。

#
★★★

19. 堆快照定位闭包、Detached DOM 与监听器泄漏

如何用堆快照定位闭包、Detached DOM 与监听器泄漏?

  • 堆快照的引用图分析
  • Detached DOM 的判定与保留链
  • 监听器泄漏与闭包持有的定位

DevTools Memory 面板的堆快照(Heap Snapshot)展示堆中所有对象及其引用关系,定位泄漏的标准流程:先做"基线快照→执行操作→再做快照→对比",找出增量对象。Detached DOM 泄漏:在快照中启用"统计"或搜索 HTMLDivElement 等节点类型,被标记 detached 的节点(已从文档移除但仍被 JS 引用)即为泄漏,沿 Retainer Path 查看保留链(常见为事件监听器、闭包、缓存引用)。监听器泄漏:快照中搜索对应对象(如自定义类实例),Retainer Path 会显示经 addEventListener 的监听列表挂载在文档/元素上,或事件处理器闭包持有大对象;结合 Performance 面板的监听器计数与 heap 对比确认。闭包泄漏:快照中搜索对象类型,Retainer Path 显示 Closure(函数上下文)引用,展开可见捕获的变量——定位到注册回调/定时器的代码位置后解除引用。工程实践:周期性快照对比 + 复现路径脚本化,修复后对比增量归零即验证。

本题考察内存泄漏的定位方法论。核心是"快照对比 + Retainer Path 引用链"的标准流程与三类泄漏的识别特征,能分别给出 Detached DOM、监听器、闭包的特征描述即展示实战能力。

#
★★★

20. 结构化克隆与 Transferable Objects 的性能差异

结构化克隆与 Transferable Objects 的性能差异是什么?

  • 克隆的序列化复制开销
  • 转移的零拷贝所有权移交
  • 大数据高频通信的选型

结构化克隆按算法把数据序列化后复制到目标上下文:开销与数据规模成正比(遍历所有字段、复制字节),且大对象克隆会在发送线程/接收线程产生明显耗时与临时内存峰值;Transferable 转移则只移交底层存储的所有权(如 ArrayBuffer 的堆内存句柄),零拷贝直达,时间复杂度近似 O(1)(与数据大小无关),是大块数据(视频帧、点云、大文件块)跨 Worker 高频传输的性能关键。差异要点:克隆保留原数据(原端可继续使用)但每次传输都复制;转移后原端 detach(不可再用)但一次转移一劳永逸;克隆支持任意可序列化结构,转移仅限可转移类型(ArrayBuffer、MessagePort、ImageBitmap、WASM 内存)。选型:小消息/低频用克隆(简单);大块/高频用转移(配合缓冲池——转移后原端从池中重新申请或轮换缓冲区,避免频繁分配);需要在共享内存上多写者并发时用 SharedArrayBuffer。衡量:性能测试对比 transfer 与 clone 的耗时与 GC 压力。

本题考察数据传输的性能模型。核心是"复制 O(N) vs 转移 O(1)"的数量级差异与 detach 约束下的缓冲池策略,按规模/频率选型是落点;能设计轮换缓冲池方案即展示工程深度。

#
★★★

21. ArrayBuffer、TypedArray、DataView 的字节序与视图

ArrayBuffer、TypedArray、DataView 之间的关系与字节序(endianness)处理是怎样的?

  • ArrayBuffer 的裸字节与视图概念
  • TypedArray 的定长类型视图与平台字节序
  • DataView 的显式字节序读写

ArrayBuffer 是固定长度的裸二进制存储(无类型、不可直接读写),TypedArray(Uint8Array、Int32Array、Float64Array 等)与 DataView 是它的"视图":TypedArray 以固定元素类型把缓冲解释为同质数组,读写按平台字节序(小端为主,大多数 x86/ARM 为小端),元素间无间隙、快速;DataView 提供 getInt32(offset, littleEndian)、setFloat64(offset, value, littleEndian) 等显式指定字节序的读写 API,可跨字节序安全处理异构数据(如网络协议头、文件格式、跨平台二进制)。字节序影响:直接对多字节类型(Int32、Float64)读写时,大端平台(网络字节序数据)与本地小端可能不一致——TypedArray 按本地序解释,跨端数据需 DataView 显式处理或字节交换;Uint8Array 是逐字节的,无字节序问题(字节流读写首选)。工程上:解析二进制协议/文件(PNG 头、WASM 头)用 DataView 显式指定端序;数值计算与同构数据用 TypedArray 获得性能;shared buffer 跨线程传多字节数据注意端序约定统一。

本题考察二进制视图体系。核心是"裸缓冲 + 类型视图 + 显式端序视图"三层结构,TypedArray 平台序 vs DataView 显式序的差异是关键;能给出解析网络字节序协议的示例即展示应用能力。

#
★★★

22. Blob/File/FormData/URL.createObjectURL 在文件数据流的现代取舍

Blob/File/FormData/URL.createObjectURL 在文件数据流处理中有哪些现代取舍?

  • Blob/File 的容器与元数据
  • FormData 的表单传输
  • objectURL 的预览与释放生命周期

文件数据流的现代链条:File(含 name/type/lastModified)与 Blob 是数据容器,FormData 把它们组装为 multipart 表单直接 fetch 上传(浏览器自动生成 boundary),objectURL(URL.createObjectURL)为 Blob/File 生成内存引用 URL 用于预览(img/video/下载链接),File 还支持 stream()/arrayBuffer()/text() 与 slice() 供流式处理与分片上传。取舍要点:上传优先 FormData + fetch(简单、进度需配合 XMLHttpRequest 或流式请求体);预览/下载用 objectURL(比 dataURL 高效——dataURL 需 base64 编码膨胀 33% 且占内存,objectURL 是引用);objectURL 必须 URL.revokeObjectURL 释放(否则内存不回收,大文件尤其明显),释放时机在预览完成后/组件卸载;分片上传用 slice 切块 + 并发 + 断点续传;FileReader 已让位于 stream()/arrayBuffer() 的现代流式读取。注意:objectURL 在文档卸载时自动失效,跨页面需重新生成;FormData 的 append 可带文件名。

本题考察文件 API 的选型体系。核心是"容器(Blob/File)→传输(FormData/fetch)→预览(objectURL+revoke)"的链条及各环节的取舍,revoke 生命周期是易漏点;能对比 dataURL 与 objectURL 的差异即展示性能思维。

#
★★

23. TextEncoder/TextDecoder 在多字节字符编码的跨语言边界处理

TextEncoder/TextDecoder 在多字节字符编码的跨语言边界上有哪些处理要点?

  • UTF-8 编码与解码的双向转换
  • 流式解码的块边界(stream 选项)
  • 编码错误与 BOM 处理

TextEncoder 只支持 UTF-8,把字符串编码为 Uint8Array(可含中文/emoji 等多字节字符),TextDecoder 按指定编码(UTF-8 为主,支持 UTF-16、GBK 等)把字节解码为字符串;二者处理"字符串 ↔ 字节"的跨语言边界(网络协议、文件、WebSocket 帧、WASM 内存互传)。要点:多字节字符——中文字符 3 字节、emoji 4 字节(代理对),按码元(UTF-16 单元)遍历或按码点切割都需注意边界;流式解码——TextDecoder(encoding, {stream:true}) 支持分块解码,块边界处的不完整多字节序列被内部缓存等待后续字节(用于流式读取、fetch chunk 解码),普通模式遇不完整序列会产生替换符;错误处理——fatal:true 时解码非法字节抛错(严格校验),否则替换为 U+FFFD;BOM——默认剥离 UTF-8 BOM,UTF-16 解码按 BOM 推断端序。工程上:跨语言系统传输文本统一 UTF-8 + 显式声明,接收端流式解码用 stream 选项防乱码。

本题考察文本编解码的边界处理。核心是"多字节序列的块边界(stream)"与"错误模式(fatal)"两大工程点,BOM 与代理对是细节;能说明流式解码防乱码的机制即展示实战认知。

#
★★

24. WeakRef 与 FinalizationRegistry 的能力边界(不可作为可靠缓存失效)

WeakRef 与 FinalizationRegistry 的能力边界是什么?为何不能作为可靠的缓存失效机制?

  • WeakRef 的弱引用读取与存活不确定性
  • 清理回调的时机不确定性
  • 缓存设计的正确替代方案

WeakRef 提供对对象的弱引用:deref() 在对象存活时返回对象、已回收返回 undefined,但"是否回收"由 GC 决定,时机完全不确定——对象可能在任意时刻被回收,也可能迟迟不回收(引擎激进/保守策略、内存压力等),因此不能依赖"弱引用仍在"判断对象存活。FinalizationRegistry 的回调在对象被回收后异步执行,但规范明确:回调时机不确定、不保证执行(进程退出前可能不触发)、执行顺序不保证,且与对象的回收解耦(清理可能延后很久)。由此"弱引用缓存"(key 强、value 弱引用、注册清理回调删缓存)并不可靠:缓存项可能"明明还在使用却被回收"(value 被 GC 拿走导致命中失败)或"已无引用却长时间占用清理回调"。正确替代:LRU 等显式容量控制的强引用缓存 + 主动失效(TTL、版本号、订阅事件);需要观察内存压力的场景用 performance.measureUserAgentSpecificMemory 或内存阈值策略。WeakRef/FinalizationRegistry 只用于辅助诊断与边缘资源关联,不作为功能正确性依赖。

本题考察弱引用机制的可靠性边界。核心是"GC 非确定性 → 弱引用缓存命中不可保证、清理回调时机不可依赖"的推理链,显式容量缓存是正确替代;能解释"明明还在却被回收"的机制即展示本质理解。

#
★★

25. performance.measureUserAgentSpecificMemory() 与跨域隔离前提

performance.measureUserAgentSpecificMemory() 如何度量内存?为何要求跨域隔离?

  • 返回内存使用明细(JS 堆、DOM、资源)
  • COOP/COEP 跨域隔离前提
  • 与 performance.memory 的对比

performance.measureUserAgentSpecificMemory() 返回 Promise,解析为内存使用明细:jsMemorySize(JS 堆内存)、domMemorySize(DOM 相关)、resourcesMemorySize(资源)、breakdown(按类型细分,含 attributions 按页面/iframe 归因),是标准化、细粒度的内存度量 API。跨域隔离前提:该方法要求页面满足跨域隔离(COOP: same-origin + COEP: require-corp),因为细分归属信息(哪些 iframe/worker 占多少内存)涉及跨源敏感数据,隔离后才能安全暴露;不满足时调用失败或返回被截断的结果。与遗留的 performance.memory(仅 Chrome、只给粗略 jsHeapSize/usedJSHeapSize 近似值且可能故意随机化)相比,新 API 精确且可归因。工程价值:内存泄漏监控的可靠数据源——周期采样对比峰值、按来源归因定位泄漏页面/组件;注意度量本身有开销(应抽样而非每帧调用),需在支持且隔离的环境中部署。

本题考察内存度量 API 的能力与前提。核心是"明细+归因"的价值与"跨域隔离"的安全前提,与 performance.memory 的对比是补充点;能说明采样策略即展示工程落地思维。

#
★★

26. BigInt 在 64 位整数与 WASM 交互的工程价值

BigInt 在 64 位整数处理与 WebAssembly 交互上有何工程价值?

  • Number 无法精确表示 64 位整数
  • BigInt 与 WASM i64 的原生互操作
  • 大 ID、时间戳与序列化边界

JS Number 只有 53 位整数精度,无法精确表示 64 位整数(如数据库自增 ID、雪花 ID、纳秒时间戳、文件大小),BigInt 提供任意精度整数解决该问题:大数计算(金融、哈希、组合数)不再溢出或丢失精度。WASM 交互:i64 是 WASM 的核心类型,JavaScript API 原生支持 BigInt 与 i64 的无损互转(WebAssembly 导出函数返回 i64 时以 BigInt 呈现、导入函数可接收 BigInt 参数),使 JS 能安全传递/计算 64 位整数而无需拆分为高低 32 位。工程价值:与后端 64 位 ID 对齐(后端返回 BigInt 或字符串,前端用 BigInt 运算)、大数算法(RSA、哈希链)、时间高精度场景;边界:JSON.stringify 不支持 BigInt(抛 TypeError,需自定义 replacer 转字符串)、BigInt 与 Number 禁止混算、部分工具链(旧版浏览器、跨 Realm 传输)需 polyfill;传输大整数建议统一字符串协议(JSON 中无 BigInt 字面量)。

本题考察大整数能力的工程定位。核心是"53 位精度边界 → BigInt 任意精度 → WASM i64 无损互转"的链条,序列化与混算边界是易错点;能给出 ID 传输的字符串协议即展示工程规范。

#
★★

27. URLSearchParams 在表单序列化与查询字符串解析的边界

URLSearchParams 在表单序列化与查询字符串解析上有哪些边界?

  • 构造与遍历查询字符串
  • 编码规则(空格、中文、特殊字符)
  • 与 URL/FormData 的协作边界

URLSearchParams 提供查询字符串的结构化处理:从字符串/URL/键值对数组/迭代器构造,get/getAll/has/set/append/delete/sort/toString 操作,for...of 按顺序遍历键值;toString() 自动按 application/x-www-form-urlencoded 规则编码(空格转 +、中文与特殊字符百分号编码),set/append 保证正确编码。边界:编码规则——同名字段可用 getAll 获取多个值,键值顺序保留(sort 可稳定排序,编码规则对 key 与 value 一致);解析歧义——"a=1=2" 中值含等号、无值字段("a")get 返回空串、重复键的删除语义(delete 移除全部同名);与 URL 协作——URL.searchParams 直接操作 URL 的查询部分(修改后需回写 URL.search 或字符串),URLSearchParams 自身不处理 #fragment 与路径;与 FormData 差异——FormData 专用于 multipart 文件上传,URLSearchParams 用于查询/表单 urlencoded,fetch body 传 URLSearchParams 自动带正确 Content-Type。工程上:参数拼接统一用 URLSearchParams 避免手写编码错误。

本题考察查询字符串 API 的边界细节。核心是"自动编码规则 + 同名多值与顺序语义 + 与 URL/FormData 的分工",手写拼接的编码坑是反面教材;能说明 + 与 %20 的差异即展示规范细节。

#
★★

28. Atomics.waitAsync 的异步等待

Atomics.waitAsync 与 Atomics.wait 有何区别?异步等待适合什么场景?

  • wait 的同步阻塞与主线程限制
  • waitAsync 的 Promise 化等待
  • 主线程参与共享内存同步的方式

Atomics.wait(int32Array, index, expected, timeout) 是同步阻塞等待:条件满足(值变化且被 notify 唤醒)或超时前阻塞当前线程,规范只允许在 Worker 中使用——主线程调用会抛 TypeError(阻塞主线程将冻结整个页面)。Atomics.waitAsync 提供同语义的异步版本:不阻塞线程,立即返回 {async:false, value:"not-equal"}(条件已不满足)或 {async:true, value: Promise}(进入等待,唤醒后兑现 "ok"/"timed-out"),且主线程与 Worker 均可使用。价值:主线程参与共享内存协作(如协调 WASM 计算、等待共享状态就绪)而不冻结 UI;Worker 中用于避免阻塞时的资源浪费(异步等待期间可处理其他任务)。注意:waitAsync 的 Promise 化等待是"微任务"层面的恢复(唤醒后排队执行),不适合要求确定性时序的紧耦合场景;配合 notify 的 count 与超时设计避免永久悬挂;等待条件仍需循环复查(防止假唤醒/竞态)。

本题考察同步与异步等待的边界。核心是"wait 阻塞+仅 Worker vs waitAsync 非阻塞+全上下文可用"的差异及主线程场景价值,返回结构(not-equal/ok/timed-out)是细节;能说明条件复查与超时设计即展示工程严谨性。

#
★★

29. JSON 序列化限制(undefined、函数、Symbol、循环引用)

JSON 序列化对 undefined、函数、Symbol 与循环引用有何限制?

  • 值的过滤与数组 null 化
  • toJSON 与 replacer 的干预
  • 循环引用的抛错行为

JSON.stringify 只处理 JSON 可表示的数据,规则:undefined、函数、Symbol——作为对象属性值时被跳过(不进入输出)、作为数组元素时转为 null、顶层直接 stringify 时返回 undefined;NaN/Infinity 转为 null;Date 经 toJSON 转为 ISO 字符串。toJSON 方法可自定义对象序列化(返回被序列化的替代值);replacer(函数或数组)可选择性过滤/改写字段。循环引用:JSON.stringify 遇到循环引用抛 TypeError("Converting circular structure to JSON"),没有默认容忍——需用 replacer 检测(维护已访问集合跳过)或在数据层避免(不可变/树形结构)。反序列化 JSON.parse 的 reviver 可还原自定义类型。工程影响:日志上报前必须清理不可序列化字段(序列化失败静默返回 undefined 或抛错导致上报中断)、redux 状态含函数时持久化需显式 replacer、深拷贝历史场景中 JSON 方案无法处理这些边界(现代用 structuredClone)。

本题考察 JSON 序列化的边界规则。核心是"三值被忽略/置 null/undefined"的规则矩阵与循环引用抛错,toJSON/replacer 是干预手段;能说明日志上报前的清理实践即展示工程经验。

#
★★

30. ArrayBuffer 与 TypedArray 在二进制数据处理(如 WebAssembly、图片解码)

ArrayBuffer 与 TypedArray 在 WebAssembly、图片解码等二进制数据处理中如何应用?

  • 二进制数据的获取与视图解析
  • WASM 线性内存与 JS 共享
  • 图片解码的数据流转

二进制数据进入 JS 后以 ArrayBuffer 承载,TypedArray 提供按类型视图:fetch 的 arrayBuffer() 获得原始字节、response.body 流式读取到缓冲;Uint8Array 用于字节级操作(协议解析、拼包),Int32Array/Float32Array 用于数值批量处理(向量、矩阵),DataView 用于混合结构与指定端序。WebAssembly:WASM 线性内存(Memory)可经 WebAssembly.Memory 与 JS 共享——jsArray.buffer 获取底层 ArrayBuffer 做零拷贝读写(大数组传入传出、结果读回),i64 用 BigInt 交互;wasm 内存增长时 ArrayBuffer.transfer 配合更新引用。图片解码:ImageDecoder(WebCodecs)或 createImageBitmap 把编码字节解码为位图(可 transfer 到 Worker/Canvas),像素数据(RGBA 字节)用 Uint8ClampedArray 处理(合成、滤镜、缩放),再 putImageData 上屏。工程要点:字节序(跨端格式用 DataView)、对齐与生命周期(detach 后不可用)、大缓冲用 transfer 而非拷贝。

本题考察二进制数据处理的全链路。核心是"获取(fetch/WASM)→视图(TypedArray/DataView)→处理(像素/数值)"的管线及零拷贝协作(WASM 内存共享、transfer),端序与生命周期是易错点;能描述图片解码的数据流即展示完整认知。

#
★★

31. structuredClone 的循环引用与不可克隆对象的错误处理

structuredClone 如何处理循环引用与不可克隆对象?错误如何分类?

  • 循环引用的克隆支持
  • DataCloneError 的触发条件
  • 错误处理与降级策略

structuredClone(value, {transfer}) 基于结构化克隆算法:循环引用被正确支持(内部维护"源对象→克隆"映射,重复引用共享同一克隆,克隆结果保持环结构),Date/RegExp/Map/Set/TypedArray/ArrayBuffer/Blob 等类型均有对应克隆语义。不可克隆对象抛 DataCloneError:函数、DOM 节点(Element/Window 等宿主对象)、Symbol、Error 等内部槽/宿主绑定对象——错误类型统一为 DOMException 的 DataCloneError,可通过 err.name === "DataCloneError" 或 instanceof DOMException 判断。错误处理策略:克隆前校验数据形态(白名单类型检查)或 try/catch 捕获后降级(返回引用/浅拷贝/序列化字符串);对已知含函数/节点的结构,先用 replacer 式清洗(提取可克隆字段)再克隆;批量克隆失败时区分"单条失败"与"整体失败"决定重试策略。工程上:状态快照(时间旅行)、跨上下文传输、避免外部修改的隔离拷贝都用 structuredClone,但需在边界层统一兜底。

本题考察结构化克隆的边界处理。核心是"循环引用支持 + 不可克隆类型抛 DataCloneError"两个机制与"预处理/降级"的工程对策;能按错误类型分类处理即展示健壮性设计。

#
★★

32. JSON.stringify 的 replacer 与 reviver 在自定义序列化与日期对象的应用

JSON.stringify 的 replacer 与 JSON.parse 的 reviver 在自定义序列化与日期处理上有哪些应用?

  • replacer 的选择性过滤与字段改写
  • reviver 的还原与类型重建
  • 日期序列化的往返一致

replacer 参数(函数或键数组)控制 stringify 的输出:函数形式 (key, value) 返回 undefined 删除该字段、返回替换值改写内容(如隐藏敏感字段、BigInt 转字符串、归一化字段名),数组形式只序列化列出的键——用于日志脱敏、体积控制、协议适配。reviver 参数 (key, value) 在 parse 时逐字段回调:可在还原阶段重建自定义类型(识别特殊标记如 __type 后 new Date 还原、BigInt 字符串还原、Map/Set 重建)、修复历史数据结构(字段迁移)。日期应用:Date 默认经 toJSON 序列化为 ISO 字符串(UTC),parse 后得到字符串而非 Date——要"往返一致"需 reviver 检测 ISO 格式重建 Date(或统一用时间戳 + reviver 转换);注意时区(toJSON 是 UTC,展示需本地化)。工程实践:序列化协议文档化(标记方案、版本号字段)、replacer/reviver 对称设计(写读一致)、严格模式下把无法序列化的值显式剔除而非依赖隐式规则。

本题考察序列化的双向定制。核心是"replacer 过滤改写 + reviver 还原重建"的对称设计,日期往返是经典案例;能给出带版本号的协议设计即展示工程规范意识。

#
★★

33. FileReader.readAsArrayBuffer 一次性读取与 File.stream() 流式读取在大文件场景的内存与体验取舍

FileReader.readAsArrayBuffer 一次性读取与 File.stream() 流式读取在大文件场景有何取舍?

  • 一次性读取的整块内存占用
  • 流式读取的分块与背压
  • 大文件上传/预览的体验差异

FileReader.readAsArrayBuffer(file) 一次性把整个文件读入内存(ArrayBuffer):实现简单、结果可直接用,但内存占用与文件大小成正比——大文件(几百 MB 以上)会占用大块连续内存,可能触发 GC 压力甚至页面崩溃,且必须等全部读完后才能开始处理(延迟高)。File.stream() 返回 ReadableStream:分块(默认约 64KB 每块)读取,配合 for await...of 或 pipeTo 边读边处理(如分片上传、逐块哈希、流式预览),内存占用近似恒定 O(chunk);背压由流机制控制(消费慢则停止拉取),体验上首字节可立即处理、进度可见。取舍:小文件(< 数十 MB)用 readAsArrayBuffer/File.text() 简单直接;大文件/需要流式处理用 stream() 分块;上传场景还有 File.slice 手动分片 + 并发控制(避免单块过大)、断点续传;预览大图/大视频用 objectURL 而非读入内存。注意 FileReader 的 onerror 与流式读取的异常处理,两者都需考虑取消(abort/reader.cancel)。

本题考察大文件读取的内存模型。核心是"整块读入 vs 分块流式"的内存/延迟对比与背压机制,按文件规模选型是落点;能给出分片上传的完整策略即展示实战能力。

#
★★

34. atob/btoa 处理非 Latin1 字符(中文/emoji)的陷阱与 Uint8Array.prototype.fromBase64/toBase64(Base64 方法提案)的现代方案

atob/btoa 处理中文/emoji 等非 Latin1 字符有何陷阱?现代 Base64 方案如何解决?

  • btoa 对码值 >255 抛 InvalidCharacterError
  • 传统的中文绕行方案(encodeURIComponent)
  • Uint8Array 的 toBase64/fromBase64 提案

btoa(str) 把二进制字符串编码为 Base64,但只接受"每字符码值 ≤ 255"的 Latin1 范围字符串:传入中文/emoji(码值 >255)直接抛 InvalidCharacterError;atob 解码结果同样是 Latin1 字符串,中文会乱码。传统绕行方案:先 encodeURIComponent(str) 把非 ASCII 转为 %XX 转义序列(纯 ASCII),再 btoa——解码反向 atob 后 decodeURIComponent,能正确传输 Unicode,但体积膨胀且转义规则有边界(lone surrogate 会抛错)。现代方案:Uint8Array.prototype.toBase64()/fromBase64()(Base64 方法提案,ES2025)直接从字节层面编解码:new TextEncoder().encode(str).toBase64() 编码 UTF-8 字节、fromBase64().decode()(TextDecoder)还原,天然支持中文/emoji、无 Latin1 限制、支持 URL-safe 变体(base64url)与 BufferSource 输入,性能优于字符串绕行。工程上:新代码用 Uint8Array 方案(先 polyfill),旧接口用 encodeURIComponent 兼容,传输大块二进制用 Base64 前考虑压缩或二进制传输。

本题考察 Base64 编码的字符边界。核心是"btoa 的 Latin1 限制 → 转义绕行 → 字节级现代 API"的演进,UTF-8 字节先编码是正确心智;能说明 lone surrogate 的转义陷阱即展示细节。

#
★★

35. MessagePack、Protobuf、CBOR 相比 JSON 在前端序列化体积与性能的取舍

MessagePack、Protobuf、CBOR 相比 JSON 在前端序列化体积与性能上有何取舍?

  • 二进制格式的体积优势(无引号/键名紧凑)
  • 编码解码速度与工具链
  • 可读性、兼容性与工程成本

二进制序列化格式(MessagePack、CBOR、Protobuf)相比 JSON 的体积优势:JSON 冗余来自键名引号、逗号分隔与文本编码(数字/字符串的文本表示),二进制格式用紧凑标记+定长数值,典型场景体积减少 30%-50%;MessagePack/CBOR 是"JSON 的二进制变体"(结构映射、无 schema),Protobuf 需要 .proto schema、用字段编号替代键名(体积最小但需编译生成代码与版本管理)。性能:二进制编码/解码通常更快(直接内存操作),但 Protobuf 需要生成的编解码器(首轮开销在生成与加载),MessagePack 可用动态库;JSON 由引擎原生实现(JSON.parse 极快),小数据量下差距不明显。取舍:数据量小/跨端通用/调试需求高用 JSON;大数据块、高吞吐传输(WebSocket 协议、存储、离线数据)用二进制;团队有 schema 治理(版本、类型)能力选 Protobuf,无 schema 想简单压缩选 MessagePack/CBOR;注意二进制格式的可读性差(DevTools 需插件)、与 JSON 互操作的转换成本、压缩(gzip)对体积差距的影响(文本高压缩率可能拉平差距)。

本题考察序列化格式的工程选型。核心是"体积(键名/文本冗余)vs 速度 vs 工具链成本"的三维比较与 schema 治理能力门槛,gzip 拉平效应是深度细节;能给出按场景的决策表即展示系统思维。

#
★★

36. Web Streams 在大文件分片读取与上传(Fetch + ReadableStream)

Web Streams 如何实现大文件分片读取与上传(Fetch + ReadableStream)?

  • File.stream() 与分片读取
  • 请求体使用 ReadableStream
  • 背压与进度的平衡

大文件上传的流式实现:File.stream() 得到 ReadableStream,可以直接作为 fetch 请求体(fetch(url, {method:"POST", body: file.stream(), duplex:"half"}))让浏览器流式读取文件上传,或先切片(file.slice(start, end) 生成子 Blob,再 stream())做分片并发上传(每片独立请求,支持重试与断点续传)。ReadableStream 作请求体的要点:必须设置 duplex:"half"(半双工流式请求,双向不可同时);用 TransformStream 可实时转换(如逐块计算哈希、加密、压缩)再输出到请求体;进度上报需手动统计已消费块数(流式上传原生事件无总进度,配合 Content-Length 与已读字节计算)。背压与体验:流式读取内存恒定、可边读边传(无需整块入内存),配合并发分片(限制在途请求数)平衡吞吐与服务端压力;取消用 AbortController 中止请求与 reader.cancel 停止读取。注意:老浏览器/服务器对流式请求体支持差异,需特性检测与回退(FormData 整传)。

本题考察流式上传的实现链路。核心是"File.stream 作请求体 + duplex 半双工 + 分片并发"的组合与进度/取消的工程细节;能说明 duplex:"half" 的必要性即展示协议级认知。

#
★★

37. 二进制帧(Binary Frame)在 WebSocket 传输高性能数据的应用

二进制帧(Binary Frame)在 WebSocket 传输高性能数据上有哪些应用与注意点?

  • 文本帧 vs 二进制帧(blob/arraybuffer)
  • 二进制协议设计(消息头、长度、类型)
  • 性能与内存的协作

WebSocket 支持文本帧与二进制帧:二进制帧(message.data 为 Blob 或 ArrayBuffer,取决于 binaryType)适合传输非文本数据(音频/视频流、图像、数值矩阵、压缩包),避免文本编码的体积膨胀与解析开销。应用:实时协作(白板笔画、二进制快照同步)、游戏状态(坐标/输入打包)、音视频传输、设备协议透传。二进制协议设计要点:自定义帧结构(魔数/版本、消息类型字节、负载长度、payload),长度字段防粘包(消息边界 + 分片重组)、序号与校验(CRC)提升可靠性;接收端用 DataView 按端序解析、Uint8Array 拼接分片(注意内存拷贝,可预分配缓冲池)。注意点:binaryType 设置("arraybuffer" 免去 Blob 转换)、大帧的背压(WebSocket 缓冲过高会内存膨胀,需监测 bufferedAmount 并限流)、与文本消息的混用约定(类型字段区分)、压缩(permessage-deflate 对已压缩二进制无效)。工程上封装收发编解码层,业务层只面对结构化数据。

本题考察二进制消息的协议化传输。核心是"二进制帧承载非文本 + 自定义帧结构解决边界/类型/校验"的设计与背压治理,binaryType 与缓冲监测是细节;能给出帧格式设计即展示协议能力。

#
★★

38. Set/Map 的迭代顺序与对象键枚举顺序在不可变更新的一致性工程价值

Set/Map 的迭代顺序与对象键枚举顺序在不可变更新中有何一致性工程价值?

  • Map/Set 的插入序迭代保证
  • 对象键的整数键升序+字符串插入序规则
  • 顺序一致性对序列化/渲染/比较的影响

Map/Set 保证按插入顺序迭代(set 后再赋值不改变位置、删除再插入会移到末尾);对象自有键枚举遵循规范顺序:整数索引键(非负数字字符串)升序在前、其余字符串键按插入序、Symbol 键最后——for...in/Object.keys/Reflect.ownKeys/JSON.stringify 均遵循该顺序(整数键部分)。一致性价值:序列化稳定——相同构建过程产出相同键序,JSON.stringify 输出确定(哈希签名、缓存键、diff 比较可依赖);渲染顺序稳定——遍历结果确定(列表渲染、图表数据点顺序);不可变更新中"顺序即状态"——Map 的顺序可承载业务语义(优先级、队列),更新时按插入序保留,配合结构共享的引用比较,顺序不变的更新可跳过重渲染。注意:对象整数键的自动升序可能违背插入期望(如键 "2"、"10" 排序为 2、10),需要顺序语义时用 Map 或数组;delete 后重加改变位置,幂等更新需显式排序。

本题考察集合顺序语义及其工程影响。核心是"插入序(Map/Set)与整数键升序规则(对象)"两条顺序保证,序列化稳定性与渲染确定性是价值落点;能指出整数键排序的坑即展示规范细节。

#
★★

39. Array.prototype.toSorted/toReversed/toSpliced(ES2023)的非破坏性更新

ES2023 的 toSorted/toReversed/toSpliced 非破坏性方法有何价值?与破坏性版本有何差异?

  • 返回新数组的不可变语义
  • 与 sort/reverse/splice 的对称关系
  • 函数式更新与响应式兼容

ES2023 为数组新增非破坏性版本:toSorted(排序返回新数组)、toReversed(反转新数组)、toSpliced(增删返回新数组)、with(替换指定索引返回新数组)——原数组完全不变,符合不可变更新理念,避免 sort/reverse/splice 就地修改的副作用(尤其共享数组被意外改动、React/Vue 中直接修改状态数组的问题)。差异:参数与行为与对应破坏性方法对齐(toSorted 的 compareFn、toSpliced 的 (start, deleteCount, ...items)、with 的 (index, value)),返回全新数组(元素引用共享,非深拷贝);稀疏数组处理保持一致(toSorted 对空洞按 undefined 排序等)。工程价值:函数式管道(filter→toSorted→toReversed 不污染中间结果)、状态管理(setState 传新数组天然触发更新、便于 memo 比较)、避免"修改了调用方数据"的隐式 bug。注意:大数组全量拷贝有开销(需要"排序但保留原数组"时才用),原地排序仍用于性能敏感且允许破坏的场景。

本题考察非破坏性数组方法的语义。核心是"返回新数组、原数组不变"与破坏性版本的对称关系,函数式更新与框架兼容是价值点;能说明元素浅共享与拷贝开销的边界即展示全面认知。

#
★★

40. findLast/findLastIndex 在反向查找的工程价值

findLast/findLastIndex 在反向查找上有何工程价值?与手写反向循环相比如何?

  • 从末尾开始查找的语义
  • 返回元素/索引的差异
  • 可读性与边界处理

findLast(predicate) 从数组末尾向前查找,返回第一个满足条件的元素(无匹配返回 undefined);findLastIndex 返回对应索引(无匹配返回 -1);ES2023 标准方法,语义与 find/findIndex 对称。工程价值:需要"最近一次"语义的场景(最新日志匹配、最后一条满足条件的记录、撤销栈最近的可用项、时间序数据按最新状态查找)无需先反转数组(reverse 会破坏原数组、拷贝反转有开销)或手写递减循环(易错:长度变化、稀疏空洞、thisArg 处理)。与手写反向循环相比:内置方法处理稀疏数组语义明确(跳过空洞)、thisArg 支持、代码声明式可读;边界注意:匹配"undefined"值元素时 findLast 返回 undefined 与"无匹配"无法区分(用 findLastIndex 或 includes 判断)、谓词副作用与执行顺序(严格从末尾到开头)。工程上:数据按时间追加的场景用 findLast 表达"最新",避免维护额外索引。

本题考察反向查找方法的语义价值。核心是"末尾起查+最近一次语义"与替代方案(反转/手写循环)的对比,undefined 歧义是易错点;能结合时间序数据场景即展示应用价值。

#
★★

41. pipe 数据链的中间值可读性治理

pipe 数据链的中间值可读性如何治理?

  • 长管道的可读性退化
  • 命名函数与中间观测
  • 拆分与文档化策略

长 pipe 链(十几个无参箭头函数串联)的常见问题:每个环节是匿名单参函数,无法从调用处理解"这一步在做什么",调试时栈帧与断点难以定位具体环节,中间值不可见。可读性治理策略:命名化——每步用具名函数(或从模块导入的语义化函数),调用处即文档,栈错误直接指向具名函数;观测点——在关键步骤插入 tap/peek 式函数(打印或记录当前值后原样透传),配合日志级别控制,排查数据流问题;拆分——超过 5-7 步的管道拆为多个具名中间管道(const prepare = pipe(...); const transform = pipe(...)),各自可独立测试,主链调用子管道;结构化——为中间值定义类型(TS 接口),每步输入输出显式标注,编译期即发现形状不匹配;文档——管道头部注释数据流图(输入→每步输出类型)。权衡:观测点与命名增加样板,重点环节优先治理,热路径避免额外闭包分配。

本题考察函数式管道可维护性的工程手段。核心是"命名化 + 观测点 + 拆分 + 类型标注"的组合治理,栈可读性改善是主要动机;能给出 tap 实现示例即展示实战技巧。

#
★★

42. 引用透明与时间/随机数破坏纯函数的关系

引用透明是什么?时间/随机数为何会破坏纯函数与引用透明?

  • 引用透明的定义(表达式可被值替换)
  • 时间、随机数、I/O 的不可预测输入
  • 纯度边界与依赖注入

引用透明(referential transparency)指表达式可用其计算结果替换而不改变程序行为——这是纯函数(确定性 + 无副作用)的形式化表达:相同输入必得相同输出,且调用不产生外部影响。时间(Date.now、performance.now)与随机数(Math.random、crypto.randomUUID)破坏引用透明:它们的输出依赖不可预测的外部状态(时钟、熵源),同一"输入"多次调用结果不同,表达式不可被单一值替换;I/O(读写、网络、DOM)同理,还引入副作用。工程影响:含时间/随机数的函数不可 memoize、不可安全重放(测试、时间旅行)、并发/重试下结果不稳定,调试时难以复现。纯度治理:把不确定源注入为参数——now 依赖改为传入 timestamp(或依赖注入时钟对象)、随机数生成器改为传入或可 seed 的 RNG,业务逻辑保持纯函数,不确定性集中在调用边界;测试用固定种子/固定时间 mock 验证确定性路径。

本题考察函数式纯度的破坏源。核心是"引用透明=可替换性"的定义与时间/随机数的不可预测性如何破坏它,依赖注入是恢复纯度的工程手段;能给出时钟注入的代码模式即展示实践。

#

43. WebAssembly Memory 与 JavaScript ArrayBuffer 的互操作工程实践

WebAssembly Memory 与 JavaScript ArrayBuffer 如何互操作?工程实践上有哪些注意点?

  • Memory 与 ArrayBuffer 的共享关系
  • 零拷贝数据交换
  • 增长与 detach 的处理

WebAssembly.Memory 的 buffer 属性直接暴露底层线性内存的 ArrayBuffer:JS 侧创建 Uint8Array/TypedArray 视图即可零拷贝读写 WASM 内存(传参、取结果、共享数据区),WASM 侧同样操作该内存,两侧共享同一份字节。工程实践:数据传入——JS 把输入写入共享缓冲、调用导出函数、函数处理后从缓冲读回(避免逐值传递的编解码开销);内存增长——memory.grow 后 buffer 会替换为新 ArrayBuffer(旧缓冲 detach),JS 侧必须重新获取 buffer 并重建视图(监听 growth 事件或调用后刷新引用);生命周期——不要把 buffer 视图长期缓存(detach 后失效),使用后及时释放引用;共享并发——多线程 WASM 用 SharedArrayBuffer 内存需 Atomics 同步;安全——WASM 内存读写无边界检查的代码段风险在宿主侧校验长度;容量规划——初始内存按需设置(浪费会占用真实内存),大内存场景考虑 grow 策略与 transfer 配合。

本题考察 WASM 内存互操作的实践要点。核心是"buffer 暴露 + 视图零拷贝 + grow 后 detach 重建"的机制链,视图缓存失效是最常见坑;能说明 growth 事件处理即展示实战经验。

#

44. CompressionStream/DecompressionStream 在请求体与响应的压缩应用

CompressionStream/DecompressionStream 在请求体与响应压缩上有哪些应用?

  • 流式压缩与解压的 TransformStream
  • 请求体压缩与响应解压
  • 与 HTTP 层压缩的关系

CompressionStream(gzip/deflate/deflate-raw)与 DecompressionStream 是基于 TransformStream 的编解码器:fetch 响应体流 pipeThrough(new DecompressionStream("gzip")) 即可流式解压(如大文件下载解压、.gz 资源读取);请求方向 body 数据流 pipeThrough(new CompressionStream("gzip")) 后作为请求体发送,实现"边压缩边上传"(日志上报、离线数据同步的传输体积优化)。应用:动态生成压缩包(多文件流汇入 CompressionStream)、读取压缩格式(gzip 日志、地图瓦片)、WebSocket 二进制压缩传输。与 HTTP 层压缩的关系:标准 HTTP 场景由 Content-Encoding 协商(服务器自动 gzip/br),JS 侧压缩用于"HTTP 压缩之外"的场景(自定义协议、非标准路径、加密后再压缩、存储层压缩);注意压缩流输出的字节型流(Uint8Array 流),上游输入可以是字符串(自动编码)或字节;压缩算法与参数不可配置(gzip 级别固定),要求细粒度控制需第三方库(fflate/pako);解压错误(损坏数据)会使管道 errored,需捕获。

本题考察流式压缩的工程应用。核心是"TransformStream 形态 + 请求/响应双向接入 + 与 Content-Encoding 的分工",算法不可配置与错误处理是边界;能区分 HTTP 层压缩与 JS 层压缩的场景即展示网络协议认知。

#

45. JSON.parse 的 reviver 在日期与自定义类型的反序列化工程实践

JSON.parse 的 reviver 在日期与自定义类型的反序列化上有哪些工程实践?

  • reviver 的逐字段回调顺序(深度优先)
  • 日期与自定义类型的还原模式
  • 版本迁移与校验

reviver(key, value) 在 parse 完成后按"深度优先、自底向上"顺序对每个键值对回调:叶子节点先、容器后(容器在子节点全部处理完后收到处理后的新值),返回值替换当前字段,返回 undefined 删除字段。工程实践:日期还原——识别约定标记(如字段名以 Date 结尾、或 value 为 ISO 字符串且键名在白名单)后 new Date(value);更通用的自定义类型还原——数据带类型标记({__type:"Money", value:...} 或版本字段),reviver 按标记构造实例(BigInt 字符串还原、Map/Set 重建、Decimal 等);注意 reviver 内不要做重逻辑(每字段调用,性能敏感)。配套工程:序列化与反序列化对称设计(stringify 的 replacer 写标记,parse 的 reviver 读标记);版本迁移——旧数据无标记时在 reviver 做兼容分支(字段默认值、格式转换),或 parse 后用迁移函数统一处理;严格模式——未知字段/类型标记非法时抛错或丢弃,保证数据形状契约。测试上固定样例数据验证往返一致(round-trip)。

本题考察反序列化的定制机制。核心是"reviver 自底向上回调 + 类型标记协议 + 版本兼容"的完整实践,对称设计与往返测试是工程保障;能给出带版本号的迁移策略即展示长期维护思维。

#

46. BigInt 在高精度计算(金融、ID)的序列化(JSON.stringify)边界

BigInt 在金融、ID 等高精度计算中的序列化边界是什么?

  • JSON.stringify 对 BigInt 抛 TypeError
  • 序列化方案(字符串协议/replacer)
  • 传输与存储的一致性

金融金额(分/厘计算)、大整数 ID(雪花 ID、订单号)用 BigInt 保证精度,但序列化有硬边界:JSON.stringify 遇到 BigInt 直接抛 TypeError("Do not know how to serialize a BigInt"),JSON.parse 也没有 BigInt 语法(1n 不是合法 JSON),因此 BigInt 数据无法直接通过 JSON 传输/存储。工程方案:字符串协议——BigInt 序列化为字符串(如 "9007199254740993"),接收端 new BigInt(str) 还原,是跨端最稳妥的方式(后端语言普遍支持大整数字符串或原生 bigint);replacer 定制——stringify 时把 BigInt 转字符串或带标记对象({type:"bigint", value:"..."}),reviver 对称还原;体积与性能考量——字符串形式比二进制多占字节,高频场景可考虑十进制字符串压缩或转二进制(但破坏可读性)。一致性风险:混用 Number 与 BigInt 字段导致精度不一致(Number 侧已丢精度)、第三方库/日志直接 stringify 崩溃、数据库 ORM 的字段类型映射(string vs bigint)需统一;协议文档应规定大整数一律字符串。测试往返一致(round-trip)与精度边界(>2^53)用例必须覆盖。

本题考察大整数序列化的协议设计。核心是"BigInt 无法 JSON 序列化 → 字符串协议 + replacer/reviver 对称"的解法与混用精度风险,协议统一是治理关键;能说明跨端字符串约定的价值即展示工程规范。

#

47. ReadableStreamDefaultController 在背压(backpressure)处理的工程取舍

ReadableStreamDefaultController 如何实现背压处理?工程上有哪些取舍?

  • enqueue/desiredSize 与队列水位
  • pull 回调的按需拉取
  • 背压策略(速率限制、丢弃、阻塞)

自定义 ReadableStream 时,控制器(ReadableStreamDefaultController)管理内部队列:enqueue 加入数据、controller.desiredSize 表示"还可接收多少数据"(队列高水位与当前长度的差值,负值表示消费者落后),引擎在消费者读取后按需调用 pull 源回调补充数据——这正是背压机制:消费者慢时队列堆积、desiredSize 变负、pull 不再被调用(源暂停生产);消费者快时 pull 被调用补充。工程取舍:生产者昂贵(数据库查询、传感器、网络请求)时利用背压"按需生产",避免过量拉取;高吞吐场景可调高队列水位(highWaterMark 构造参数)批量产出减少拉取频率;消费者处理慢且数据不能丢时让背压自然传导(enqueue 不限制但消费者自动限速),可丢场景用丢弃策略(超水位丢弃/采样)控制内存;注意 enqueue 返回值为未实现标准的旧语义(不要依赖),检测 desiredSize 手动节流更可靠;流取消(cancel)时源应清理资源(关闭查询、断开连接)。

本题考察流式生产的背压模型。核心是"enqueue + desiredSize + pull 按需拉取"的反馈环与 highWaterMark 调节,生产者昂贵场景是背压的价值场景;能说明取消清理即展示资源管理意识。

#

48. 二进制协议在 WebTransport(HTTP/3)相较 WebSocket 的工程性能优势

二进制协议在 WebTransport(HTTP/3)相较 WebSocket 有哪些工程性能优势?

  • WebTransport 的流/数据报模型
  • 无序可靠性与头阻塞消除
  • 拥塞控制与多路复用

WebTransport 基于 HTTP/3(QUIC):支持双向流(流内有序可靠)与数据报(无顺序保证、尽力投递)两类通道,相比 WebSocket(单一有序字节流 + 帧),性能优势:多路复用——多个流独立传输,一个流的慢速/阻塞不影响其他流(WebSocket 单连接内帧共享一个字节流,队头阻塞影响所有消息);无序通道——数据报适合实时游戏状态、音视频、遥测(迟到数据无需重传等待,旧状态直接丢弃),减少时延抖动;原生拥塞控制与连接迁移——QUIC 处理丢包恢复、0-RTT 重连,移动网络切换不断连;二进制帧——数据报/流的字节负载直接对应二进制协议,无 WebSocket 帧协议开销。工程取舍:低延迟强实时(多人同步、直播互动、多路并行传输大文件)用 WebTransport,浏览器支持仍有限(Chrome 系为主,需安全上下文)且 HTTP/3 需要服务端支持;二进制协议设计(流 ID 语义、分片、校验)仍是应用层职责;生态与调试工具(Wireshark QUIC 支持、DevTools)成熟度低于 WebSocket,需权衡部署成本与收益。

本题考察新一代传输协议的架构优势。核心是"多流消除队头阻塞 + 数据报无序可靠 + QUIC 拥塞控制"三个机制差异,实时场景是价值落点;能说明支持矩阵与部署成本的边界即展示理性选型。