新特性与手写实现

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

1. Math.sumPrecise 在数值累加精度提升的边界(替代 Kahan 求和)

Math.sumPrecise 如何提升数值累加精度?与 Kahan 求和相比边界是什么?

  • 浮点累加的误差累积问题
  • sumPrecise 的精确求和算法
  • 与手写补偿算法的取舍

浮点累加(arr.reduce((a,b)=>a+b, 0))在大量小数值或正负交替的数组上会累积舍入误差:每次加法都按当前精度舍入,误差沿链放大。Math.sumPrecise(ES2025)是标准化的精确求和:内部采用改进的补偿算法(类 Kahan/Neumaier 思想),对数值数组做更精确的累加,返回结果显著优于朴素求和;它在规范层面保证比逐项相加更精确(不保证完全精确到最后一位,但误差大幅降低)。边界:sumPrecise 接收可迭代数值,混合 BigInt 会抛 TypeError(不允许混算);对性能敏感的超大数组,sumPrecise 的补偿计算比朴素 reduce 慢(常数倍);精度需求极高(金融、科学计算)时,可继续用 Decimal/BigInt 精确算术或显式 Kahan 实现(sumPrecise 是引擎实现,无法定制与调试);返回值仍是 Number(双精度),超过表示范围的场景仍需 BigInt。工程取舍:默认累加用 sumPrecise 获得更优精度与简洁 API,热点路径先基准测试。

本题考察精确求和的标准化方案。核心是"补偿求和思想 + 比朴素累加更精确 + 不改变双精度表示范围"的边界,与手写 Kahan 的对比是落点;能说明 BigInt 混算限制即展示细节。

#
★★★

2. 上线 Temporal/Decorators 前的浏览器支持矩阵、Polyfill 与测试一致性核查

上线 Temporal/Decorators 前,浏览器支持矩阵、Polyfill 与测试一致性如何核查?

  • 特性支持矩阵与基线制定
  • polyfill 的选取与实现质量
  • 跨环境测试一致性策略

上线新标准特性(Temporal、Decorators、Iterator Helpers 等)前的核查流程:支持矩阵——查 caniuse/MDN 的浏览器版本覆盖,按业务流量设定基线(如"近 12 个月使用浏览器占比 95%"),明确特性在哪些环境可用;polyfill 策略——选权威实现(如 Temporal 官方 polyfill、core-js 的对应模块)并评估:实现完整性(是否覆盖全部 API 与边界行为)、体积(按需引入)、性能(polyfill 用 JS 模拟,慢于原生)、与原生行为的一致性(规范符合度测试);构建侧把 polyfill 与业务代码分离,运行时特性检测后决定加载。测试一致性核查——同一套测试在"原生支持环境 + polyfill 环境 + 不支持环境"三态跑通:行为差异(如 Temporal polyfill 的时区数据完整性与原生差异)、错误类型一致性(TypeError vs RangeError)、性能回归(polyfill 路径的耗时基准)。治理:建立特性登记表(支持状态、polyfill 版本、风险等级),发布前 CI 跑三态测试矩阵。

本题考察新特性的发布治理。核心是"支持矩阵定基线 → polyfill 选型评估 → 三态测试矩阵"的流程化核查,行为一致性是核心风险;能给出登记表与 CI 矩阵的治理思路即展示工程成熟度。

#
★★★

3. ES2026 已纳入特性(Map.getOrInsert/getOrInsertComputed、Error.isError、Uint8Array to/from Base64、Array.fromAsync、Math.sumPrecise)的工程价值

ES2026 纳入的 Map.getOrInsert/getOrInsertComputed、Error.isError、Uint8Array Base64、Array.fromAsync、Math.sumPrecise 有何工程价值?

  • 各特性的语义与替代写法
  • 工程场景的落地价值
  • 与现有 API 的衔接

ES2026 特性逐一落地工程价值:Map.getOrInsert(key, value) 在键存在时返回现有值、不存在时插入并返回(替代手写 has/get/set 三步),getOrInsertComputed(key, fn) 按需计算默认值(懒初始化,避免无条件构造开销),适合缓存表、默认配置、计数器的原子化读写;Error.isError(value) 跨 Realm 可靠判断 Error(替代 instanceof Error 与 toString 检测,跨 iframe/Worker 场景正确);Uint8Array.prototype.toBase64/fromBase64 原生字节级 Base64(替代 atob/btoa 的 Latin1 限制与手写算法,支持 URL-safe 变体);Array.fromAsync(iterable) 把异步可迭代对象/含 Promise 的数组物化为数组(替代手写 for await 收集);Math.sumPrecise 提升浮点累加精度。共同价值:减少手写样板与边界 bug、统一语义(跨 Realm、字节级)、声明式替代命令式循环;工程接入注意浏览器支持阶段、polyfill(core-js)与类型定义更新,老代码逐步迁移并保留回退。

本题考察一批新特性的整体价值。核心是"每个特性解决的具体痛点与替代写法"的映射,跨 Realm 判断与懒初始化是典型收益;能给出迁移路径(polyfill + 渐进替换)即展示落地思维。

#
★★★

4. import defer 提案(Stage 3)的延迟模块求值

import defer 提案(Stage 3)的延迟模块求值语义是什么?有哪些工程价值?

  • defer 声明与运行时加载时机
  • 与静态/动态 import 的对比
  • 冷启动与按需模块的优化

import defer 提案为 ESM 增加延迟导入语义:import defer * as mod from "./heavy.js" 只把模块加入"已声明未加载"集合——模块的获取与求值延迟到首次访问其导出时才发生(首次访问时同步完成加载与求值),与静态 import(模块图求值时立即执行)和动态 import()(返回 Promise、异步、需 await)都不同。工程价值:把"当前代码路径用不到或晚用到"的重模块(大依赖、非首屏功能、工具库)从启动求值链中剥离,减少冷启动的模块求值时间与内存占用(模块副作用不提前执行);相比动态 import() 的异步化改造(调用点需 await、代码结构变化),defer 保持同步访问语义——代码写法不变(直接访问 mod.foo),只把加载时机推迟,迁移成本低;浏览器/打包器可据此做更优的按需加载调度(依赖 defer 集合并行预取)。边界:defer 模块的顶层副作用延迟执行(依赖其副作用的代码需注意)、首次访问的同步加载可能引入瞬时延迟(重模块首用场景评估预取)、与循环依赖交互需谨慎。当前为 Stage 3,需转译/特性检测。

本题考察延迟求值的模块语义。核心是"声明式延迟 + 首次访问时同步求值"与静态/动态 import 的三方对比,冷启动优化与低迁移成本是价值点;能说明副作用时机即展示规范细节。

#
★★★

5. Array.fromAsync/Iterator.from 等新迭代协议的工程价值

Array.fromAsync 与 Iterator.from 等新迭代协议工具有何工程价值?

  • Array.fromAsync 物化异步序列
  • Iterator.from 的迭代器包装与规范化
  • 异步数据管道的简化

Array.fromAsync(iterable, mapFn?) 把异步可迭代对象(async generator、异步流)、含 Promise 的数组、甚至同步可迭代对象统一物化为数组:内部按序 await 每个元素(mapFn 可异步映射),替代手写 for await...of + push 的样板,处理分页拉取结果聚合、异步流收集、并发任务结果归并等场景。Iterator.from(value) 把任意"类迭代器"(有 next 的对象、可迭代对象)规范化为标准迭代器(补齐 return/throw、包装 next 结果),让手写协议对象可直接使用 Iterator Helpers 链式方法(map/filter/take)与 for...of,统一了"半实现迭代协议"对象的接入。工程价值:异步数据管道的声明式化(fromAsync 聚合 + Iterator Helpers 变换)、第三方库自定义迭代器对象的标准化接入、减少协议细节的重复实现;注意 Array.fromAsync 保留异步顺序语义(串行 await)、对拒绝的处理(中断并拒绝整体)、与 Promise.all 并行的取舍(fromAsync 是串行)。

本题考察新迭代工具的语义。核心是"fromAsync 串行物化异步序列 + Iterator.from 规范化任意迭代器"的定位,与手写收集/协议细节的对比是价值落点;能说明串行 vs Promise.all 的差异即展示边界理解。

#
★★★

6. TC39 提案阶段(Stage 0-4)的前端工程师参与路径与生产可用性判断

TC39 提案阶段(Stage 0-4)是什么?前端工程师如何参与并判断生产可用性?

  • 五个阶段的准入标准与交付物
  • 提案内容的变动风险
  • 生产采用的判断依据

TC39 提案五阶段:Stage 0(strawperson 想法征集)、Stage 1(proposal 概念成形,committee 采纳并指定 champion)、Stage 2(draft 草案含正式规范文本初稿,API 大框架确定但细节可改)、Stage 3(candidate 候选,规范文本完整、实现与测试就绪,只做小修订)、Stage 4(finished 完成,至少两个实现通过测试套件、TypeScript/浏览器发布后纳入标准)。参与路径:阅读提案仓库与规范文本、参与 issue 讨论与会议旁听、写使用案例反馈语义缺陷、在 polyfill/转译工具中试跑(Babel 插件)、为实现(V8/SpiderMonkey)提交 bug 或测试用例。生产可用性判断:Stage 3+ 可评估试用但需锁定版本与转译(语义可能微调);Stage 4 才建议无转译直接使用(引擎与 TS 支持确认);决策要素——浏览器支持矩阵、polyfill/转译成本、社区采用与生态工具链(lint/类型定义)、规范冻结度;高风险项(Decorators、Temporal 前期)在 Stage 3 前变化大,避免大规模依赖。

本题考察标准流程与工程决策。核心是"阶段门槛(champion/规范文本/双实现)→ 风险随阶段递减"的模型,生产采用以 Stage 4 与支持矩阵为准;能说明 Stage 3 试用的锁定策略即展示工程判断。

#
★★★

7. 装饰器(Decorators)提案在 TypeScript 5 与 JavaScript 标准化的进展与差异

装饰器提案在 TypeScript 5 与 JavaScript 标准化中的进展与差异是什么?

  • TS 5 默认新装饰器语义
  • 与 legacy(experimentalDecorators)的差异
  • 迁移与共存策略

TypeScript 5.0 起默认采用与 TC39 Stage 3 对齐的新装饰器语义(不再需要 experimentalDecorators 选项,但 legacy 模式仍可用):装饰器接收 (value, context) 二元参数、context 含 kind/name/static/private/addInitializer/metadata,字段装饰器不直接返回描述符而用 addInitializer 注入初始化——与旧版"参数装饰器/方法装饰器按 (target, key, descriptor) 签名、运行时 reflection(emitDecoratorMetadata)"的 legacy 实现有本质差异。进展差异:新装饰器的静态语义更严格(类型检查覆盖 kind 分派)、类型推导更精确;但 emitDecoratorMetadata(设计:paramtypes 等反射元数据)与旧生态(Angular、NestJS 依赖 legacy)不兼容,NestJS 等框架需显式保留 legacy 配置。迁移策略:新项目默认新语义;既有 legacy 项目评估依赖(框架是否支持新语义)后逐步迁移,混用会产生行为不一致;标准侧装饰器已 Stage 3 但未被所有引擎实现(需转译),与 Decorator Metadata 提案配合形成完整反射能力。

本题考察装饰器双轨制现状。核心是"TS 5 默认新语义 + (value, context) 形态 + legacy 的反射依赖差异",框架兼容是迁移决策点;能说明 emitDecoratorMetadata 的冲突即展示实战迁移经验。

#
★★★

8. Record 与 Tuple(不可变数据结构)提案在状态管理与不可变更新的应用

Record 与 Tuple 提案在状态管理与不可变更新上有哪些应用?

  • 值语义(按值比较)的不可变数据结构
  • 与对象/数组的语义差异
  • 状态管理的潜在简化

Record 与 Tuple 提案引入"值语义"的不可变数据结构:#{} 创建 Record、#[1,2] 创建 Tuple:内容不可变(无 set/delete 能力,属性访问正常)、按值比较(=== 对相同内容返回 true,不再需要引用比较或深比较)、嵌套含其他不可变值(可递归);与对象/数组的本质差异是把"内容即身份"(value equality)带入语言层。状态管理应用:reducer/状态更新后直接用 === 判断是否变化(结构共享的引用比较成本为零且绝对正确)、状态快照的 diff 与撤销重做简化(值相等即未变)、跨组件共享状态无需关心引用稳定性;配合 Map/Set(Record 可作键)实现"按内容索引"的缓存与去重。边界:提案仍为 Stage 2(形态可能变化)、不能包含对象/函数(可变值)会抛错、转译(Babel plugin)后性能取决于实现(当前 polyfill 慢于普通对象)、生态与类型支持未成熟;工程上短期用 Object.freeze + 深比较或 Immer 达到近似,提案落地后简化迁移。

本题考察值语义数据结构的愿景。核心是"不可变 + 按值比较"对状态管理的范式价值(=== 即 diff),边界在可变值限制与提案成熟度;能对比 Object.freeze/Immer 的现状方案即展示务实判断。

#
★★★

9. 正则灾难性回溯(ReDoS)的成因,嵌套量词与歧义分支的指数级复杂度及排查与修复手段

正则灾难性回溯(ReDoS)的成因是什么?如何排查与修复?

  • 嵌套量词与歧义分支的回溯爆炸
  • 指数级时间复杂度模型
  • 检测、加固与修复手段

正则引擎(回溯型,如 V8 的 Irregexp 在复杂模式下的回溯路径)在匹配失败时尝试所有可能的分支组合:当模式含"嵌套量词"((a+)+、(a|a)* 等)且输入不匹配时,同一组字符被多个量词反复划分尝试,组合数呈指数增长;"歧义分支"(a|aa|aaa 并列、.* 与后续 token 可多种分配)同样放大回溯路径。恶意输入(长串不匹配字符)可触发指数级回溯,使单次匹配耗时从微秒级暴涨到秒级——ReDoS 攻击面(服务端校验、前端富文本渲染)。排查:对可疑模式在增长输入上测耗时(指数曲线)、用工具(regex 静态分析、安全扫描器)识别嵌套量词/歧义模式。修复:模式简化(消除嵌套量词,(a+)+ 改为 a+)、原子组/占有量词(不回溯,(?>...) 、a*+)、前瞻先行确定分支((?:a|aa|aaa) 排序最具体在前)、输入长度/格式前置校验、超时中断(Worker 中执行并设时限);后端配合引擎级防护(RE2 等线性引擎或回溯限制)。

本题考察正则安全性的机制与治理。核心是"嵌套量词/歧义分支→回溯组合爆炸→指数耗时"的因果链,修复要消除回溯或限制输入;能给出具体模式改写示例即展示实战能力。

#
★★★

10. 命名捕获组(?...)与 String.prototype.matchAll() 在结构化文本解析的工程应用

命名捕获组与 matchAll 在结构化文本解析中有哪些工程应用?

  • 命名捕获组的可读性与引用
  • matchAll 的全局迭代语义
  • 结构化文本的解析模式

命名捕获组((? ...))为捕获结果命名:match 结果的 groups 对象按名字取值(match.groups.name),替换中可用 $ 引用,取代易错的数字索引(数字索引随模式修改易错位),提升可读性与健壮性;支持反向引用 \k 与后向引用(跨组校验,如 HTML 标签配对)。matchAll(regexp) 要求全局标志(g),返回迭代器:每次匹配结果含完整信息(index、groups、捕获组),避免 match 全局模式的"只返回匹配串丢失捕获"问题与 exec 手动循环的样板。工程应用:结构化文本解析——日志行解析(时间/级别/消息命名分组)、模板变量提取({{name}} 类)、CSV/键值对解析、代码语法高亮的 token 化;遍历用 for...of 消费迭代器(或 Array.from 转数组),配合命名组构建结构化对象(解构 groups 直接成字段)。注意:命名组不能重名、matchAll 要求 g 标志否则抛 TypeError、性能上大量解析考虑预编译正则。

本题考察正则解析的结构化工具。核心是"命名组解决可读性与引用稳定 + matchAll 提供迭代式全局匹配",组合用于行式文本解析;能给出日志解析的完整模式即展示应用能力。

#
★★★

11. 装饰器(TC39 当前 Stage 3)的语义与 TypeScript legacy decorators 的差异

TC39 Stage 3 装饰器与 TypeScript legacy decorators 的语义差异有哪些?

  • 参数形态(value, context vs target, key, descriptor)
  • 字段/访问器的处理差异
  • 元数据与互操作

核心差异在参数形态与执行模型:新装饰器(Stage 3)接收 (value, context),context 携带 kind/name/static/private/addInitializer/metadata,装饰器返回"替代值"(方法返回新函数、类返回新类、字段用 addInitializer 注入初始化逻辑),无描述符直接改写;legacy 装饰器接收 (target, key, descriptor)(类装饰器只收 target),直接改写属性描述符(method 装饰器改 value、accessor 改 get/set),字段装饰器返回 void 且只能旁路标记(配合 reflect-metadata 存元数据)。语义差异:字段初始化——新装饰器经 addInitializer 与类实例初始化顺序集成(字段初始化时机一致),legacy 对字段无标准初始化钩子(需 getter 包装 hack);私有元素——新装饰器 context.private 支持私有方法/字段装饰;元数据——新方案经 context.metadata/Symbol.metadata 标准收集,legacy 依赖 emitDecoratorMetadata 实验性反射;执行顺序与 this 绑定语义不同。互操作:两者不能混用(Babel/TS 需选模式),框架需明确声明支持的形态;迁移时 legacy 的 descriptor 改写逻辑需改写为返回新函数/访问器组合。

本题考察两套装饰器语义的精确差异。核心是"参数形态与改写模型(descriptor vs value/context)"及字段初始化与元数据的机制差异,混用禁止是工程要点;能说明 addInitializer 的时机语义即展示规范细节。

#
★★★

12. Unicode 属性转义(\p{Letter}、\p{Script=Han})与 u/v 标志在国际化正则匹配的边界差异

Unicode 属性转义(\p{Letter}、\p{Script=Han})与 u/v 标志在国际化匹配上有何边界差异?

  • \p 属性转义的要求与分类
  • u 与 v 标志的差异
  • 国际化文本匹配的实践

Unicode 属性转义(\p{...}/\P{...})按字符属性匹配:\p{Letter} 匹配所有字母(含中文等非 ASCII 字母、组合字符区分的分类)、\p{Script=Han} 匹配汉字、\p{Emoji}、\p{Decimal_Number} 等,是国际化正则的基础——替代手写字符区间(\u4e00-\u9fff 等易错且不全)。要求:属性转义必须在 u 或 v 标志下使用(无标志时报错)。u 与 v 标志差异:v 标志(unicodeSets,ES2024)是 u 的超集增强——支持集合运算(交集 [a-z&&[^aeiou]]、差集、并集)、\q{...} 字符串字面量(多字符序列)、更严格的语法校验(修正 u 标志下字符类歧义)、嵌套字符类;边界差异:u 模式下某些遗留语法(如 \p 误用位置、字符类内无效转义)行为不同,v 模式更严格(错误提前抛);v 的集合运算不能混用数字区间与属性(部分组合受限)。工程实践:中英文混排校验用 \p{Letter}+u、按脚本过滤(中文/日文/韩文)用 \p{Script=...}、emoji 序列匹配用 \p{Emoji_Presentation} 与 v 的 \q;注意属性值与语法版本兼容(v 较新需特性检测/转译)。

本题考察国际化正则的属性机制。核心是"\p 属性转义需 u/v 标志 + v 是 u 的集合运算增强"的层次,脚本匹配与 emoji 是典型场景;能说明 v 的集合运算语法即展示新特性掌握。

#
★★★

13. String 方法 slice/substr/substring 在处理负索引与长度参数时的边界差异与工程陷阱

String 的 slice/substr/substring 在负索引与长度参数上有何边界差异?

  • 三个方法的参数语义差异
  • 负索引与越界处理
  • 工程陷阱与选择

三个方法的参数语义:slice(start, end)——按索引区间取子串,start/end 可为负(从末尾计数:-1 是最后一个字符),end 不包含,负值与越界自动 clamp(取 max(0, len+idx) 等),start>end 时返回空串;substring(start, end)——负值按 0 处理(不反转)、参数自动交换顺序(start>end 时交换)、end 不包含;substr(start, length)——已废弃(非标准、仅规范化的遗留),start 可为负、length 表示字符数(非索引),length 为负/0 返回空串,超出截断。工程陷阱:substring 与 slice 对负值与大小关系的不同处理(负索引场景结果完全不同)易混淆;substr 的 length 语义与其他两方法不一致;三者的"结束边界"包含性差异(substr 是长度);中文等多字节字符按 UTF-16 码元切分(代理对 emoji 会截断半个字符,需按码点/Intl.Segmenter 处理)。工程建议:统一用 slice(语义清晰、负索引支持),避免 substr(废弃),按码点切割用 Array.from/展开 + slice 或 Intl.Segmenter。

本题考察字符串截取的参数边界。核心是"负索引(slice 反转计数 vs substring 归零)+ 长度 vs 索引(substr)"的差异矩阵,代理对截断是隐藏坑;能给出统一用 slice 的建议即展示工程规范。

#
★★★

14. 手写 call/apply 并处理 this 为 null/undefined、原始值包装与 Symbol 作为 this 的边界

手写 call/apply 时如何处理 this 为 null/undefined、原始值与 Symbol 作为 this 的边界?

  • null/undefined 时的默认绑定
  • 原始值 this 的装箱处理
  • Symbol 作为 this 与属性挂载的边界

手写 call/apply 的核心是"以指定 this 调用函数":thisArg 为 null/undefined 时按规范走默认绑定——非严格模式替换为全局对象(严格模式保留 null/undefined),因此实现中要判断 null/undefined 后赋 globalThis(严格模式下目标函数自有严格语义,模拟实现本身可统一传全局)。原始值 this(数字/字符串/布尔/Symbol):调用时引擎自动装箱(Number/String/Boolean/Symbol 包装对象),属性挂载在包装对象上有效但生命周期是瞬时的(方法内可见,之后包装对象可回收)——实现中可直接用 Reflect.apply(fn, thisArg, args) 让引擎处理装箱,手写挂载方案(obj[key]=fn)需注意:Symbol 作为 thisArg 时是原始值不能挂属性(只能装箱为 Object(symbol)),或者干脆用 Object(thisArg) 统一装箱。规范细节:Symbol 作为 this 时函数内 this 是装箱后的 Symbol 包装对象(Object(symbol)),typeof this === "object";null/undefined 挂载方案下要用临时对象兜底。工程实现优先 Reflect.apply(引擎保证边界),手写练习时覆盖 null/undefined→global、原始值→Object() 装箱、函数调用结果正确返回。

本题考察 call/apply 实现的边界处理。核心是"null/undefined 默认绑定 + 原始值装箱 + Symbol 装箱"三类边界,Reflect.apply 是最稳实现;能说明装箱瞬时性即展示引擎语义理解。

#
★★★

15. 手写 new 操作符,原型链绑定、构造函数返回值判断(对象 vs 原始值)与 instanceof 配合

手写 new 操作符时,原型链绑定、构造函数返回值判断与 instanceof 如何配合?

  • 新对象原型绑定到 constructor.prototype
  • 返回对象 vs 原始值的分支
  • 实例与构造函数的 instanceof 关系

手写 new 的流程:创建新对象 obj(Object.create(constructor.prototype) 或 {} 后设原型)→ 以 obj 为 this 调用构造函数(apply)→ 返回值判断——构造函数返回对象类型(对象/函数/数组等)则整体返回该返回值(覆盖新建对象),返回原始值或 undefined 则返回新对象 obj(这是"构造函数的返回值优先级"规则)。原型链绑定使 instanceof 成立:new F() 后 obj instanceof F 为 true,因为 F.prototype 在 obj 的原型链上(obj.proto === F.prototype);若构造函数显式返回对象 R,则实例实为 R,R instanceof F 取决于 R 的原型链(R.proto 是否指向 F.prototype)——这正是"返回对象导致 instanceof 失效/保留"的边界。边界细节:构造函数是箭头函数时不可 new(无 prototype 且无构造语义,调用抛 TypeError);new 与 new.target 配合(new.target 在构造内指向构造函数本身,可用于抽象基类检测);手写实现中判断"返回值是对象"用 value !== null && (typeof value === "object" || typeof value === "function")(null 不算对象返回)。

本题考察构造语义的完整实现。核心是"原型绑定 + 返回值覆盖规则(对象返回、原始值忽略)"与 instanceof 联动,null 返回值边界与箭头函数限制是易错点;能解释返回对象导致 instanceof 的变化即展示规范细节。

#
★★★

16. 手写符合 Promises/A+ 的 Promise,状态机、then 链式调用、微任务调度与 resolvePromise 算法

手写符合 Promises/A+ 的 Promise 需要实现哪些核心:状态机、then 链、微任务调度与 resolvePromise 算法?

  • 三态单向状态机与值/原因存储
  • then 返回新 Promise 与回调队列
  • resolvePromise 的决议递归与 thenable 吸收

手写 Promise 的核心模块:状态机——pending/fulfilled/rejected 三态单向转移,状态与结果(value/reason)落定后不可变,回调在落定后按注册顺序执行且多次调用无效;then 链——then(onFulfilled, onRejected) 返回新的 Promise,回调返回值被 resolvePromise 吸收(返回 Promise 则跟随其状态,返回普通值则兑现,抛错则拒绝),使链式无限延续;微任务调度——回调执行必须异步(Promise/A+ 要求"调用发生在当前执行栈清空后"),用 queueMicrotask/setTimeout 包装(现代实现用 queueMicrotask 保证微任务语义);resolvePromise 算法——处理 thenable 的决议递归:若决议值是对象/函数且有 then 方法,则提取 then 调用(resolve 与 reject 作为参数,带标志位防重复决议),处理 then 抛错、递归 Promise 的扁平化(self-resolution 检测:promise 不能 resolve 自身,否则 TypeError)与循环 thenable 检测(多次提取同一 thenable 死循环)。边界:回调参数缺失时的透传(onFulfilled 非函数则透传 value)、executor 同步抛错转为拒绝、空 thenable.then 的重入保护。

本题考察 Promise 实现的算法级细节。核心是"状态机 + resolvePromise 决议递归(thenable 吸收与自决议检测)"两大难点与微任务异步性,透传与重入保护是易漏点;能画出决议流程的状态图即展示算法掌握。

#
★★★

17. 手写深拷贝,循环引用(WeakMap 检测)、Symbol 键、特殊对象(Date/RegExp/Map/Set)与原型链保留

手写深拷贝如何处理循环引用、Symbol 键、特殊对象与原型链保留?

  • WeakMap 记录已拷贝对象
  • Symbol 键与不可枚举键的处理
  • Date/RegExp/Map/Set/TypedArray 的专门分支

手写深拷贝的完整方案:循环引用——用 WeakMap(源对象→拷贝)记录已处理对象,递归前先查表,命中直接返回既有拷贝(保持环与共享引用结构);键完整性——除字符串键外遍历 Symbol 键(Object.getOwnPropertySymbols)与不可枚举键(用 Reflect.ownKeys 而非 Object.keys),保持描述符语义;特殊对象分支——Date(new Date(d.getTime()))、RegExp(new RegExp(d.source, d.flags) 并复制 lastIndex)、Map(new Map([...d].map(([k,v]) => [deep(k), deep(v)])))、Set 同理、TypedArray(用构造器+缓冲区复制,注意 byteOffset/length)、ArrayBuffer(slice)、函数与 DOM 节点返回引用或抛错(不可拷);原型链保留——Object.create(Object.getPrototypeOf(d)) 作为拷贝壳,或按需选择"保留原型/仅数据拷贝"两种策略;属性写入用 Object.defineProperty 保留描述符(getter/setter 语义可选保留)。工程取舍:手写深拷贝正确性成本高(边界无穷),通用场景优先 structuredClone(原生、循环引用与内置类型支持),手写版本仅用于定制需求(过滤字段、保留原型、跳过不可克隆值)。

本题考察深拷贝实现的完整边界。核心是"WeakMap 环检测 + 键完整性(Symbol/不可枚举)+ 内置类型分支 + 原型策略"四模块,structuredClone 对比是工程落点;能说明共享引用与环的区分即展示算法严谨性。

#
★★★

18. 手写防抖(debounce)与节流(throttle),leading/trailing 变体、cancel 方法与 flush 立即执行

手写防抖与节流时,leading/trailing 变体、cancel 与 flush 如何实现?

  • 防抖的延迟合并与节流的频率限制
  • leading/trailing 边界组合
  • cancel 清理与 flush 立即执行

防抖(debounce):最后一次调用后延迟 wait 执行,期间调用重置定时器——用于输入联想、窗口 resize、搜索请求;节流(throttle):固定时间窗内最多执行一次——用于滚动、拖拽、高频按钮。变体:leading 控制"时间窗开头是否立即执行"(防抖 leading:true 首次立即执行后续合并、节流 leading 默认 true 首触发即执行),trailing 控制"窗口末尾是否补执行"(防抖默认 trailing、节流 trailing 末次调用在窗口结束补执行);组合语义(trailing 只在窗口内发生过调用时补发)。工程方法:cancel() 清除定时器并复位状态(组件卸载、输入清空时调用,防止过期回调);flush() 立即执行当前挂起的调用并清空(表单提交前确保最后一次输入生效);实现要点——保存 this/参数透传(apply)、定时器句柄复位、leading/trailing 状态标志(lastCallTime、timer)、防抖与节流都需处理"定时器已触发后新调用重置"的时序。注意:节流的 trailing 补发与 leading 执行不能重复(窗口内有 leading 则 trailing 不再补)、高频参数为最后值。

本题考察防抖节流的完整实现变体。核心是"延迟合并/频率限制的主逻辑 + leading/trailing 四象限 + cancel/flush 生命周期"的完备实现,参数透传与状态标志是易错点;能给出带注释的完整实现即展示实战能力。

#
★★★

19. 自定义错误类的继承设计与栈信息保留(Error.captureStackTrace、构造函数中 name 设置),为何直接 throw 字符串是反模式

自定义错误类如何设计继承与保留栈信息?为何直接 throw 字符串是反模式?

  • extends Error 与 name/prototype 设置
  • Error.captureStackTrace 的栈定制
  • 结构化错误的工程价值

自定义错误类:class ApiError extends Error,构造函数调用 super(message) 后设置 this.name = "ApiError"(否则 name 为 "Error" 无法区分类型)、可选挂载业务字段(code、status、details、cause);栈信息——默认 new Error 已捕获栈(message 与位置),但继承链在旧引擎下需手动 Object.setPrototypeOf(this, new.target.prototype) 修复 instanceof(现代引擎/TS 目标 es2022+ 已修正);Error.captureStackTrace(this, ApiError) 可裁剪栈(去掉构造函数自身帧,栈从业务调用处开始,更干净且提升性能);V8 支持 error.stackTraceLimit 控制栈深度。为何 throw 字符串是反模式:字符串错误没有类型、没有栈、没有结构化字段——catch 后无法用 instanceof 区分、无法定位来源(无 stack)、无法携带上下文(status/code),监控上报无法聚合去重;应始终 throw Error 实例(或子类),携带 name/code/cause/message 四要素,配合聚合归因。工程规范:错误分层(领域错误/基础设施错误)、错误码枚举、上报前统一结构化为 {name, message, code, stack, cause}。

本题考察错误类的工程化设计。核心是"继承 + name 设置 + 栈裁剪"的实现细节与 throw 字符串的三大缺陷(无类型/无栈/无上下文),结构化错误是监控与调试的基础;能说明 captureStackTrace 的用途即展示细节掌握。

#
★★★

20. Error.cause 的链式传播设计,如何在跨层调用中附加错误上下文而不丢失原始堆栈

Error.cause 如何做链式错误传播?跨层调用中如何附加上下文而不丢失原始堆栈?

  • Error 构造的 options.cause
  • 错误包装与上下文附加
  • 监控中的 cause 链展开

Error(message, {cause})(ES2022)为错误附加"原因":底层失败(数据库错误、网络错误)被上层捕获后包装为新错误,原错误存入 cause,形成错误链——上层错误携带业务上下文(接口、参数、操作),cause 保留底层原始错误(含其独立堆栈),不丢失根因。跨层传播模式:低层抛原始错误 → 中层 catch 后 new ServiceError(msg, {cause: err})(或附加字段)→ 高层再包装(requestId、用户上下文);每层只加"本层上下文",保持 cause 链可达;错误不可变思维下用 Object.assign 或自定义字段附加 extra。监控与调试:上报时展开 cause 链(while(err) { 记录 name/message/stack; err = err.cause })序列化完整链路,聚合时以"根因指纹"(底层错误指纹)归组,业务层错误分组后按 cause 定位基础设施问题;Node 的 util.inspect、DevTools 已原生展示 cause 链。注意:cause 可以是任意值(约定为 Error 实例)、循环 cause 链要防护、cause 链深度限制上报体积;避免"每层重复包同样的信息"造成噪声。

本题考察错误链的工程化设计。核心是"options.cause 包装 + 分层附加上下文 + 上报时展开 cause 链"的三段式,根因聚合是监控价值;能说明指纹归组策略即展示可观测性思维。

#
★★★

21. window.onerror 与 unhandledrejection 的职责边界与事件参数差异(message/filename/lineno vs reason/promise)

window.onerror 与 unhandledrejection 的职责边界与事件参数有何差异?

  • 同步错误 vs 未处理 Promise 拒绝
  • 参数差异(message/filename/lineno/colno/error vs reason/promise)
  • 监控集成的分工

职责边界:window.onerror(与 error 事件)捕获"同步抛错与未捕获的异步回调错误"(运行时异常,含加载资源错误需捕获阶段监听),unhandledrejection 捕获"Promise 拒绝且无处理"的错误——两条通道覆盖不同的错误来源,现代错误监控必须同时接入。参数差异:onerror 回调收到 (message, filename, lineno, colno, error)——脚本错误定位信息 + 原始 Error 对象(error 参数,现代浏览器,含 stack);unhandledrejection 事件对象含 reason(拒绝原因,通常为 Error 或任意值)与 promise(被拒绝的 Promise)——没有行列号定位(需 reason.stack),reason 可能是任意值(字符串、对象)需类型归一化。协作要点:onerror 的 error 参数优先使用(含完整栈),无 error 时用行列号;两者都可 preventDefault 抑制浏览器默认控制台输出;监控 SDK 统一把两类事件归一化为标准错误结构(name/message/stack/extra),Promise 拒绝归因时展开 reason 的 cause;注意 onerror 不捕获"资源加载失败"(img/script 404 需捕获阶段 error 监听),与 JS 异常分开处理。

本题考察全局错误通道的差异。核心是"同步/异步异常 vs Promise 拒绝"的职责划分与"定位信息 vs reason/promise"的参数差异,归一化上报是集成落点;能区分资源加载错误即展示监控完整性。

#
★★★

22. ES2024/ES2025 正式特性(Promise.withResolvers、Object.groupBy、ArrayBuffer.transfer、正则 v flag、Iterator Helpers、Set 方法、RegExp.escape、Float16Array)的应用场景

ES2024/ES2025 正式特性(Promise.withResolvers、Object.groupBy、ArrayBuffer.transfer、正则 v flag、Iterator Helpers、Set 方法、RegExp.escape、Float16Array)的应用场景有哪些?

  • 各特性的语义与场景映射
  • 分组聚合、错误处理、正则安全等
  • 现代代码库的接入方式

各特性场景:Promise.withResolvers——外部事件/回调驱动落定的 Promise(事件桥接、手动控制流程);Object.groupBy/Map.groupBy——数据聚合(按状态/类型/字段分组统计,替代 reduce 样板);ArrayBuffer.transfer/transferToFixedLength——缓冲所有权转移与重组(WASM 内存、流积累);正则 v flag(unicodeSets)——字符类集合运算与属性组合(复杂校验、脚本过滤);Iterator Helpers——惰性数据管道(无限序列、大流变换);Set 方法(union/intersection/difference 等 7 个)——集合运算(权限集合、标签筛选、去重对比);RegExp.escape——用户输入安全转义构造正则(搜索高亮、防注入);Float16Array——半精度浮点(WebGPU 纹理、ML 模型权重的内存减半)。共同价值:声明式替代命令式样板、边界正确性由规范保证(跨 Realm、Unicode、集合语义)、性能(原生实现)。接入方式:core-js polyfill 渐进启用(新项目直接使用、老项目按特性替换热路径)、TypeScript lib 更新(ES2024/2025)、特性检测 + 回退(旧浏览器);注意部分特性依赖运行时能力(Float16Array 需底层支持、v flag 需引擎实现),polyfill 覆盖程度按需核查。

本题考察新特性的场景地图。核心是"特性→场景→替代写法"的映射与"polyfill + 特性检测"的接入路径,按需替换而非全量升级是关键;能按场景归类特性即展示系统性认知。

#
★★★

23. try/catch 无法捕获异步错误的典型场景(忘记 await、回调内抛错、定时器与事件处理器)及全局兜底方案

try/catch 无法捕获异步错误的典型场景有哪些?全局兜底方案是什么?

  • 忘记 await 的 Promise 拒绝
  • 回调/事件处理器/定时器内的抛错
  • onerror/unhandledrejection 兜底

try/catch 只捕获"同步执行栈内"的错误,以下场景会漏:忘记 await——async 函数内 Promise 未 await(或调用方未 await async 函数)时拒绝发生在函数返回后,try 块已结束;回调内抛错——Array.forEach/setTimeout 回调、事件处理器、Promise.then 回调(then 内抛错被该 Promise 吸收,不会传到外层 try);定时器与事件——setTimeout/setInterval、addEventListener 的回调在独立任务中执行,其同步抛错不被外围 try 捕获(除非在回调内捕获);async 函数调用未 await——返回的 Promise 拒绝成为 unhandledrejection。全局兜底方案:window.addEventListener("unhandledrejection") 捕获未处理拒绝(event.reason/promise),window.onerror(error 事件)捕获运行时异常,两者构成最后防线——用于"记录与上报"而非"恢复逻辑"(无法恢复已发生的错误,只能监控告警);业务代码仍应显式 await + try/catch 处理可预期错误,全局兜底只处理漏网之鱼;Promise 链的每一环都应有处理策略(catch 兜底或让错误被上层显式处理)。

本题考察异步错误捕获的边界。核心是"try/catch 的同步栈语义 vs 异步错误发生时机"的错位,典型场景分类列举是答题骨架,全局兜底定位是工程原则;能说明 then 内抛错被吸收的机制即展示细节。

#
★★★

24. AggregateError 与 Promise.any 的错误聚合语义,如何展开 errors 数组做归因

AggregateError 与 Promise.any 的错误聚合语义是什么?如何展开 errors 数组归因?

  • any 全部拒绝时聚合拒绝
  • AggregateError.errors 数组
  • 归因与上报的展开策略

Promise.any 等待第一个兑现:全部输入 Promise 都拒绝时,整体以 AggregateError 拒绝,其 errors 属性是"按输入顺序"排列的所有拒绝原因数组(无兑现时聚合)。语义要点:AggregateError 是 Error 子类(name 为 "AggregateError"),errors 数组可含任意类型的拒绝值(Error 实例或任意值);与 all 的快速失败(首个拒绝即整体拒绝、不聚合)和 allSettled(返回状态数组而非拒绝)形成对比。归因展开:捕获 AggregateError 后遍历 errors 数组——逐个归一化(Error 实例取 name/message/stack,非 Error 值结构化包装)、按原因类型/错误码分组统计(找出"全部失败"的共性根因,如服务超时、参数错误)、结合业务上下文排序(首个失败时间、失败占比);上报时把 errors 展开为独立错误条目(保留各自栈)并附加聚合信息(失败总数、聚合类型),便于监控按根因指纹归组;注意 errors 可能为空数组(空输入 any 拒绝)、嵌套 AggregateError 需递归展开、深度与体积限制。

本题考察聚合错误的展开技术。核心是"any 全败聚合 + errors 顺序数组"的语义与"归一化→分组→根因定位"的归因流程,嵌套展开与体积控制是工程细节;能结合监控归组说明即展示可观测性实践。

#
★★★

25. class 语法与原型继承的等价转换(constructor、prototype、隐式 proto 链接、super 调用)

class 语法与原型继承如何等价转换?constructor、prototype、proto 链接与 super 调用分别对应什么?

  • class 定义与方法挂载 prototype
  • 类继承的 proto 双链接
  • super 的 [[HomeObject]] 语义

class 是原型继承的语法糖:class Foo { constructor() {...} method() {...} } 等价于 function Foo() {...}(constructor 体) + Foo.prototype.method = function() {...}(方法挂原型,非枚举);static 成员挂构造函数本身;实例字段在构造时初始化。类继承(class Bar extends Foo)的双链接:Bar.prototype.proto === Foo.prototype(实例方法继承链),Bar.proto === Foo(静态成员继承链,构造函数的原型链接)——普通函数继承没有后者。super 语义:super.method() 在方法内按 [[HomeObject]](定义该方法所在对象)找到父类原型调用;super() 在 constructor 中调用父构造函数——等价转换中 super 调用需显式 Foo.call(this)(实例初始化),且必须先于 this 使用(派生类构造函数在 super() 前访问 this 抛 ReferenceError)。细节差异:class 的 method 不可枚举(function 方式默认可枚举)、class 声明不提升(TDZ)、class 体自动严格模式、class 方法不可 new(无 [[Construct]]);理解等价转换有助于调试原型链与继承问题(instanceof、方法遮蔽)。

本题考察 class 的底层机制。核心是"方法挂原型 + 继承双 proto 链 + super 的 HomeObject 绑定"的转换模型,TDZ 与严格模式是语法糖之外的差异;能说明静态继承链即展示深度。

#
★★★

26. 私有字段 #field 与 WeakMap 模拟私有在封装强度、性能、可检测性(#x in obj 品牌检查)上的差异

私有字段 #field 与 WeakMap 模拟私有在封装强度、性能与可检测性(#x in obj 品牌检查)上有何差异?

  • 私有字段的引擎级封装
  • WeakMap 模拟的运行时近似
  • 品牌检查(#x in obj)的检测能力

私有字段(#field)是引擎级封装:只能在类体内部访问,外部任何方式(反射、ownKeys、getOwnPropertySymbols)都不可见,且每个类实例的私有字段由"品牌"标识(实例创建时与类绑定),无法伪造——封装强度最高;性能上由 V8 等引擎以内部槽/隐藏类优化,接近普通字段访问。WeakMap 模拟私有(构造器闭包 WeakMap,键为实例):外部可通过捕获 WeakMap 引用(原型方法暴露、调试钩子)间接访问,封装依赖"不泄露"约定而非语言保证;性能每次访问多一次 WeakMap 查找且阻止某些优化;弱引用带来额外语义(实例回收时条目随 GC)。可检测性差异:品牌检查 #x in obj 可在类内(或经静态方法)检测对象是否"由该类创建"(是否持有该私有品牌)——比 instanceof 更可靠(不受原型链伪造/跨 Realm 影响);WeakMap 模拟的"品牌"可被外部伪造(构造一个 fake 并注册进 WeakMap 无意义,但检查逻辑在外部不可验证);#x in obj 用于防御性校验(拒绝非本类实例、区分子类实例与伪造对象)。工程上:库的强封装用私有字段,WeakMap 模拟仅用于需要"动态键集合"的元数据场景。

本题考察私有机制的三个维度。核心是"引擎级不可见 vs 约定级可泄露、性能差异、品牌检查的可靠性"的对比,品牌检查是私有字段独有的检测能力;能说明伪造边界即展示安全思维。

#
★★★

27. super 的 [[HomeObject]] 绑定机制,为何把方法复制到另一个对象后 super 调用会出错

super 的 [[HomeObject]] 绑定机制是什么?为何把方法复制到另一个对象后 super 调用会出错?

  • [[HomeObject]] 的静态绑定
  • super 查找基于 HomeObject 的原型
  • 方法解绑/复制的陷阱

super.method() 的查找基于函数的内部槽 [[HomeObject]]:方法定义时被绑定到"定义它的对象"(类中方法绑定到该类原型),调用 super.method() 时从 [[HomeObject]] 的原型(即父类原型)开始查找方法——这个绑定在"定义时"静态确定,与调用时的 this/接收者无关。陷阱:把方法复制到另一个对象(如 const fn = obj.method; other.method = obj.method; Object.assign 拷贝)后,函数的 [[HomeObject]] 仍是原对象,super 依旧从原对象的原型查找——若目标对象与原对象原型不同或没有该父原型链,super 调用会"找不到方法"抛 TypeError(super 引用必须找到或报错),即使 this 已绑定到新对象(call/apply 也无法改变 [[HomeObject]])。工程影响:mixin 组合、方法混入(把类方法混到普通对象)、原型拷贝场景都会踩坑;修复方式:不用 super(改用显式父引用)、以函数形式传递(回调抽取为普通函数)、避免把含 super 的方法在定义对象外调用。注意箭头函数不绑定 super 语义(词法继承外层)、动态派发需设计接口而非复制方法。

本题考察 super 的静态绑定本质。核心是"[[HomeObject]] 定义时绑定 + super 沿其原型查找"与"复制方法不改变 HomeObject 导致查找失败"的机制链,mixin 场景是典型陷阱;能说明为何 call/apply 无法修复即展示深度。

#
★★★

28. 类字段(实例字段与 static 字段)的初始化执行顺序,实例字段初始化相对父类构造函数的时机

类字段(实例与 static)的初始化执行顺序是什么?实例字段相对父类构造函数的时机如何?

  • static 字段的类求值顺序
  • 实例字段在 super() 之后的初始化
  • 字段初始化与 constructor 体顺序

类字段初始化顺序:类定义求值时先初始化 static 字段(按声明顺序,在类体执行期间、constructor 定义后);实例字段在"实例创建时"初始化——基类:字段初始化先于 constructor 体执行(字段声明顺序);派生类:super() 调用后、本类 constructor 体执行前,本类实例字段按声明顺序初始化——因为基类构造需要先完成(super() 负责调用父构造函数初始化 this),本类字段依赖 this 已就绪;派生类在 super() 之前访问 this 抛 ReferenceError(this 未初始化)。细节:字段初始化表达式在"声明顺序"中执行(后声明的字段不能在前一字段初始化时访问——TDZ 语义);static 字段初始化时 this 为类本身(可互相引用需注意顺序);字段初始化早于 constructor 内代码,因此 constructor 中看到的字段值已含初始化结果;父类字段在 super() 期间(父类构造)已初始化,若父类构造调用了子类覆写的方法,方法执行时子类字段尚未初始化(经典陷阱:此时读子类字段为 undefined)。工程影响:初始化依赖注入放 constructor、避免构造期调用可覆写方法读取字段。

本题考察类字段的初始化时序。核心是"static 在类求值期 + 实例字段在 super 后 constructor 前(派生类)"的两段式模型,构造期方法调用陷阱是深度考点;能给出字段与构造器的分工建议即展示工程经验。

#
★★★

29. 静态初始化块(static {})的用途与可访问私有静态成员的特性

静态初始化块(static {})的用途是什么?为何能访问私有静态成员?

  • 类求值期的初始化块
  • 复杂静态初始化逻辑
  • 类内作用域与私有访问

静态初始化块(static {})在类求值时按声明顺序执行,用于复杂静态初始化:static 字段只能写单个表达式,而块内可写完整语句(try/catch、循环、条件逻辑、多次赋值、注册元数据/事件),初始化顺序与其他 static 字段声明位置一致(块与其所在位置参与顺序)。可访问私有静态成员:块内的词法作用域位于类体内部,可直接访问 #privateStatic 字段与私有方法(与实例方法/字段同理,私有性对类内代码开放)——静态私有状态(如类级注册表、实例计数器、单例缓存)在块内初始化与操作,外部仍不可见,实现"模块级私有 + 类作用域"的封装。用途示例:依赖环境动态初始化配置、把类注册到全局注册表(DI/工厂)、静态缓存预热、多静态字段的联动初始化。注意:块内 this 指向类本身(可经 this 访问静态成员);块不能是 async(需包在异步函数中启动);块与 static 字段混排时顺序敏感(先声明先执行)。

本题考察静态初始化的进阶语法。核心是"类求值期执行 + 语句级能力 + 类内私有可见"三个特性,注册表与缓存是典型场景;能说明块与字段的顺序语义即展示规范细节。

#
★★★

30. 手写 Promise.all/race/allSettled/any,拒绝聚合(AggregateError)与空数组边界如何处理?

手写 Promise.all/race/allSettled/any 时,拒绝聚合(AggregateError)与空数组边界如何处理?

  • 四个组合器的落定语义
  • AggregateError 聚合与空数组
  • 输入非 Promise 值的吸收

手写要点:先 Promise.resolve 吸收输入(非 Promise 值转为已兑现,保证统一处理),再按语义实现:all——计数兑现,任一拒绝立即整体拒绝(首个原因),全部兑现按输入顺序返回数组,空数组直接兑现空数组;race——首个落定(兑现或拒绝)即整体落定,空数组永远 pending(无元素可落定,注意不 resolve 也不 reject);allSettled——全部落定后按输入顺序返回 {status, value/reason} 数组,永不拒绝,空数组兑现空数组;any——首个兑现即兑现,全部拒绝时以 AggregateError(errors 按输入顺序含全部原因)拒绝,空数组直接拒绝 AggregateError(errors 为空数组)。边界细节:all 的拒绝不等待其他(快速失败,其他 Promise 继续运行不受影响);any 的聚合需收集全部原因再抛 AggregateError;输入数组的索引对齐(稀疏数组处理为 undefined 参与吸收);拒绝原因保持原样(不包装);race 不取消落选者。实现用计数器 + 结果数组 + 状态标志防重复落定(settled 标志)。

本题考察组合器手写实现的语义边界。核心是"计数器 + 标志位"的通用骨架与四个器各自的空数组/聚合分支,any 的 AggregateError 顺序是关键;能逐一说出空数组行为即展示完整掌握。

#
★★★

31. 手写防抖与节流,立即执行选项、取消方法与 this/参数透传的完整实现?

手写防抖与节流时,立即执行选项、取消方法与 this/参数透传如何完整实现?

  • 定时器状态与 this/args 透传
  • leading/trailing 的边界组合
  • cancel/flush 生命周期

完整实现要点:闭包保存定时器句柄与上下文;调用时用 fn.apply(this, arguments) 透传调用者 this 与参数(防抖:清旧定时器、设置新定时器;节流:记录上次执行时间,未到窗口则定时器补发 trailing);立即执行选项——leading:true 时首次调用立即执行(记录时间戳,窗口内后续调用不再立即执行);trailing:true(默认)窗口末尾补执行最后一次(防抖默认"延迟合并后执行",节流默认"窗口头执行 + 尾部补发");组合边界——窗口内有 leading 执行时 trailing 不重复补发(同一窗口只执行一次),防抖的 leading 后仍合并后续调用;cancel()——clearTimeout + 复位定时器与状态(卸载清理,防止过期回调);flush()——若存在挂起定时器,立即执行并清除(提交前强制生效);immediate/leading 变体的定时器复用与重置时序(防抖中"执行后重新计时"与"执行后禁止再触发"的差异)。实现时注意:定时器回调中执行后重置 lastCallTime、空参数数组的 apply 兼容、返回被包装函数以保持原函数属性(fn.name/length 可选保留)。

本题考察防抖节流的完备实现。核心是"状态机(定时器 + 时间戳 + leading/trailing 标志)+ 透传 + 生命周期(cancel/flush)"的完整闭环,组合边界是易错点;能给出四象限行为说明即展示扎实功底。

#
★★★

32. 手写 call/apply/bind,bind 返回函数被 new 调用时的行为如何兼容?

手写 call/apply/bind 时,bind 返回函数被 new 调用时的行为如何兼容?

  • new 调用时 this 的替换
  • newTarget/构造标志的检测
  • 原型链与 instanceof 保留

bind 返回的绑定函数被 new 调用时,规范要求 this 指向新建实例而非 bind 时的目标 this——手写实现需要区分调用方式:ES6 中可用 new.target 检测(普通函数内 new.target 为 undefined 表示普通调用,非 undefined 表示构造调用):构造调用时 fn.apply(instance, args)(instance 由 new 机制创建且原型链已接 boundFn.prototype);普通调用时 fn.apply(boundThis, args)。原型链兼容:绑定函数应继承原函数的 prototype(boundFn.prototype = Object.create(fn.prototype) 或 fn.prototype 的引用),使 new bound() 后实例 instanceof fn 成立(规范中绑定函数无 prototype 属性,其 [[Prototype]] 指向目标函数,构造时实例原型链经绑定函数的原型连接——手写用 prototype 继承模拟)。边界:箭头函数无 prototype 且不可 new(bind 箭头函数后 new 抛 TypeError,手写需检查目标可构造性)、参数合并顺序(柯里化参数在前、调用参数在后)、this 为 null/undefined 时默认绑定、bind 的目标 this 仅在普通调用生效。工程上:jQuery 式库方法、React 事件绑定(注意新 API 已不再需要 bind)等场景理解该语义避免踩坑。

本题考察 bind 的构造兼容语义。核心是"new.target 分流 + 原型链继承 + 柯里化合并"三要点,原型链保留是手写中最易遗漏;能解释规范为何让绑定函数"看似无 prototype"即展示规范理解。

#
★★★

33. queueMicrotask 与 TC39 治理流程的协作

queueMicrotask 与 TC39 治理流程是如何协作的?

  • queueMicrotask 的标准来源
  • Web 平台 API 与 TC39 的协同
  • 微任务语义的规范一致性

queueMicrotask 是"Web 平台与 TC39 协同"的典型:它最初由 HTML 规范定义(补充 Web 环境微任务调度的标准入口),随后被 TC39 采纳进 ECMAScript 规范(ES2020),实现统一——如今它既是 ECMAScript 标准的一部分(语言核心),又在 HTML 规范中规定其与事件循环微任务 checkpoint 的集成语义。协作机制:TC39 负责语言级 API(函数形态、语义),Web 平台(WHATWG/W3C)负责与浏览器事件循环、渲染、Worker 的衔接;两规范通过交叉引用保持一致(HTML 引用 ECMAScript 的微任务队列定义,ECMAScript 引用 HTML 的环境语义);兼容性承诺(Web 平台兼容性:已被广泛实现的 API 即使设计不完美也尽量保持语义稳定,避免破坏既有站点)。工程价值:理解该协作解释"为何 queueMicrotask 与 Promise.resolve().then 语义一致但实现独立"(两规范各自定义,行为对齐)、为何浏览器/Node 行为一致(统一标准);TC39 治理中"标准化已部署 API"的流程(提交提案→纳入标准→规范文本对齐)避免社区各自为政的 polyfill 分歧。类似案例:globalThis、structuredClone 的规范化路径。

本题考察标准组织的协作模型。核心是"Web API 反哺语言标准 + 双规范交叉引用 + 兼容性承诺"的机制,queueMicrotask 是代表性案例;能举出 globalThis 等类似案例即展示广度。

#
★★★

34. structuredClone() 与结构化克隆算法,哪些值无法克隆(函数、DOM 节点、Error 等会抛 DataCloneError),循环引用与 TypedArray/Map/Set 如何处理,与手写深拷贝的取舍?

structuredClone() 无法克隆哪些值?循环引用与 TypedArray/Map/Set 如何处理?与手写深拷贝的取舍是什么?

  • DataCloneError 的不可克隆类型
  • 循环引用与内置类型的克隆语义
  • 与手写深拷贝的选型

structuredClone 基于结构化克隆算法:可克隆——循环引用(按引用共享,环结构保留)、Date/RegExp/Map/Set(内容深度克隆,键值同样克隆)、TypedArray/ArrayBuffer(字节复制,transfer 可转移所有权)、Blob/File、普通对象/数组(含 Symbol 键与不可枚举键);不可克隆抛 DataCloneError——函数(无数据形态)、DOM 节点(Element/Window 等宿主对象)、Symbol(原始值不可克隆)、Error 实例、类实例的私有字段与内部槽(克隆后丢失类原型与私有状态,属性被浅拷贝为普通对象)、WeakMap/WeakRef 等弱引用对象。循环引用处理:算法维护"源→克隆"映射,同一对象只克隆一次,环与共享引用保持一致(与 JSON.stringify 抛错不同)。与手写深拷贝取舍:structuredClone 原生实现正确性有保证(边界全、内置类型语义规范)、性能优于多数 JS 实现;限制是无法克隆函数/类实例(原型不保留)、不可定制(无法过滤字段/跳过键);手写深拷贝用于定制需求(保留原型、脱敏过滤、忽略不可克隆值),但正确性风险高(边界无数)。工程建议:通用场景用 structuredClone,定制场景封装专用拷贝函数并充分测试边界。

本题考察克隆算法的完整边界。核心是"可克隆集合(环/内置类型)+ 不可克隆抛错集合(函数/DOM/类实例)+ 与手写版选型"的三段式,类实例克隆后的退化是易忽视点;能说明共享引用 vs 复制的差异即展示算法理解。

#
★★★

35. 正则表达式 v flag(unicodeSets)与 u flag 在集合运算与 Unicode 属性的升级

正则 v flag(unicodeSets)与 u flag 在集合运算与 Unicode 属性上有哪些升级?

  • v 对 u 的增强范围
  • 字符类集合运算语法
  • 兼容与迁移注意事项

v 标志(ES2024,unicodeSets)是 u 标志的升级版,增强点:字符类集合运算——并集([a-z[0-9]] 嵌套字符类)、交集([a-z&&[^aeiou]])、差集([a-z--[aeiou]]),在单个字符类中表达复杂字符集组合,替代"多次取反 + 交叉"的笨拙写法;字符串字面量 \q{...}——匹配多字符序列(如 \q{ab|cd} 匹配 "ab" 或 "cd",配合集合运算做序列集合);属性转义增强——\p 与集合运算可组合,嵌套字符类([..[..]])支持;语法严格性——v 模式修复 u 模式字符类中的遗留歧义(如类内 - 的处理、转义规则更严格,错误尽早抛出)。升级差异:v 下部分 u 的旧写法报错(如类内 [a-b-c] 的歧义区间被拒绝);集合运算不能混合"单字符类"与"字符串字面量"(\q 只能与 \q 运算);v 要求引擎支持(需特性检测/转译)。工程应用:国际化的复杂脚本校验(中文+标点差集)、emoji 序列匹配(\q 多码点)、密码策略字符集(可读的并/交/差表达);迁移:特性检测 "v" in RegExp.prototype.flags 或直接构建测试,老环境用转译或保留 u 写法。

本题考察正则 v 标志的集合代数能力。核心是"并/交/差 + \q 序列 + 嵌套类"的语法升级与 u 模式的差异,迁移兼容是工程落点;能写出交集差集的实际模式即展示掌握。

#
★★★

36. Decorator Metadata 提案与反射元数据

Decorator Metadata 提案与反射元数据是什么?如何工作?

  • Symbol.metadata 的挂载结构
  • context.metadata 的写入与继承
  • 反射框架的读取模型

Decorator Metadata 提案与装饰器(Stage 3)配套:装饰器执行时可经 context.metadata 访问"当前类及其原型链的元数据对象",写入的元数据被收集到类的 [Symbol.metadata] 属性(实例无、类静态侧有);继承语义——子类装饰器的 context.metadata 是"父类元数据对象的原型式继承"(子类 metadata 的 [[Prototype]] 指向父类 metadata),写入子类自己的键不影响父类,读取时沿链可见父类元数据——形成与类继承平行的元数据继承树。反射读取:框架在运行时读 Class[Symbol.metadata](Object.getOwnPropertySymbols 或直接访问),按结构解析(类级元数据、成员名→元数据),驱动依赖注入(装饰器登记注入键)、序列化(字段装饰器登记类型/忽略标记)、路由(方法装饰器登记 path)、校验规则注册。设计要点:元数据写入发生在"装饰器求值期"(类定义时),是静态声明信息的承载,不是运行时可变状态;多个装饰器可写同一键(后者覆盖或约定合并);Symbol 键可避免与业务属性冲突。工程价值:框架获得标准化的反射能力(替代 reflect-metadata 的实验性实现),TypeScript 5 与 Babel 已支持。

本题考察装饰器元数据的完整模型。核心是"context.metadata 写入 → Symbol.metadata 挂载 → 原型链继承 → 框架反射读取"的链路,继承语义是关键;能给出 DI 的元数据流即展示框架设计认知。

#
★★★

37. ShadowRealm(Stage 3 提案)的隔离执行边界

ShadowRealm(Stage 3 提案)的隔离执行边界是什么?

  • 独立全局环境与内置对象
  • 值传递与对象封装
  • 与 iframe/Worker 的对比

ShadowRealm 创建完全独立的 JS 执行环境(新全局对象、新内置对象、独立作用域链),提供 evaluate(code) 执行代码字符串与 importValue(specifier, name) 异步导入模块并取回导出值。隔离边界:领域内代码与外部隔离——不能访问外部的全局变量/闭包(不会"逃逸"),外部也无法直接操作领域内对象;值传递按结构化克隆/包装——原始值与可克隆值可传递,对象与函数以"包装对象"(wrapped function/object)形式暴露(调用包装函数在领域内执行、对象访问经代理转发),不可克隆类型受限;领域内的错误、Promise、全局对象均独立(跨 Realm 判断需注意)。用途:插件/第三方代码的沙箱执行(配置、模板、用户脚本)、动态代码求值的隔离、测试环境隔离;与 iframe 对比——ShadowRealm 是纯 JS 级隔离(无 DOM、无渲染、更轻量,但不能跑 DOM 代码),与 Worker 对比——Worker 是完整线程级并行且消息传递,ShadowRealm 是同步/同线程的隔离域(适合非并行安全代码)。注意:evaluate 的代码字符串有 CSP 与审计要求、无限循环会阻塞宿主线程(无超时)、包装函数跨领域边界有性能与语义开销(原型不同)。

本题考察 JS 级隔离域的语义。核心是"独立全局 + 值/对象跨界规则 + 与 iframe/Worker 的定位差异",包装函数语义与阻塞风险是边界;能说明适合的插件沙箱场景即展示应用判断。

#
★★★

38. 双冒号绑定 Bind Operator(::)状态与历史包的取合

双冒号绑定操作符(::)的提案状态与历史取舍是什么?

  • :: 的语义(方法绑定与提取)
  • 提案的搁置与回退
  • 工程上的等价替代

双冒号绑定操作符(::)提案意图:obj::fn 等价于 fn.bind(obj)(绑定调用)、obj::method 提取方法并绑定(this 预绑定),提供"绑定 + 提取"的简洁语法(结合管道思想 :: 与 |>);曾进入 Stage 1(甚至讨论过 Stage 2 形态),但因语义分歧(与管道操作符的交互、方法提取的 this 语义、与现有函数式库的冲突)长期搁置,目前未进入 Stage 3,属于"暂停活跃"状态,规范文本不再推进。历史取舍:支持者认为它解决"解构方法即丢失 this"的痛点(const { method } = obj 后 this 丢失)、提升函数式管道可读性;反对/搁置原因——与管道提案(|>)的优先级与解析歧义、新语法增加语言复杂度的成本 vs 收益、社区可通过 bind/call/箭头函数/库函数(ramda 的 unboundMethod)实现等价能力,收益不足以推进。工程实践:不依赖该语法(状态不稳定),用等价方案——箭头函数闭包(x => obj.method(x))、Function.prototype.bind 显式绑定、解构后手动绑定、函数式库的适配器;跟踪 TC39 议程时以"搁置提案"对待(不进入生产依赖)。

本题考察提案治理中的"搁置"案例。核心是":: 语义 + 搁置原因(与管道交互、收益不足)+ 等价替代"的结构,工程结论是不依赖未稳定语法;能分析提案搁置的治理逻辑即展示标准参与素养。

#
★★★

39. Pattern Matching(Stage 2 提案 match(expr) { case... })

Pattern Matching 提案(match(expr) { case... })的模式匹配语义是什么?

  • match 表达式与 case 模式
  • 模式类型(字面量/对象/数组/绑定)
  • 穷尽性与优先级设计

Pattern Matching 提案(Stage 2)为 JS 引入表达式式模式匹配:match (value) { case pattern => result, ... },case 后的模式按顺序尝试匹配值,首个成功者执行对应表达式并作为整个 match 的值;模式包括——字面量模式(匹配具体值)、对象模式({ a, b: x } 解构匹配并绑定)、数组模式([x, ...rest])、通配符(_)、逻辑或(case a or b)、类型守卫(when 条件子句)、binding 模式(把值绑定到变量)——语义融合了"结构性解构 + 条件分发"。设计要点:match 是表达式(有值,可赋值/返回);模式匹配检查用"结构相等 + 绑定"而非 ===(如对象模式匹配"形状");case 顺序敏感(首个命中生效,常把最具体模式放前);穷尽性——提案鼓励/要求对联合类型穷尽(TS 配合),运行时无命中抛错(或需 default 兜底);与 switch 的关系——match 是"表达式化 + 模式化"的升级(switch 是语句、只有 === 比较)。工程价值:代数数据类型(联合/判别联合)的分发处理(状态机、协议解析、错误归因)声明式表达;当前需 Babel 插件/TS 预研,生产依赖待 Stage 3+。

本题考察模式匹配的表达力。核心是"表达式形态 + 模式种类(结构解构/绑定/守卫)+ 顺序与穷尽语义",与 switch 的对比是定位落点;能给出判别联合的匹配示例即展示应用价值。

#
★★★

40. Error.isError 在跨 Realm/iframe 错误类型判断的工程价值

Error.isError 在跨 Realm/iframe 错误类型判断上有何工程价值?

  • instanceof Error 的跨 Realm 失效
  • Error.isError 的规范语义
  • 错误监控的可靠性

instanceof Error 基于原型链判断:跨 Realm(iframe、Worker、不同打包实例)中错误对象由各自 Realm 的 Error 构造(或子类),主 Realm 的 Error.prototype 不在其原型链上,instanceof Error 返回 false——多 iframe 应用、Web Worker 错误回传、微前端子应用错误上报都会遇到"明明是个错误却判不出"的问题。Error.isError(value)(ES2025)提供跨 Realm 可靠的错误判定:内部按"是否为错误对象"([[ErrorData]] 内部槽或规范错误对象身份)检查,不依赖原型链,iframe/Worker 中的 Error 与自定义 Error 子类都返回 true。工程价值:错误监控 SDK 对捕获值(onerror/event.reason/接口返回值)做归一化——先用 Error.isError 判定是否 Error 实例,是则提取 name/message/stack,否则包装为结构化错误(任意值拒绝原因、字符串错误统一化);避免误判(普通对象带 message 属性不被误认)与漏判(跨 Realm 真错误被忽略)。边界:Error.isError 对"看起来像错误的对象"(duck-typed)返回 false——判定的是真实错误语义而非形状;与 Object.prototype.toString.call(err)("[object Error]" 可能被 Symbol.toStringTag 伪造)对比,isError 不受伪造影响(内部槽检查)。

本题考察跨 Realm 错误判定的机制。核心是"instanceof 原型链失效 vs isError 内部语义检查"的差异,监控归一化是价值场景;能对比 toStringTag 伪造的边界即展示安全认知。

#
★★

41. Explicit Resource Management(using 声明)在资源释放的工程应用

Explicit Resource Management(using 声明)在资源释放上有哪些工程应用?

  • using 声明与 [Symbol.dispose] 协议
  • 作用域退出时的确定性释放
  • 与 try/finally 的对比

Explicit Resource Management 提案(using 声明,ES2026 已定稿)为 JS 引入确定性的资源释放:using resource = 获取资源,变量在其作用域退出时(正常结束/return/throw)自动调用资源的 [Symbol.dispose] 方法(异步资源用 await using 与 [Symbol.asyncDispose])——编译器在作用域出口注入释放调用,释放顺序与声明顺序相反(栈式)。工程应用:文件句柄/数据库连接(获取后自动关闭,防止遗忘)、锁的获取与释放(作用域结束自动 unlock)、定时器与监听器(组件逻辑中自动清理)、事务提交/回滚(配合状态标记)、测试夹具;与 try/finally 的对比——using 是声明式的(资源管理内聚在资源对象),避免 finally 中手写释放的样板与遗忘风险,且嵌套作用域与提前 return 都自动覆盖(finally 需逐处手写)。边界:资源对象需实现 Symbol.dispose(或使用内置资源如 FileHandle、AbortController 相关);作用域退出时 dispose 抛错会覆盖原错误(规范有聚合处理);using 与 async 资源需 await using(作用域退出时 await);当前引擎支持有限(V8 新版本),需 polyfill(显式调用)或转译;不适用于"释放时机需要提前/动态"的场景(显式调用 dispose 方法即可)。

本题考察确定性资源管理的语义。核心是"作用域退出自动释放 + dispose 协议 + 栈式顺序"的模型与 try/finally 的对比优势,异步资源与抛错聚合是细节;能给出连接池/锁的示例即展示应用价值。

#
★★

42. Promise.allSettled、Promise.any 在容错并发的现代实践

Promise.allSettled 与 Promise.any 在容错并发中有哪些现代实践?

  • 全量落定归因与任一成功
  • 部分失败的容错策略
  • 与并发池/重试的组合

容错并发实践:批量独立任务(批量上传、批量查询、多数据源拉取)用 Promise.allSettled 并行执行并逐项归因——单条失败不影响其他任务,结果数组按输入顺序返回 {status, value/reason},业务层按状态分流处理(成功的进列表、失败的记日志/重试队列);Promise.any 用于"任一成功即可"的冗余场景(多 CDN/多接口/多 provider 择优),首个成功即返回,全部失败聚合 AggregateError 便于归因。组合模式:allSettled + 失败重试(对 rejected 项有限次重试后仍失败再归因)、allSettled + 并发上限(配合 p-limit 类信号量控制同时进行数,防连接/限流压力)、any + 降级(首源失败自动切换备用源,全部失败走兜底默认值);错误治理:any 的 AggregateError 展开 errors 归因根因,allSettled 的 reason 归一化后统一上报。注意:allSettled 不中断执行(失败任务继续运行)、并发数控制是独立的信号量问题(组合器本身不限制并发)、容错粒度按业务划分(整体成功/部分成功/任一成功)。

本题考察容错并发的组合实践。核心是"allSettled 逐项归因 + any 冗余择优"的场景分工与重试/限流/降级的组合模式,并发上限是易漏点;能给出批量上传的完整容错方案即展示工程能力。

#
★★

43. Decorator Metadata 在依赖注入与框架集成的工程应用

Decorator Metadata 在依赖注入与框架集成中有哪些工程应用?

  • 装饰器登记依赖信息
  • 元数据的反射读取
  • 框架级 DI 容器的实现模型

依赖注入与框架集成的应用模式:依赖登记——类字段/构造参数装饰器(@inject(TOKEN))经 context.metadata 写入注入键(类级与成员级元数据),容器运行时读取;容器集成——框架在加载类时读 Class[Symbol.metadata](与原型链合并的继承元数据),解析出"该类需要哪些依赖、成员需要哪些注入",用容器解析并构造实例(new 或工厂 + 属性赋值);路由与生命周期——方法装饰器登记路由信息(path/method/中间件)到元数据,框架启动时统一注册路由表;序列化/校验——字段装饰器登记类型与规则,框架反射读取生成 schema。与旧方案(reflect-metadata 的 emitDecoratorMetadata + 全局注册表)相比,新元数据方案:数据挂在类自身(Symbol.metadata)无需全局副作用、原型链继承语义自然、类型安全(TS 可推导元数据结构);工程注意:元数据写入时机是类定义期(装饰器求值),容器需在定义后读取(模块加载顺序管理)、元数据键用 Symbol 避免冲突、子类继承元数据的合并语义(覆盖 vs 追加按约定设计);框架落地需 Babel/TS 编译支持与 polyfill。

本题考察元数据在框架中的工程落地。核心是"装饰器登记 → Symbol.metadata 反射读取 → 容器/路由/序列化驱动"的模型,与 reflect-metadata 的对比与继承合并语义是细节;能画出 DI 容器的元数据流即展示架构能力。

#
★★

44. 类字段(Class Fields)的 private 字段 #field 与 TypeScript private 的差异

类字段的 #field 私有字段与 TypeScript 的 private 修饰符有何差异?

  • 运行时 vs 编译期的封装
  • 品牌检查与访问边界
  • 迁移与混用注意

本质差异在"何时生效":#field 是运行时(引擎级)私有——编译器生成的 JS 中仍是私有字段(ES2022 语法直接输出),外部(反射、Object.keys、子类、任意代码)都无法访问,品牌检查 #x in obj 可验证实例归属,封装强度有语言保证;TS private(及 protected)是编译期检查——仅 TS 类型层面禁止访问,编译产物(target ES5/ES2015 时代码)中就是普通属性(如 this._field 或 this.field),运行时任何代码(含 JS 调用方、反射、子类原型链)都可以直接读写,只靠"命名约定"(下划线)防御;TS 的 private 不影响属性可枚举性与序列化。差异影响:库的强封装(防外部篡改、防误用)用 #field;TS private 适合"团队约定 + 类型提示"的轻量封装;混用注意——#field 不能与同名 private 属性共存会冲突、TS 类型检查不识别 # 私有为 public 类型的一部分、装饰器与元数据对 # 私有的支持差异、子类不能访问父类 #field(但可访问 protected)。迁移:两者可在同项目共存,暴露公共 API 需考虑运行时消费者的访问需求(序列化、测试注入)。

本题考察两类私有机制的边界。核心是"引擎级运行时私有 vs 编译期类型私有"的生效层差异,反射与子类访问是验证场景;能说明 #field 对序列化可见性即展示细节。

#
★★

45. 正则表达式新特性(命名捕获组、Lookbehind、/v flag)的现代应用

正则表达式新特性(命名捕获组、Lookbehind、/v flag)在现代应用中有哪些?

  • 命名组的可读性收益
  • 后行断言的匹配语义
  • v 标志的集合运算应用

现代应用:命名捕获组((? ...))——复杂模式(URL 解析、日志格式、模板语法)的字段化提取,groups.name 直读 + $ 替换引用 + \k 反向引用,模式可读性与维护性大幅提升(数字索引随分组修改易错位);后行断言 Lookbehind((?<=...) 与 (?<!...))——"前面是/不是某内容"的边界匹配:金额数字提取((?<=$)\d+)、文件名去扩展名(\w+(?=.\w+$)、取扩展名前部分)、密码强度校验的上下文约束、词边界精细化(不消费字符的零宽断言);/v 标志(unicodeSets)——字符类集合运算(交集/差集)处理复杂字符集(中文区间扣除标点、emoji 与文字混合的校验、Unicode 属性组合),嵌套字符类表达可读性。工程注意:Lookbehind 在部分旧引擎不支持(需特性检测)、固定长度约束(JS 后行断言要求固定长度,可变长度需重构)、v 标志与转译/特性检测、命名组重名冲突报错。组合实践:用命名组 + Lookbehind 解析结构化文本(如日志行:(?...) 前缀断言)、v 标志做国际化字符集策略。

本题考察正则新特性的应用面。核心是"命名组字段化 + Lookbehind 零宽上下文 + v 集合运算"三类能力与典型场景,兼容性检测是工程落点;能给出组合解析示例即展示实战能力。

#
★★

46. JSON Modules Import Attributes 提案在 JSON 导入的类型安全

JSON Modules Import Attributes 提案如何为 JSON 导入提供类型安全?

  • with { type: "json" } 的声明
  • 解析器与模块图的安全约束
  • 与打包器/TS 的协作

JSON Modules 提案允许 ESM 直接导入 JSON:import data from "./cfg.json" with { type: "json" }——Import Attributes 显式声明模块类型,浏览器/Node 据此选择对应解析器(JSON 模块解析器),约束:必须声明类型(无声明报错,防止 MIME 混淆与任意文件被当 JSON 执行)、类型不匹配报错(如声明 json 但实际不是);JSON 模块按规范默认导出解析后的对象(且其成员为"只读"视图——冻结语义,防止修改影响缓存与共享)。类型安全层面:声明式类型让构建期工具(打包器、TypeScript)可以校验——TS 的 resolveJsonModule + 声明文件生成推导出 JSON 结构类型(字面量类型/接口),导入值获得静态类型检查(字段拼写、类型错误编译期暴露);打包器(Vite/Webpack)原生支持 JSON 导入并做 tree-shake(仅保留用到的字段)。工程注意:Import Attributes 的语法(with 关键字,早期 import assertions 的 assert 已废弃)、Node 与浏览器的支持进展(需特性检测/转译)、JSON 模块的缓存与只读语义(修改抛错或静默失败)、大 JSON 按需拆分成小模块以利 tree-shaking。工程价值:配置/常量/词典以模块方式导入获得类型与静态分析,替代 fetch + JSON.parse 的运行时解析。

本题考察 JSON 模块化的类型安全链路。核心是"with 声明驱动解析器 + 只读导出 + TS/打包器静态协作"的模型,语法演进(assert→with)是易混点;能说明 tree-shake 收益即展示工程价值。

#
★★

47. Float16Array 在 WebGPU 与 ML 模型加载的工程应用

Float16Array 在 WebGPU 与 ML 模型加载中有哪些工程应用?

  • 半精度浮点的内存优势
  • WebGPU 纹理与计算数据
  • ML 模型权重的加载处理

Float16Array(ES2025)是半精度浮点(16 位,约 3 位十进制有效数字)的 TypedArray,内存与带宽为 Float32Array 的一半(缓存友好、传输更快),精度足够部分场景:WebGPU——纹理数据(RGBA16F、R16F 格式)与顶点属性(half float)直接以 Float16Array 视图填充 buffer,shader 内按半精度读取,减少显存占用与上传带宽(GPU 原生支持 fp16 计算);ML 模型——量化权重(fp16 权重是常见部署格式,如 ONNX fp16、LLM 半精度权重)以 Float16Array 读取二进制权重(new Float16Array(buffer) 直接视图解析),避免"先转 float32 再喂 GPU"的转换开销与精度损失;图像/音频的 HDR 数据(半精度像素、16 位 PCM)。工程注意:JS 侧对 Float16Array 的读写有转换开销(引擎模拟半精度运算,性能低于 Float32Array,仅作存储视图时避免逐元素读写的热路径)、精度损失(大数值/高精度计算不要用 fp16)、ArrayBuffer 传输与 transfer、WebGPU 的 feature 支持(shader-f16 需要特性检测);TypedArray 系列的新增成员(Float16Array)与 DataView.getFloat16 配套使用。

本题考察半精度类型的工程定位。核心是"存储/带宽减半 vs JS 侧转换开销 + 精度取舍"的边界,WebGPU 与 ML 权重是典型场景;能说明何时只作视图不做计算即展示性能思维。

#
★★

48. Error.cause 链在错误传播与日志追踪的工程价值

Error.cause 链在错误传播与日志追踪中有何工程价值?

  • 分层包装保留根因
  • 日志的链路展开
  • 根因聚合与告警

工程价值:分层传播——底层错误(DB、网络、解析)在各层被包装(new ServiceError(msg, {cause})),每层附加自己的上下文(操作、参数、状态码),cause 链完整保留原始错误与堆栈,上层消费方既看到业务语义又保留根因;日志追踪——结构化日志序列化错误时遍历 cause 链(记录每层 name/message/stack/code),把"调用链上下文 + 根因"写入一条日志,排查时从表现一路追到根因,无需猜测中间层发生了什么;监控聚合——以"根因指纹"(最底层错误按 name/message/stack 归一化哈希)做错误分组,业务层错误按 cause 归组到基础设施问题(如"所有接口失败都是某依赖超时"),告警按根因收敛;重试决策——按 cause 类型决定是否重试(网络抖动可重试、业务校验不可重试)。实践要点:包装时避免丢失 cause(always 传 cause)、防止 cause 链环(对象图循环防护)、限制链深度(上报体积)、cause 可为任意值(非 Error 时包装);Node 的 Error 序列化与 util.inspect 已展示 cause 链,DevTools 栈中可折叠查看。

本题考察错误链的观测价值。核心是"分层包装保留根因 + 日志展开 + 根因指纹聚合"的闭环,重试策略联动是加分点;能给出指纹归一化思路即展示可观测性设计。

#
★★

49. TC39 提案治理流程(Stage 0 → Stage 4)

TC39 提案治理流程(Stage 0 → Stage 4)是怎样的?

  • 各阶段的门槛与交付物
  • champion 与委员会的角色
  • 阶段回退与推进条件

TC39 提案流程:Stage 0(strawperson)——任何人可提交想法,进入 backlog;Stage 1(proposal)——委员会采纳,指定 champion(推进负责人),形成问题陈述、API 方向与用例(含 polyfill 演示);Stage 2(draft)——规范文本初稿(正式语法与语义描述),API 大框架冻结(细节可改),通常提供 Babel 插件供试验;Stage 3(candidate)——规范文本完整,需实现(浏览器/V8 等)与测试套件(test262)就绪,语义只做小修订(fix 级);Stage 4(finished)——至少两个独立实现通过全部测试、TypeScript 等生态确认、规范编辑批准,正式纳入 ECMAScript 版本。治理角色:champion 负责推进与答辩、reviewer 审查语义、委员会(全会)表决每阶段晋升;推进条件:每个阶段有明确门槛(文档、实现、测试),不合格会被打回(stage back)或搁置(如早期管道/绑定操作符长期停留);会议以共识而非投票为主,浏览器厂商与学术/社区代表共同参与。工程视角:跟随 Stage 3+ 提案评估试用(锁版本)、Stage 4 才无转译使用;提案状态查询(tc39.es/process-document、proposals 仓库)与 test262 覆盖是质量信号。

本题考察标准治理的流程化认知。核心是"五阶段门槛(想法→文档→草案→实现→标准化)+ champion/共识治理"的模型,回退与搁置是真实治理现象;能说明 Stage 3 到 4 的实现门槛即展示客观认知。

#
★★

50. Object.groupBy 与 Map.groupBy 在数据聚合的现代实践

Object.groupBy 与 Map.groupBy 在数据聚合上有哪些现代实践?

  • 分组回调与键类型差异
  • 分组结果的迭代与统计
  • 与 reduce 的对比

Object.groupBy(items, callback) 按回调返回值把元素分组为普通对象(键为字符串,组内元素保持原顺序),Map.groupBy 返回 Map(键可为任意类型,如对象/Symbol/枚举值)——ES2024 标准方法,替代手写 reduce 分组样板:const groups = Object.groupBy(users, u => u.department)。现代实践:统计聚合——分组后对每组 map 统计(计数、求和、平均:Object.entries(groups).map(([k, v]) => ({k, count: v.length, ...})));状态机分发——按状态字段分组渲染(待处理/进行中/完成列表)、按类型分组处理;Map.groupBy 场景——键需要对象身份或 Symbol(协议类型分组)、键非字符串(数值区间分组用回调返回 number)、保留键的语义(WeakMap 不可枚举的限制由 Map.groupBy 补足——注意 Map 强引用键);与 reduce 对比——groupBy 是"只分组"的声明式 API(回调只返回键,不能同时聚合),聚合需求仍用 reduce 或 groupBy + map 两步;边界:回调返回 undefined/NaN 等值时的键处理(undefined 键在 Object 版本被忽略或成属性)、稀疏数组跳过、分组结果按首次出现顺序保留键序、callback 的 thisArg。工程实践:数据看板的分组统计、列表分桶、权限按角色分组。

本题考察分组聚合的标准实践。核心是"Object 版字符串键 vs Map 版任意键"的差异与"只分组不聚合"的定位,键序与 undefined 边界是细节;能给出分组统计的两步式即展示工程写法。

#
★★

51. Temporal API(已纳入 ES2026)相比 Date 的时区、非格里高利历、Duration/Instant 在金融与排程系统的工程价值

Temporal API 相比 Date 在时区、非格里高利历、Duration/Instant 上有何工程价值?

  • 类型化的时间模型
  • 时区与历法支持
  • 金融与排程场景的精度

Temporal 把时间拆分为强类型对象:Instant(绝对时刻,UTC 时间线上的点)、ZonedDateTime(带时区/日历的日期时间)、PlainDate/PlainTime/PlainDateTime(无时区的日历时间)、Duration(时间段)、TimeZone/Calendar 对象——每个类型语义明确、不可变,运算(add/subtract/until/compare)按类型规则进行,杜绝 Date 的"毫秒数 + 隐式本地时区"混叠问题。工程价值:时区——ZonedDateTime 明确携带 IANA 时区("Asia/Shanghai"),转换/运算正确处理夏令时与历史时区变化(Date 的本地时区隐式、DST 调整错位);历法——Calendar 支持非格里高利历(农历、希伯来历等),国际化排程不丢历法语义;Duration——明确的时间段运算("2 个月 + 3 天"的日历语义 vs 纯时长),金融场景(利息期、结算日)与排程场景(定时任务、账单周期)获得确定性的计算规则;Instant 提供无时区歧义的绝对时间戳(事件日志、跨时区协作)。工程价值:金融系统(计息日、到期日、跨时区交易日)、调度系统(cron 语义、时区感知排程)、日历应用(多历法、夏令时);对比 Date 的日期字符串解析歧义、setHours 等方法的 DST 坑,Temporal 的不可变 API 与明确类型显著降低时间 bug;需 polyfill(官方 @js-temporal/polyfill)渐进接入。

本题考察现代时间 API 的类型化价值。核心是"Instant/ZonedDateTime/Plain/Duration 类型矩阵 + 时区历法语义"对金融排程的确定性收益,与 Date 的坑点对比是落点;能举例 DST 或计息日场景即展示领域认知。

#
★★

52. Array.fromAsync 在异步可迭代对象与 for await ... of 的协作场景

Array.fromAsync 在异步可迭代对象与 for await...of 的协作场景有哪些?

  • 异步序列的物化
  • mapFn 的异步映射
  • 与手写循环的对比

Array.fromAsync(iterable, mapFn?) 把异步可迭代对象物化为数组:内部串行 await 每个元素(保持顺序),mapFn 可以是异步函数(逐项映射后可继续 await),替代手写 for await...of + push(或 + map 累加)的样板。协作场景:异步分页聚合——async generator 产出各页数据,fromAsync 一次收集全部(如拉取全部翻页记录做统计);异步流收集——fetch 流/WebSocket 消息流逐条收集中间态处理;含 Promise 的同步数组——[p1, p2, p3] 直接物化为兑现值数组(但注意是串行 await 而非 Promise.all 并行);async map——对异步可迭代源做映射收集(mapFn 异步,每项 await 后 push)。与 for await...of 的取舍:for await...of 适合"边消费边处理"(流式、提前退出、逐项副作用),fromAsync 适合"需要完整数组结果"(后续批量处理、传递数据);fromAsync 的语义是串行(每项 await 完再取下一项,与并行 Promise.all 区分);错误处理——任一 rejected 中断整体(整体拒绝,与 allSettled 的隔离不同),需批量容错时配合包装;注意非异步可迭代输入会被包装为异步迭代(同步数组也支持)。

本题考察异步物化的协作语义。核心是"串行 await 物化 + 异步 mapFn + 与 for await 的消费/收集分工",错误中断是边界;能对比 Promise.all 的并行差异即展示细节。

#
★★

53. ArrayBuffer 的 transfer()/resize() 在 WebAssembly 内存与音频工作线程的边界

ArrayBuffer 的 transfer()/resize() 在 WebAssembly 内存与音频工作线程中有哪些边界?

  • resizable ArrayBuffer 与 grow 语义
  • transfer 的所有权移动
  • 共享内存与线程的边界

ArrayBuffer 新能力:resize(newLength)——resizable 缓冲区(new ArrayBuffer(len, {maxByteLength}))在不超上限时调整大小(内存就地扩展/收缩,不产生新缓冲、引用不失效);transfer()——创建新缓冲并移动数据,旧缓冲 detach(所有权转移);transferToFixedLength()——转移为固定长度。WASM 边界:WebAssembly.Memory 增长(grow)时其 buffer 会被替换为新 ArrayBuffer(旧缓冲 detach)——JS 侧需监听 growth 后重新获取 buffer 重建视图;resizable 缓冲可用于"JS 与 WASM 共享可调整内存"的规划(共享同源可 grow 的 buffer 需约定增长协议);transfer 用于内存重组(扩容后迁移到更紧凑的分配)。音频工作线程边界:AudioWorklet/Worker 中音频数据用共享内存(SharedArrayBuffer 或 transfer 的 ArrayBuffer)零拷贝传递——transfer 把缓冲所有权移给音频线程(主线程 detach 后不可再用,符合"音频数据一次性提交"模型);resize 不适合共享缓冲(SharedArrayBuffer 的 grow 需额外协议);边界注意:resizable 缓冲 resize 后所有视图(TypedArray)长度自动跟随(byteLength 变化)、resize 可能失败(超上限抛 RangeError)、transfer 后的旧引用访问抛 TypeError(已 detach)、音频回调中不可分配/transfer(实时线程约束,需在回调外预分配缓冲池)。

本题考察缓冲新能力的工程边界。核心是"resize 就地调整 vs transfer 移动所有权"的语义与 WASM grow detach、音频线程所有权移交的场景映射,视图跟随与 detach 后访问是易错点;能说明音频线程的预分配模式即展示实战经验。

#
★★

54. Promise.try 在同步抛出异常与异步函数统一捕获错误处理的工程价值

Promise.try 如何统一同步抛错与异步拒绝的错误处理?有何工程价值?

  • 统一包装同步/异步函数
  • 消除 try/catch 与 catch 链的分裂
  • 边界处理与兼容

Promise.try(fn) 调用 fn 并统一包装结果:fn 同步抛错 → 返回拒绝的 Promise;fn 返回 Promise → 状态跟随(拒绝传播);fn 返回普通值 → 兑现。工程价值:统一错误通道——调用"可能同步抛错也可能异步拒绝"的代码(第三方库、配置驱动函数、动态加载模块)时,只需 catch 链一处处理两种错误形态,消除"同步 try/catch + 异步 catch"的分裂写法:const result = await Promise.try(() => loadConfig(type)).catch(err => fallback);组合性——包装后的函数可安全参与 Promise.all/allSettled/race 等组合(同步抛错不会中断组合器的执行流程——未包装时同步抛错会直接逃逸出组合调用栈);与 async 函数对比——async () => fn() 也有类似效果,但 Promise.try 不引入 async 语义(无额外微任务包装层级,语义更纯粹、性能略优);边界:fn 必须可调用(否则抛 TypeError 被包装拒绝?规范为对非函数直接拒绝)、Promise.try 同步调用 fn(立即执行,不延迟)、与 withResolvers 等 ES2025 特性配套。工程实践:策略模式、插件加载、输入驱动的动态调用统一用 Promise.try 包装后进入异步管线。

本题考察统一错误通道的工具语义。核心是"同步抛错转拒绝 + 状态跟随"的统一包装能力与组合器安全参与的价值,async 包装的对比是细节;能给出策略加载的示例即展示工程应用。

#
★★

55. Sync Iterator 与 Async Iterator 协议在 Node.js Streams、Web Streams 与 Observables 的统一抽象

Sync Iterator 与 Async Iterator 协议如何在 Node.js Streams、Web Streams 与 Observables 中形成统一抽象?

  • 双协议的结构差异
  • 流与异步迭代的接入
  • Observables 与迭代模型的关系

迭代协议([Symbol.iterator] 返回同步迭代器、[Symbol.asyncIterator] 返回 next 返回 Promise 的异步迭代器)成为"数据序列"的统一抽象:Node.js Readable 与 Web ReadableStream 都实现 [Symbol.asyncIterator](for await...of 直接消费),把"流式字节序列"统一为"异步元素序列";TransformStream/生成器构建的管道同样以迭代形式暴露;Observables 的差异——Observable(如 RxJS)是"推送型"(push:数据主动到达,可随时取消,支持多播与操作符),迭代器是"拉取型"(pull:消费者按需索取,单播、有 done 终止语义);统一抽象的价值——把 Node 流、Web 流、生成器、可迭代集合统一为可组合的数据序列(Iterator Helpers 变换、for await 消费、fromAsync 物化),降低多来源数据的适配成本;RxJS 提供 from(可迭代对象→Observable)与 toArray 等互转。边界:流的背压与取消经迭代协议部分暴露(提前退出触发 return→底层 cancel),但流的暂停/恢复细节(水线、精确缓冲)仍在流 API 层;Observable 的推送语义无法完整映射到拉取迭代(需要异步迭代的"等待推送"模式);工程上按"消费驱动(拉)还是事件驱动(推)"选型,混合场景用互转适配器。

本题考察序列抽象的统一模型。核心是"拉取(迭代器)vs 推送(Observable)"的语义分野与双迭代协议对流的统一接入,背压/取消的暴露边界是关键;能说明 RxJS from 互转即展示生态衔接。

#
★★

56. Logical Assignment Operators(ES2021)

逻辑赋值运算符(ES2021)的语义与工程应用是什么?

  • ||=/&&=/??= 的短路语义
  • 与朴素赋值的差异
  • 默认值与条件更新的应用

逻辑赋值运算符(ES2021):||= —— 左值为 falsy 时赋值(a ||= b 等价 a = a || b,短路:左侧为 truthy 时不求值右侧);&&= —— 左值为 truthy 时赋值(左侧 falsy 时短路);??= —— 左值为 null/undefined 时赋值(空值合并赋值,注意与 ||= 的区别:0、""、false 不触发 ??=)。与朴素赋值差异:短路性(右侧表达式仅在需要时求值——避免不必要的函数调用/计算,且语义更明确);用于:默认值初始化(this.count ??= 0、配置对象补默认字段 opts.timeout ??= 5000)、条件更新(只读保护:obj.value &&= compute())、惰性初始化(缓存字段 this._cache ??= new Map()——首次访问才创建,之后直接复用)、事件处理中的去空合并(handler ??= defaultHandler)。工程注意:??= 与 ||= 的语义混用错误(业务默认值用 ??= 更精确,因为 0/空串可能是有效值)、链式组合的优先级、对 null 原型对象/代理对象的行为(触发 set trap——响应式场景中 ??= 会触发依赖更新);配合可选链的现代写法:obj?.a ??= 1。

本题考察逻辑赋值运算符的语义细节。核心是"短路求值 + ??= 与 ||= 的空值/假值差异",默认值与惰性初始化是典型应用;能指出 0/"" 有效值场景选 ??= 即展示工程判断。

#
★★

57. 字符串不可变性对大字符串拼接性能的影响与 Array.join/StringBuilder 模式的取舍

字符串不可变性对大字符串拼接性能有何影响?Array.join/StringBuilder 模式的取舍是什么?

  • 不可变字符串的复制成本
  • 拼接的引擎优化(rope)
  • 大字符串场景的模式选择

JS 字符串不可变:每次拼接(+、+=)都产生新字符串,理论上小字符串拼接 n 次是 O(n²) 的复制成本(每次复制全部已拼内容)。但现代引擎(V8)对字符串拼接采用惰性树结构(rope/cons string):+ 只建立"拼接节点"引用原片段,不立即复制,直到需要具体内容时才扁平化(且可优化),因此常规规模拼接性能可接受;隐患:rope 树过深(极端拼接次数)或长链中反复子串截取(substring 可能保留父串引用)会造成内存浪费与访问开销;跨引擎行为不同(部分引擎无 rope)。取舍:Array.join——先把片段 push 到数组再 join 一次成串,是"显式批量拼接"的经典模式(中间无长串累积、内存可控),适合大量动态片段(模板渲染、列表转文本、代码生成);StringBuilder 模式(JS 中即数组 + join 或现代模板/分段写入)同理,配合 buffer 分块(如输出流分块 write)避免超大单串;模板字符串适合少量片段;性能结论:大多数业务拼接用 +/模板即可(引擎优化),> 千级片段或超大串用数组 join/分块写入,并避免"循环内频繁截取长串"的坏模式。

本题考察字符串性能模型。核心是"不可变语义 + 引擎 rope 优化 + 何时需要显式批处理"的平衡,join 模式与 rope 深度的边界是关键;能指出 substring 引用父串的内存坑即展示细节。

#
★★

58. replaceAll 与带 g 标志 replace 在捕获组引用($1、$&, $`)行为差异及函数替换的边界

replaceAll 与带 g 标志的 replace 在捕获组引用($1、$&、$`)与函数替换上有何行为差异?

  • replaceAll 的全局语义与参数限制
  • 替换字符串的 $ 模式
  • 函数替换的返回值语义

replaceAll(pattern, replacement) 替换全部匹配:pattern 为字符串时按字面量替换所有出现(无需转义,replace 字符串只替换首个)、为正则时必须带 g 标志(否则抛 TypeError);带 g 的 replace 等价"替换全部",但 replaceAll 语义更明确(且字符串 pattern 不会触发正则特殊字符)。替换字符串的 $ 模式(二者一致):$&(整个匹配)、$1-$n(捕获组)、$(匹配前文本)、$'(匹配后文本)、$$(字面 $)、$<name>(命名组)——替换时注意 $ 转义(用户输入含 $ 需转义为 $$ 或走函数替换)。函数替换:replacement 为函数时接收 (match, ...captures, offset, string)(命名组在最后参数 groups),返回字符串作为替换结果——差异点:函数返回值永远按字符串处理(返回 undefined 变成 "undefined" 文本,需显式返回空串)、函数在每次匹配处调用(全局多次)、可通过返回值做动态替换(条件替换、大小写转换、格式化);replaceAll 与函数替换组合完成"逐匹配处理"。边界:replaceAll 的字符串 pattern 对特殊字符无正则语义(安全)、regexp.lastIndex 在 replaceAll 中被忽略(不依赖全局状态)、$ 与 $' 的大段文本拼接注意性能(超长匹配时)。

本题考察替换 API 的语义矩阵。核心是"replaceAll 全局语义与 g 要求 + $ 模式体系 + 函数替换参数/返回值"三层,$ 转义与 undefined 返回值是易错点;能说明字符串 pattern 的安全性即展示工程细节。

#
★★

59. 正则 sticky(y)标志与 exec() 循环在词法分析器(Tokenizer)中的工程价值

正则 sticky(y)标志与 exec() 循环在词法分析器(Tokenizer)中有何工程价值?

  • y 标志的 lastIndex 锚定语义
  • exec 循环的逐 token 消费
  • 词法分析的工程实现

sticky(y)标志要求匹配必须从 lastIndex 位置开始(不自动扫描后续位置),失败时(规范)重置 lastIndex 为 0:exec 循环中每轮用 y 正则从当前位置匹配一个 token,成功则 lastIndex 前进到匹配末尾,实现"指针式"逐 token 消费——与 g 标志(全局扫描、可能跳过不匹配文本)不同,y 保证严格的位置推进,天然适合词法分析:tokenizer 按关键字/标识符/数字/运算符的正则集合在当前位置依次尝试,匹配推进、失败报告"非法字符位置",无需手动维护 offset;lastIndex 既是指针又是进度(配合 regexp.exec 与 sticky 自动前进)。工程价值:编译器/解析器的 token 化、模板引擎的标签解析、Markdown/协议文本的扫描器——y 标志提供"锚定 + 推进"的内建语义,替代手写"slice + 手动 index 更新"的状态机样板;注意:y 失败后 lastIndex 归零(需保存外部位置或重新设置)、y 与 g 同用时的语义(sticky 优先)、每类 token 使用独立正则或组合(顺序尝试 + 最长匹配策略);性能上 y 避免了 g 的"扫描+验证"开销(直接从 lastIndex 尝试)。

本题考察 sticky 标志的扫描器语义。核心是"y 锚定 lastIndex + exec 循环推进 = 指针式 token 消费"的模型,与 g 的差异(不跳过)与失败复位是易错点;能给出 tokenizer 骨架即展示编译原理实践。

#
★★

60. 手写 bind,柯里化参数、new 调用忽略 this 与 prototype 链保留的工程边界

手写 bind 时,柯里化参数、new 调用忽略 this 与 prototype 链保留的工程边界是什么?

  • 参数合并的柯里化顺序
  • new 调用时 this 覆盖
  • prototype 链的保留与 instanceof

手写 bind 的三边界:柯里化参数——bind 时传入的参数与调用时传入的参数合并(bind 参数在前、调用参数在后),需用展开符拼接(bound(...args) 内 fn.apply(thisArg, [...boundArgs, ...callArgs]));new 调用——返回函数被 new 时 this 必须指向新实例而非 bind 的目标 thisArg(规范语义),实现用 new.target 判断:构造调用时 fn.apply(newInstance, args)(newInstance 由 new boundFn 创建);prototype 链保留——绑定函数应继承原函数 prototype(boundFn.prototype = Object.create(originalFn.prototype) 或在 ES6 中利用"绑定函数 [[Prototype]] 指向目标函数"的规范行为),使 new boundFn() 后实例 instanceof originalFn 为 true;边界:箭头函数作为目标时不可 new(无 prototype、无构造语义,new 抛 TypeError)、bind 后 length 属性的变化(绑定函数 length 为原函数 length 减绑定参数数——手写可近似)、thisArg 为 null/undefined 时的默认绑定(普通调用走全局)、柯里化参数与 new 同时(参数仍合并);工程注意:原型链复制方式(Object.create 保留属性查找)、绑定函数 name 的保留(可设 bound fn.name = "bound " + original.name)。

本题考察 bind 手写实现的完整边界。核心是"参数合并 + new.target 分流 + 原型链保留"三者的协同,箭头函数限制与 length 变化是细节;能说明 new 与柯里化共存的行为即展示规范掌握。

#
★★

61. 手写 EventEmitter,once、removeListener 与内存泄漏防护(setMaxListeners)

手写 EventEmitter 时,once、removeListener 与内存泄漏防护(setMaxListeners)如何实现?

  • 事件名→监听器列表的映射
  • once 的一次性包装
  • 泄漏警告与容量治理

手写 EventEmitter 的核心结构:events 映射(事件名 → 监听器数组),emit 时复制数组遍历调用(避免回调中移除导致迭代错乱);once——包装监听器:包装函数内部执行原监听器后立即移除自身(且调用时机正确:只触发一次、removeListener 时能定位到原监听器——需在包装函数上挂原函数引用);removeListener——按引用从数组移除(全量移除同名用 removeAllListeners),移除后维护数组紧凑(splice);错误处理——emit 时监听器抛错不应中断其他监听器(逐个 try/catch 或异步传播);内存泄漏防护——监听器数量阈值(Node 的 setMaxListeners 默认 10):超出阈值时 emit 警告(console.warn 提示"可能泄漏"),用于提醒"动态添加监听器不清理"的模式(循环注册、事件总线滥用);实现要点:事件的"新监听器"钩子(Node 的 newListener 事件)、once 与 removeListener 的联动(once 包装后 removeListener 需识别)、off 别名、异步事件(监听器可以是 async,错误处理注意 unhandledRejection);工程实践:事件总线在组件卸载时移除全部相关监听(弱引用或登记表)、命名规范避免拼写错误(用常量/Symbol 事件名)。

本题考察事件系统的实现细节。核心是"映射结构 + once 包装移除 + 容量警告"三模块,迭代中修改列表与 once/remove 联动是易错点;能给出卸载清理模式即展示工程实践。

#
★★

62. 手写 instanceof,递归查找原型链与 Symbol.hasInstance 自定义的边界

手写 instanceof 时,递归查找原型链与 Symbol.hasInstance 自定义的边界是什么?

  • instanceof 的原型链算法
  • Symbol.hasInstance 的优先语义
  • 边界(非函数右值、跨 Realm)

instanceof 的标准算法:右值必须是函数(否则抛 TypeError——手写需检查 typeof 为 function),取右值的 prototype 属性,沿左值对象的原型链(proto/Object.getPrototypeOf 逐级)查找:找到返回 true、到 null 未找到返回 false(原型链终止于 Object.prototype 之后为 null)。手写实现:let proto = Object.getPrototypeOf(obj); while (proto) { if (proto === Ctor.prototype) return true; proto = Object.getPrototypeOf(proto); } return false。Symbol.hasInstance 边界:instanceof 运算符会优先调用右值的 [Symbol.hasInstance] 方法(若存在)——class 可自定义 static [Symbol.hasInstance] 改变判定逻辑(自定义类型判定、白名单验证),手写实现需模拟该优先调用(right[Symbol.hasInstance] ? rightSymbol.hasInstance : 原型链算法);未定义时默认行为是原型链查找(Function.prototype[Symbol.hasInstance] 默认实现)。其他边界:跨 Realm——右值构造函数的 prototype 与左值原型链来自不同 Realm 时返回 false(不是错误,是语义如此);右值为 bound 函数(用其 target 的 prototype);对象非函数右值抛 TypeError 需防御。工程价值:理解 instanceof 的机制用于类型判断工具(结合 Symbol.hasInstance 做多态判定)、调试原型链问题。

本题考察 instanceof 的算法与扩展点。核心是"原型链逐级查找 + Symbol.hasInstance 优先调用"的双路径模型,非函数右值抛错与跨 Realm 是边界;能给出自定义 hasInstance 的判定示例即展示元编程能力。

#
★★

63. 资源加载错误为何需要用 window.addEventListener('error', ..., true) 捕获阶段监听,其与 window.onerror 的信息差异

资源加载错误为何要用捕获阶段监听 error?与 window.onerror 的信息差异是什么?

  • 资源错误不冒泡的机制
  • 捕获阶段监听的必要性
  • 事件参数的信息差异

资源加载错误(img/script/link 等元素加载失败)不冒泡:error 事件在资源元素上触发后不会沿 DOM 冒泡到 window,因此 window.onerror 无法捕获资源加载失败——必须在捕获阶段监听:window.addEventListener("error", handler, true)(第三参数 true 启用捕获),事件沿捕获路径下行时在 window 层被拦截,从而捕获所有后代元素的资源错误。信息差异:资源错误的 event 是 Event 而非 ErrorEvent——没有 message/filename/lineno 等脚本错误信息,只有 target(失败的资源元素)、src/href(可经 target 读取)等;window.onerror 的 (message, source, lineno, colno, error) 参数对应脚本运行时错误(ErrorEvent,含错误对象与栈),不适用于资源错误;因此两类错误要分开处理:脚本异常走 onerror(ErrorEvent 语义),资源加载失败走捕获阶段 error 监听(检查 target 与 src 判断资源类型、上报 URL 与阶段)。实践要点:捕获监听中判断 event.target 是否资源元素(img/script/link)并区分 script 错误(ErrorEvent)与资源错误(Event)、避免重复上报(同一资源错误在捕获阶段一次)、onerror 与 addEventListener 同时存在时注意 preventDefault 的抑制行为(阻止默认控制台报错)。

本题考察错误监听的机制差异。核心是"资源错误不冒泡 → 捕获阶段监听"的必要性与"Event vs ErrorEvent"的信息差异,按事件类型分流上报是关键;能区分 script 错误与资源错误即展示监控完整性。

#
★★

64. Source Map 的原理(VLQ 编码、mappings 字段)与线上压缩栈还原为源码位置的基本流程

Source Map 的原理(VLQ 编码、mappings 字段)是什么?线上压缩栈如何还原为源码位置?

  • source map 的 JSON 结构与 mappings
  • VLQ 编码的偏移语义
  • 栈还原(行列映射)流程

Source Map 是 JSON 文件(version、sources、sourcesContent、names、mappings):mappings 字段是分号分隔的行序列(每行对应生成文件一行),行内逗号分隔段,每段用 VLQ(Base64 VLQ)编码 1-4 个字段:生成列偏移、源文件索引偏移、源码行偏移、源码列偏移(及 names 索引)——全部是"相对上一段的增量"(delta),VLQ 用 6 位二进制(1 位连续位 + 5 位数据)表示带符号整数,base64 字符集输出,实现压缩表达。还原流程:拿到压缩代码的栈(行列号)→ 加载 source map → 定位 mappings 中"目标行"的段序列 → 按相对偏移累加还原"生成列 → 源码文件/行列" → 得到源码位置(配合 sourcesContent 展示源码片段);现代还原还支持"列级精确定位 + 变量/词法作用域映射"(sections、names 的标识符映射)。工程注意:线上栈的列号来自压缩后代码(minify 后一长行),还原需真实列号(错误监控 SDK 要上报 colno);发布时 source map 不上线(或受控发布)——避免源码泄露;还原工具(source-map 库、Sentry 等平台的 upload + 服务端映射)在服务端/离线完成;VLQ 的增量解码(逐段累加)与分号/逗号的分隔语义是实现细节。

本题考察 source map 的编码原理与还原流程。核心是"mappings 的段结构 + VLQ 相对偏移编码 + 行列累加还原"的链条,列号真实性是工程前提;能说明为何要服务端还原即展示安全实践。

#
★★

65. hidden-source-map 配合上报通道的栈解析方案,以及 Source Map 直接暴露的安全风险与访问控制

hidden-source-map 配合上报通道的栈解析方案是什么?Source Map 直接暴露有何安全风险与访问控制?

  • hidden-source-map 的构建形态
  • 服务端栈解析的通道设计
  • 访问控制与风险治理

hidden-source-map(webpack 配置)只生成 .map 文件不注入 sourceMappingURL 注释(或注入到隐藏位置):浏览器不会自动加载 map(减少请求与暴露),但 map 文件随构建产物发布,配合"上报通道"做服务端栈解析——前端错误上报(含行列号)到平台,平台持版本对应的 source map 在服务端/离线把压缩栈还原为源码栈(source-map 库解析,含 sourcesContent 展示原文),并做指纹聚合与告警;部署时 map 文件通常放在受限目录(不随静态资源公开访问)或内网/对象存储私密桶,或构建后仅存 CI 制品库。安全风险:source map 包含完整源码(sourcesContent)——直接公开等于源码泄露(商业逻辑、密钥模式、内部接口路径曝光);攻击者可从公开 map 逆向出未压缩源码;风险治理:map 不公开(CDN/静态目录排除)、访问鉴权(签名 URL、内网访问)、map 与产物分离(服务端解析用,前端不加载)、发布前扫描禁止 .map 上线(CI 检查)、敏感代码(密钥、内部逻辑)避免进入 sourcesContent(或构建时剔除)、上传平台时配置访问控制(白名单);"直接暴露 vs 服务端解析"的取舍:解析准确性(服务端持完整 map)与响应性(前端直接解析快但暴露)权衡,多数成熟监控走"上传 map + 服务端解析"。

本题考察 source map 的发布安全与解析架构。核心是"hidden 不注入引用 + 上报通道服务端还原 + 访问控制防泄露"的三段设计,sourcesContent 泄露面是风险重点;能给出 CI 扫描禁上线的治理即展示安全工程。

#
★★

66. Error.isError 与 Object.prototype.toString.call 在跨 Realm(iframe/Worker)错误判定中的各自作用

Error.isError 与 Object.prototype.toString.call 在跨 Realm 错误判定中各自起什么作用?

  • toString.call 的标签机制与伪造风险
  • Error.isError 的内部槽判定
  • 判定的可靠性分层

Object.prototype.toString.call(value) 返回类型标签("[object Error]"):通过读取内置 Symbol.toStringTag(或默认内部类型)生成标签,跨 Realm 也能工作(不依赖原型链);但它可以被"伪造"——对象自定义 [Symbol.toStringTag] = "Error" 即可冒充 Error 标签,且 toString.call 只给出标签字符串,无法区分真实 Error 与伪装对象。Error.isError(value)(ES2025)按规范错误对象语义判定:检查是否为真正的 Error 对象(内部错误状态/错误对象身份,含 Error 子类),不受 toStringTag 伪造影响、跨 Realm(iframe/Worker/不同实例)可靠。各自作用:toString.call 是"通用类型标签工具"(兜底识别各种内置类型、无 isError 环境的降级),Error.isError 是"错误判定专用且可靠"的现代 API;工程实践:错误归一化流程先用 Error.isError(受支持时)判真伪,再提取 name/message/stack;环境不支持 isError 时降级为 toString.call 但需知晓伪造风险(配合"对象自身没有 toStringTag 覆盖"检查或信任边界假设);跨 Realm 场景(iframe 抛错、Worker onerror、微前端子应用错误)两者都可用,但可靠性 isError 更优。注意:Error.isError 对"带 message 的普通对象"返回 false(不做鸭子类型判定),需要形状判定时另行处理。

本题考察错误判定的机制分层。核心是"内部槽语义判定(可靠)vs 标签字符串判定(可伪造)"的对比,归一化流程中的降级策略是工程落点;能说明 toStringTag 的伪造边界即展示安全认知。

#
★★

67. error 事件上 preventDefault 对浏览器默认控制台报告与后续冒泡的影响

error 事件上调用 preventDefault 对浏览器默认控制台报告与事件冒泡有何影响?

  • 默认行为:控制台错误报告
  • preventDefault 抑制报告
  • 冒泡与捕获的传播

window 上的 error 事件(ErrorEvent)默认行为是"在控制台输出未捕获异常报告"(把错误打印到 DevTools 控制台)——调用 event.preventDefault() 可抑制该默认控制台报告(错误不再出现在控制台,或标记为已处理),用于:错误监控 SDK 已捕获并上报(避免重复输出)、已知可忽略的错误(实验性 API 的抛错)静默处理;注意 preventDefault 只抑制"默认报告行为",不阻止错误继续传播与后续监听器执行。传播语义:error 事件沿捕获/目标/冒泡路径传播(捕获阶段可由 addEventListener("error", h, true) 在 window 拦截),preventDefault 不影响传播路径本身——其他监听器仍会收到事件;对资源加载错误(Event)preventDefault 同样抑制"资源失败报告",不影响加载失败的事实。工程注意:全局错误监听中"既上报又 preventDefault"需谨慎(会隐藏真实错误于控制台,调试困难;DevTools 的"暂停在异常"依赖未被吞掉的错误)、onerror 的返回值(return true)在旧语义中等同 preventDefault(抑制默认处理)、区分"监听报告"与"吞掉错误"——监控场景建议上报但不吞,或吞后记录日志;不同浏览器对 preventDefault 的细节支持一致但输出样式有差异。

本题考察错误事件默认行为的控制。核心是"preventDefault 抑制控制台报告而非传播"的边界,监控 SDK 的吞错策略是工程决策点;能说明 onerror 返回值的历史语义即展示兼容知识。

#
★★

68. Mixin 模式与多重继承的工程取舍(类表达式组合、Symbol 冲突、super 链断裂)

Mixin 模式与多重继承的工程取舍是什么?类表达式组合、Symbol 冲突与 super 链断裂如何处理?

  • 函数式 mixin 的组合方式
  • 方法名与 Symbol 键冲突
  • super 与 [[HomeObject]] 的断裂

Mixin 模式用"函数接收类、返回扩展类"实现多能力组合(class A extends mixinB(Base)):通过类表达式逐层包装,把多个能力混入单继承链——避免多重继承的语言复杂度(菱形问题、冲突仲裁),是"组合优于继承"的常用工程折中。取舍与坑:方法名冲突——多个 mixin 提供同名方法时后者覆盖前者(隐式优先级,无显式仲裁),用命名空间前缀/委托(mixin 持有内部对象)或组合(mixins 调各自方法)缓解;Symbol 冲突——mixin 的"隐藏"能力用 Symbol 键避免与业务键冲突(但多个 mixin 用同 key 仍冲突,需命名空间化 Symbol.for 前缀);super 链断裂——mixin 方法内 super.method() 的 [[HomeObject]] 绑定在"定义处"(mixin 包装时基类的原型),多层 mixin 组合后 super 指向的是定义时基类原型而非最终链——组合顺序变化时 super 查找可能错位或抛错,需把"调用父类能力"改为显式传入(mixin 接收依赖函数)或保证组合顺序固定。工程取舍:功能内聚用继承、横切能力(日志、校验、事件化)用 mixin/装饰、复杂组合用组合(委托)优先;mixin 的链深影响性能与调试,超过 2-3 层建议重构。

本题考察 mixin 组合的工程边界。核心是"类表达式组合机制 + 三类冲突(同名/Symbol/super)"的治理,委托组合是替代方案;能说明 [[HomeObject]] 导致 super 断裂的机制即展示深度。

#
★★

69. instanceof 与 Symbol.hasInstance 在自定义类型判定中的协作与覆盖行为

instanceof 与 Symbol.hasInstance 在自定义类型判定中如何协作与覆盖?

  • 默认的原型链算法
  • 静态 Symbol.hasInstance 覆盖
  • 覆盖行为与边界

instanceof 运算符的判定入口是右值的 [Symbol.hasInstance] 方法:默认(Function.prototype[Symbol.hasInstance])执行"沿左值原型链查找右值 prototype"的算法;class 可定义 static Symbol.hasInstance 自定义判定——完全覆盖默认行为(不再走原型链),用于:判定逻辑基于业务状态(白名单/能力表)、区分"伪造实例"(对象形状像但非本类实例)、多态判定(按条件返回不同结果)、框架的类型收窄(替代 instanceof 的严格原型要求)。协作模式:自定义方法内部可手动回退到默认逻辑(调用 Function.prototype[Symbol.hasInstance].call(ctor, value) 或自行遍历原型链)、与 Symbol.toStringTag 配合(标签 + 实例判定双通道);边界:hasInstance 必须是"可调用"的(否则 TypeError)、非静态定义不生效(实例方法不影响 instanceof)、Proxy 包裹的函数(hasInstance trap 与 Symbol.hasInstance 的交互)、跨 Realm 时自定义判定不受原型链限制(正是其价值);注意覆盖后 instanceof 结果不再反映"真实的构造来源",依赖 instanceof 的既有代码(Array.isArray 风格判断)语义变化需评估;工程上自定义判定用于"语义类型"(isPoint、isToken)而非替代 Array.isArray 等标准判定。

本题考察 instanceof 的扩展点机制。核心是"Symbol.hasInstance 优先调用 + 静态覆盖默认原型链算法"的模型,业务语义判定是价值场景;能给出覆盖实现并说明与原型链的关系即展示元编程掌握。

#
★★

70. 私有方法、私有 getter/setter 与 #brand-check 模式在防御性编程中的应用

私有方法、私有 getter/setter 与 #brand-check 模式在防御性编程中如何应用?

  • 私有成员的封装与隐藏
  • #x in obj 品牌检查
  • 防御非法输入与伪造实例

私有方法(#method)、私有 getter/setter(get #x/set #x)是引擎级私有:类外不可访问、不可反射枚举,用于隐藏实现细节(内部算法、状态访问入口)——防御性编程的第一层(防止误用与篡改:外部不能直接改内部状态或调用内部流程,只能走公开 API)。#brand-check(#x in obj)检测"对象是否持有该私有品牌"(是否为本类或其品牌链创建):可用于——参数校验(公共方法入口检查传入实例是否为本类实例,拒绝伪造/非法对象:if (!(#field in candidate)) throw new TypeError)、防御鸭子类型误用(形状相似的对象不再被接受,替代"结构检查"的不可靠)、区分"本类实例 vs 其他 Realm/原型链伪造"(instanceof 可被原型链篡改,品牌检查不可伪造——私有字段无法在类外复制);防御性编程组合:私有 getter 暴露只读视图(内部用 setter 校验写入)、私有方法集中校验逻辑(公开方法转调)、品牌检查拦截异常输入。边界:品牌检查只在"持有该私有字段定义的类内代码"可用(外部代码无法执行 #x in obj——除非经类提供的静态检查方法)、子类实例持有父类品牌(通过检查)、构造时品牌已建立(构造中即可检查);工程上把品牌检查封装为静态方法(static isFoo(obj))对外提供。

本题考察私有机制在防御性编程的运用。核心是"私有成员隐藏状态/流程 + 品牌检查拒绝伪造实例"的两层防御,品牌不可伪造是机制保障;能给出静态 isFoo 检查方法的模式即展示工程实践。

#
★★

71. 继承内置对象(extends Array/Map)的行为与 Symbol.species 对派生构造的影响

继承内置对象(extends Array/Map)时行为如何?Symbol.species 对派生构造有何影响?

  • 内置对象子类的实例语义
  • 返回新实例的方法的构造器选择
  • Symbol.species 的定制影响

class MyArray extends Array 时派生类实例具备数组全部能力(length 语义、索引、迭代、方法),Array 的方法(map/filter/slice/concat 等返回新实例的方法)默认用"当前实例的构造函数"(this.constructor[Symbol.species])构造返回值——即 MyArray 的 map 返回 MyArray 实例(而非基类 Array 实例),保持类型链(链式方法不退化);同理 Map/Set 的派生构造(filter 类方法)。Symbol.species 定制:在类上定义 static get Symbol.species { return Array; } 使"返回新实例"的方法改用指定构造器(如子类需要方法返回普通数组以规避子类开销或语义)——影响点:构造函数的选取(species 决定返回值类型)、copy 类方法(slice/concat)、TypedArray 的 slice 等;不设置时默认返回 this 构造器。边界与坑:重写构造器(constructor)时未正确调用 super 或返回类型不符会破坏 length 语义(Array 子类必须正确维护 length)、species 返回非构造器时回退 Array 或抛错(规范行为:species 为 null/undefined 用默认 Array;非法值可能 TypeError)、JSON.stringify/响应式框架对数组子类实例的检测(Array.isArray 仍 true 但原型链不同)、V8 对数组子类的性能优化不如基类(PACKED 形状与 species 检查开销)——热路径评估。工程价值:类型化集合(带校验的数组、事件化 Map)继承内置获得完整语义,species 控制"派生方法的结果类型"。

本题考察内置对象继承的语义细节。核心是"方法返回值的构造器选择(species 链)+ 子类实例保持类型 + length 语义维护"的模型,species 定制与性能边界是关键;能给出自定义集合的示例即展示应用能力。

#
★★

72. 用 new.target 检测实现抽象基类(禁止直接实例化)的设计模式

如何用 new.target 检测实现抽象基类(禁止直接实例化)?

  • new.target 的构造上下文语义
  • 抽象基类的守卫模式
  • 与 TS abstract 的对比

new.target 在构造函数内指向"实际被 new 的构造函数"(派生类实例化时是派生类;基类被直接 new 时是基类自身):抽象基类守卫——class Abstract { constructor() { if (new.target === Abstract) throw new TypeError("Abstract cannot be instantiated"); } } 禁止直接 new Abstract(构造即抛错),但允许派生类继承(new Derived 时 new.target 为 Derived,通过检查)——实现"抽象类"的运行时强制,且检查发生在构造期(实例创建前拦截)。设计模式应用:抽象基类(模板方法:基类定义流程、抽象方法抛 NotImplementedError 由子类实现)、单例基类的构造限制、接口模拟(全部方法抽象 + 禁止实例化);注意:抽象方法的"未实现即用"防御——基类方法体抛"not implemented"错误(调用时报而非构造时报)、new.target 检查也可配合 Symbol.hasInstance 或品牌检查强化(防止伪造子类);与 TS abstract 的对比:TS abstract 是编译期约束(子类未实现抽象方法编译报错、抽象类不可 new),new.target 守卫是运行时防线(编译产物仍可被 JS 调用方直接 new,需运行时抛错兜底)——两者互补:TS 管开发期,守卫管运行期;边界:检查只能防"直接 new 基类",不能防"子类未实现抽象方法"(需调用期检查)、箭头函数无 new.target(构造内普通函数可用)、new.target 在普通函数调用为 undefined(非构造场景区分)。

本题考察抽象基类的运行时实现。核心是"new.target 指向实际构造器 + 构造期守卫"的机制与 TS abstract 的编译期/运行期互补,调用期抽象方法检查是补充;能给出模板方法模式的示例即展示设计能力。

#
★★

73. 手写 instanceof、new 操作符、Object.create 的模拟实现?

手写 instanceof、new 操作符与 Object.create 的模拟实现是什么?

  • 三者的底层算法
  • 边界处理(原型、null、函数)
  • 与原生 API 的差异

模拟实现:Object.create——Object.create(proto, descriptors) 创建以 proto 为原型的新对象:实现用临时构造函数 F,F.prototype = proto,new F()(或 Object.setPrototypeOf({}, proto))——注意 proto 为 null 时创建无原型对象({} + setPrototypeOf(null) 或 { proto: null })、proto 必须对象或 null(否则 TypeError);手写 instanceof——沿原型链查找右值 prototype:let p = Object.getPrototypeOf(left); while (p) { if (p === right.prototype) return true; p = Object.getPrototypeOf(p); } return false(right 非函数先抛 TypeError;需先调用 right[Symbol.hasInstance] 的优先语义可选);手写 new——创建对象(Object.create(Ctor.prototype))、调用 Ctor.apply(obj, args)(this 绑定)、返回值判断(对象/函数则返回该值,原始值/undefined 返回 obj)(可用 Reflect.construct 验证语义一致)。边界:三者组合(new 内用 Object.create 建原型、instanceof 验证 new 结果)、构造函数返回对象时 instanceof 的变化(返回对象非本类原型链时 false)、Object.create(null) 对象无 hasOwnProperty(配合 Object.hasOwn)、Reflect.construct 的 newTarget 差异;工程上模拟实现用于"理解算法 + 低版本环境 polyfill",现代环境直接用原生(正确性与性能更优)。

本题考察三个核心算法的模拟。核心是"Object.create 临时构造器、instanceof 原型链遍历、new 的返回对象覆盖"的算法骨架与相互配合,null 原型与返回值判断是易错点;能说明模拟与原生差异即展示细节掌握。

#
★★

74. 实现并发限制的请求调度器(p-limit),Promise 池的核心逻辑?

实现并发限制的请求调度器(p-limit)的核心逻辑是什么?

  • 并发计数与任务队列
  • 信号量式调度
  • 边界(错误隔离、取消、动态并发)

p-limit 式并发调度器核心:并发池——维护"运行中任务数"(activeCount)与"等待队列"(queue 数组);enqueue(fn) 包装任务:activeCount < limit 时直接执行(activeCount++,完成后 activeCount-- 并出队下一个),否则把"启动器"(执行 fn 的闭包)推入队列,等待"执行完成回调"触发时取出下一个启动器执行——本质是信号量(许可证)模型:每个任务占用一个许可证,完成后释放给队首。实现要点:启动器返回 Promise 链(enqueue 返回的对外 Promise 与内部执行 Promise 关联——resolve/reject 转发);任务完成后从队列取下一项(队列 FIFO);错误隔离——单个任务拒绝不影响调度器(队列继续推进),对外 Promise 各自拒绝;边界处理:limit 为 0/非正数(恒等待或立即拒绝)、空队列的稳定状态、动态调整并发(limit 可变)、取消(AbortSignal 支持——等待中的任务被取消时从队列移除且不触发调度)、任务函数同步抛错(包装为拒绝);工程应用:批量接口请求限流(防 429/连接耗尽)、文件分块上传的并发控制、任务池(爬虫/重试队列);与 Promise.all 的区别——all 一次性全并发,调度器动态限制同时运行数;复杂度 O(n) 队列操作,大任务量用链表优化。

本题考察并发调度的核心算法。核心是"信号量计数 + FIFO 队列 + 完成回调驱动出队"的闭环,错误隔离与取消是工程边界;能画出调度状态机即展示扎实掌握。

#
★★

75. 手写数组扁平化(flat)的递归与迭代(栈)两种实现?

手写数组扁平化(flat)的递归与迭代(栈)两种实现是什么?

  • 递归的深度遍历
  • 栈迭代避免调用栈溢出
  • 深度参数与边界

递归实现:function flat(arr, depth=1) { return depth > 0 ? arr.reduce((acc, v) => acc.concat(Array.isArray(v) ? flat(v, depth-1) : [v]), []) : arr.slice(); }——逐层展开嵌套数组,depth 控制深度,reduce + concat 累积结果;优点直观,缺点深嵌套(> 数千层)时递归调用栈溢出(RangeError: Maximum call stack size exceeded)。迭代(栈)实现:用显式栈模拟递归——把"待处理的元素 + 剩余深度"压栈(或记录层级),弹出处理:非数组或 depth 用尽直接入结果、数组则把其元素按逆序压栈(保持顺序):function flatIt(arr, depth=1) { const stack = arr.map(v => [v, depth]); const out = []; while (stack.length) { const [v, d] = stack.pop(); if (Array.isArray(v) && d > 0) { for (let i = v.length-1; i >= 0; i--) stack.push([v[i], d-1]); } else out.push(v); } return out; }——无调用栈风险,任意深度安全;边界:深度参数(默认 1、Infinity 全展开)、稀疏数组(空洞处理——flat 会跳过空洞,模拟实现需处理 undefined 语义差异,或用展开判断)、循环引用数组(递归版本无限递归,需访问集合检测)、类数组元素(Array.isArray 只认真数组——flat 规范对类数组不展开)。工程上:通用小数据用原生 flat(引擎实现最优),超深嵌套/兼容场景用手写栈版本。

本题考察扁平化的两种实现范式。核心是"递归的简洁 vs 栈迭代的栈安全"对比,深度参数与稀疏/循环边界是易错点;能给出栈实现并说明逆序压栈保持顺序即展示实现细节。

#
★★

76. WeakRef 与 FinalizationRegistry 的工程边界,弱引用缓存为何可能明明还在却被回收,unregister 时机与 GC 非确定性如何影响缓存与资源清理设计?

WeakRef 与 FinalizationRegistry 的工程边界是什么?弱引用缓存为何"明明还在却被回收"?unregister 时机与 GC 非确定性如何影响设计?

  • 弱引用缓存的命中不确定性
  • unregister 的时机要求
  • GC 非确定性下的设计原则

弱引用缓存(key 强、value 弱引用 + FinalizationRegistry 清理)的"明明还在却被回收":GC 的回收决定是引擎启发式的——内存压力、分代策略、引擎实现都影响回收时机;value 只被弱引用持有时,GC 在任意一次标记阶段都可能回收它(即使业务逻辑"刚用完"),deref() 返回 undefined 导致缓存未命中——弱引用不保证"存活到显式失效",只保证"不阻止回收";反之,对象也可能长期不被回收(内存充足时 GC 不积极),清理回调迟迟不触发——两端都是非确定性。unregister 时机:FinalizationRegistry.unregister(token) 必须在"对象仍存活"时调用才有意义(注册的持有令牌)——若在对象回收后才 unregister,清理回调可能已入队/已执行;设计上:资源不再需要时主动 unregister(防回调误删新对象——token 复用冲突)、清理回调执行后 token 自动作废(不需再 unregister)、在"对象仍存活"的代码路径(如 deref 成功时)执行 unregister 避免缓存条目被清理回调误删。设计原则:WeakRef/FinalizationRegistry 不能作为功能正确性依赖——缓存/资源管理用显式生命周期(容量、TTL、引用计数、显式 close);弱引用仅用于"尽力而为"的辅助(观测内存、冗余清理、避免意外强持有);关键资源(文件句柄、连接)必须显式释放,FinalizationRegistry 只是兜底;测试不能依赖 GC 时机(用 --expose-gc 手动触发或断言"最终"行为)。

本题考察弱引用机制的非确定性边界。核心是"弱引用只保证不阻止回收、不保证存活"与"unregister 需在存活期调用"的两个机制约束,显式生命周期是设计结论;能解释缓存命中的不确定性根因即展示本质理解。

#
★★

77. RegExp.escape(ES2025)在用户输入转义构造正则时的安全价值,与手写转义函数的差异及回退策略

RegExp.escape(ES2025)在用户输入转义构造正则时有怎样的安全价值?与手写转义函数有何差异及回退策略?

  • 正则特殊字符的转义需求
  • RegExp.escape 的规范语义
  • 手写转义的风险与回退

用用户输入动态构造正则(搜索高亮、过滤器、模板校验)时,未转义的特殊字符(. * + ? ( ) [ ] { } | ^ $ \ 等)会改变模式语义,造成错误匹配甚至 ReDoS(输入含量词组合);RegExp.escape(str)(ES2025)是标准转义函数:返回把字符串中"语法字符"转义为字面量的字符串(new RegExp(RegExp.escape(input)) 安全匹配输入本身),规范精确界定转义集合(含全部语法字符与部分上下文敏感字符),保证模式语义与输入一致。与手写转义差异:手写函数(/[.*+?^${}()|[]\]/g 替换)容易出现——集合遗漏(新语法字符、v flag 的集合运算字符)、过度转义(改变语义)、上下文错误(字符类内外转义需求不同)、字符范围错误(如 - 在类内位置);规范版按标准集合与语义处理,且随语法演进维护。回退策略:环境不支持 RegExp.escape 时——polyfill(core-js 提供规范实现)或先手写并按其规范集合实现(对照 spec 转义表),同时特性检测(typeof RegExp.escape === "function")决定路径;工程实践:用户输入的搜索词统一经 escape 后构造,配合"输入长度/复杂度限制"防 ReDoS、正则构造缓存(同一输入避免重复构造)。

本题考察正则转义的安全与标准化。核心是"未转义输入的语义破坏风险 + 规范转义集合 vs 手写遗漏风险"的对比,回退策略是工程落点;能说明 v flag 下转义集合变化即展示细节。

#
★★

78. Set.prototype 集合运算(union/intersection/difference/symmetricDifference/isSubsetOf/isSupersetOf/isDisjointFrom)在权限集合、标签筛选与去重场景的工程价值,与手写去重/交差补的对比

Set 的集合运算方法(union/intersection/difference 等)在权限集合、标签筛选与去重场景有何工程价值?与手写实现有何对比?

  • 7 个集合运算的语义
  • 权限/标签/去重场景
  • 与手写循环的性能与简洁对比

ES2025 Set 方法:union(并集)、intersection(交集)、difference(差集)、symmetricDifference(对称差)、isSubsetOf/isSupersetOf/isDisjointFrom(关系判断)——语义按数学集合定义,结果返回新 Set,链式可组合。工程价值:权限集合——用户权限与角色权限的并集/交集(any/all 授权判断:交集非空即有权)、角色继承(差集计算增量权限)、权限校验(isSubsetOf:所需权限是否都在已有权限内);标签筛选——标签集合并集(任一标签命中)、交集(全部标签命中——筛选器)、差集(排除标签)、isDisjointFrom(互斥标签校验);去重对比——两集合差异(symmetricDifference 找仅一侧存在的元素——同步/对账场景)、isSubsetOf 判断"是否全包含"(配置校验)。与手写对比:手写(Set + for...of 循环展开、filter/Some 组合)容易出错(结果新集合与原地修改混淆、方向记反、漏处理空集),且需手动处理"选择哪个集合作为迭代基准"(规范要求取较小集合迭代提升性能——方法内部按 size 优化迭代方向);原生方法性能优(引擎优化)且语义精确(定义在规范)。边界:方法均要求参数为 Set(或带 size 的可迭代?规范要求 Set 或 Set-like)、不支持多集合链式传参(需嵌套)、结果集合元素为引用共享(不深拷贝)。

本题考察集合运算的工程语义。核心是"7 方法语义映射到权限/标签/去重场景 + 与手写实现的正确性与性能对比",迭代方向优化是规范细节;能给出权限判断的组合示例即展示应用能力。

#

79. TC39 提案治理中 champion 与 reviewer 的角色以及 Stage advance 在企业内 SDK API 设计的参考价值

TC39 提案治理中 champion 与 reviewer 的角色是什么?Stage advance 流程对企业内 SDK API 设计有何参考价值?

  • champion 与 reviewer 的职责
  • 阶段化推进的门槛
  • 企业 API 治理的映射

TC39 治理角色:champion(提案负责人)——负责提案的推进:撰写规范文本、组织讨论、收集实现反馈、答辩与争取共识,是"提案的产品经理 + 技术作者";reviewer(评审者)——独立审查规范文本的语义正确性、与现有语言的一致性、实现可行性,发现设计缺陷,防止"仓促定稿";委员会以共识推进,各阶段有明确交付物门槛(Stage 1 问题陈述、Stage 2 规范草稿、Stage 3 实现 + 测试、Stage 4 双实现 + 纳入标准),不合格打回或搁置。企业内 SDK API 设计的参考价值:责任分工——每个 API 提案指定"负责人 + 独立评审"(避免作者自审偏见),评审检查语义一致性、向后兼容、边界完整;阶段化发布——API 分阶段(设计评审 → 内部试用(canary)→ 灰度 → 全量)且每阶段有准入门槛(类型定义、测试覆盖、文档、迁移指南),新 API 先"试验性"标记再稳定;变更治理——破坏性变更需要显式的弃用周期(deprecation → migration → removal)、语义冻结前接受反馈(冻结后只修 bug)、回退机制(设计错误及时搁置而非强推);文档与测试即规范(test 套件作为验收标准)。文化价值:共识而非独裁、反对意见显性化(champion 回应)、迭代节奏稳定(不 rush)。

本题考察标准治理向工程治理的迁移。核心是"champion/reviewer 分工 + 阶段门槛 + 弃用与回退机制"的模型映射到 SDK 设计流程,评审独立性与共识文化是价值点;能给出内部 API 的分级流程即展示治理设计能力。

#

80. String.prototype.normalize() 在 Unicode 等价性比较(NFC/NFD/NFKC/NFKD)中的文本去重应用

String.prototype.normalize() 在 Unicode 等价性比较(NFC/NFD/NFKC/NFKD)中有哪些文本去重应用?

  • 规范等价与兼容等价
  • 四种规范化形式
  • 去重与搜索的实践

Unicode 存在"等价序列":同一字符可由不同码点序列表示(é 可为 U+00E9 单码点或 e + U+0301 组合序列;全角/半角、兼容字形的兼容等价);normalize(form) 把字符串规范化为四种形式之一:NFC(规范组合——组合序列合并为单码点)、NFD(规范分解——单码点拆为基字符+组合符)、NFKC/NFKD(在规范基础上再应用兼容分解,如全角→半角、连字拆分)。文本去重应用:等价性归一——用户输入(搜索词、用户名、标签)先 normalize("NFC")(或 NFKC 兼容归一)再存储/比较,使"语义相同的不同写法"(é 两种写法、全角半角)命中同一键——去重、搜索、索引(数据库按归一化字段建唯一索引);规范比较——比较前统一形式(NFC 作为标准:比较双方 NFC 后 === 等价于 Unicode 规范化等价);NFKC 常用于"宽松匹配"(兼容字形、大小写无关场景,但注意兼容分解会改变信息(字形区分丢失)——严格去重用 NFC,宽松搜索用 NFKC);边界:normalize 只处理规范等价的组合序列(组合标记)、分解后的码点顺序(先基字符后组合符)、NFKC 不改变大小写(需另配合 toLowerCase)、性能(长文本 normalize 有开销,可只在键生成时做)。

本题考察 Unicode 规范化的工程应用。核心是"规范等价 vs 兼容等价 + 四种形式的选择(严格 NFC / 宽松 NFKC)"与键生成时归一化的实践,兼容分解的信息丢失是边界;能给出搜索索引的归一化方案即展示国际化功底。

#

81. 模板字符串(Tagged Template)在 SQL/HTML 转义与 DSL 构建中的工程实践

标签模板(Tagged Template)在 SQL/HTML 转义与 DSL 构建中有哪些工程实践?

  • 标签函数的静态片段与插值分离
  • 自动转义的实现
  • DSL 与预处理器的构建

标签模板(tag...${...}...)把模板拆为"静态字符串数组(strings,含 cooked/raw)"与"插值数组(values)"传给标签函数——静态片段与动态值分离,使标签函数能按上下文处理插值。SQL 转义:sqlSELECT * FROM users WHERE id = ${id} 中标签函数把 values 中的值转义/参数化(字符串值加引号并转义、数字校验、对象映射为列白名单),构造安全的 SQL 语句——静态结构由开发者控制(不可注入),动态值经标签统一消毒(SQL 注入防护的"默认安全"写法,如 sql-template-strings、pg 的 tagged 接口)。HTML 转义:html<div>${userInput}</div> 标签自动转义插值(& < > " ' 实体化),同时允许"信任的 HTML"(标签包裹的 SafeHtml 对象不二次转义)——XSS 防护的结构化模板(lit-html/innerself 等基于标签模板的渲染器);DSL 构建:标签模板作为"源码块"——编译器/预处理器(CSS-in-JS 的 css...、GraphQL 的 gql...、正则的 re...)接收原始源码(strings.raw 保留反斜杠语义)做解析、校验、缓存与类型推导。工程要点:strings.raw 与 cooked 的区别(转义序列处理)、标签函数返回值的约定(SafeHtml/防二次转义标记)、插值位置的静态验证(SQL 中插值只能出现在值位置——DSL 设计约束)、性能(静态片段缓存(模板无插值变化时可缓存解析结果))。

本题考察标签模板的工程化价值。核心是"静态/动态分离 → 上下文转义与 DSL 处理"的机制,SQL/HTML 的默认安全模式与 SafeHtml 标记是落点;能说明 strings.raw 的用途即展示细节。

#

82. 手写扁平化(flat)与数组去重的多种实现(Set/reduce/filter indexOf)的性能对比

手写扁平化与数组去重的多种实现(Set/reduce/filter indexOf)性能如何对比?

  • 去重的四种实现
  • 时间复杂度与空间
  • 大数据量的选型

数组去重的经典实现:Set——[...new Set(arr)](或 Array.from(new Set(arr))):哈希去重,O(n) 时间、O(n) 空间,现代引擎对 Set 迭代有优化,通用首选;reduce + includes——acc.includes(v) ? acc : [...acc, v]:O(n²)(includes 线性扫描),小数组可接受,大数组明显退化;filter + indexOf——arr.filter((v, i) => arr.indexOf(v) === i):O(n²)(indexOf 全扫),语义上保留"首次出现位置",同样大数组退化;Map 变体——Map 键去重 O(n)(保序 + 任意键类型,与 Set 相近,需区分 key 语义时用)。性能对比结论:数据量大(万级以上)优先 Set(或 Map),n 小(百级)差距不显著,可读性优先;注意:Set 去重是 SameValueZero 语义(NaN 可去重、+0/-0 视为同一),indexOf 是 === 语义(NaN 不去重)——语义差异影响正确性;对象去重(引用 vs 序列化)按需求选择(引用用 Set、内容用序列化键 + Map)。扁平化性能对比:递归 reduce+concat(每层创建数组、concat 拷贝)慢于"迭代栈 + push"(原地 push 结果数组、无中间数组),原生 flat 最优(引擎 C++ 实现);大数据量嵌套深时用原生或栈实现,小数据递归直观即可。工程实践:默认原生 API(Set/flat),自定义实现前先确认瓶颈(去重/扁平化很少是性能瓶颈,优先可读性)。

本题考察算法实现的性能权衡。核心是"Set O(n) vs indexOf O(n²)"的数量级对比与 SameValueZero/=== 语义差异,选型以数据规模为依据;能说明语义差异影响正确性即展示细致。

#

83. 手写 curry 与 partial,占位符(hole)支持与 length 属性保留的边界取舍

手写 curry 与 partial 时,占位符(hole)支持与 length 属性保留的边界取舍是什么?

  • 柯里化与偏函数的分工
  • 占位符的跳过语义
  • length 属性的保留策略

curry 把多参函数转为"一次一参"链(f(a)(b)(c) 直至参数齐);partial 一次固定任意位置参数(partial(f, a)(b))——手写实现的核心是"参数累积 + 收集齐后调用"。占位符(hole)支持:用约定符号(如 _)表示"该位置参数稍后提供":curry/partial 实现中区分占位符——把占位符位置保留(记录占位符索引),后续调用时按"从左到右填充首个占位符"的规则填入实际参数(实现需维护"已占位参数列表"并在填充后收缩);边界:占位符与实参顺序(多次调用交替填充)、占位符数量与填充规则(经典 lodash curry 的占位语义)、默认值参数与占位符的冲突(缺省参数会被跳过——占位符不触发默认值,需注意)。length 属性保留:原函数的 length(形参个数,不含默认值/rest)在柯里化后语义改变——完全保留(返回函数的 length = 原 length)会误导调用方(实际接收一个参数即可),常见取舍:返回函数的 length 设为"剩余参数数"(curry(f, 3) 后第一级 length=1)、或约定不依赖 length(文档化);实现"length 保留"需用动态函数(Function constructor)或接受不完美(普通闭包 length=0——多数实现接受);工程上:库(lodash/fp、ramda)提供成熟实现(占位符 + 变参 + length 策略),业务手写时优先简单版本(无占位符)并文档化约束,占位符需求明确时引入成熟库。

本题考察函数式工具的实现边界。核心是"占位符的填充规则 + length 语义取舍"两大难点,成熟库 vs 手写的选择是工程结论;能说明占位符填充的从左到右规则即展示实现细节。

#

84. Error.stackTraceLimit 在深层异步调用链栈截断中的作用与取值取舍

Error.stackTraceLimit 在深层异步调用链栈截断中起什么作用?取值如何取舍?

  • stackTraceLimit 的帧数控制
  • 异步栈的帧累积
  • 取值权衡(信息 vs 开销)

Error.stackTraceLimit(V8 专属)控制"捕获的栈帧最大数量"(默认 10):new Error()/抛出时按该值截断栈(超过部分不记录),影响 Error.stack 的长度与生成开销。深层异步调用链场景:async/await 链每层增加帧(异步栈追踪记录 async 帧),深层调用(数十层嵌套)默认 10 帧只保留尾部(丢失根因帧,排查困难)——适当调大(如 50-100)可保留完整链路,但代价:栈捕获耗时与内存随帧数增长(每次 throw/构造的开销、stack 字符串大小)、上报体积膨胀(监控 SDK 的 payload 限制);取值取舍:开发环境调大(完整链路定位)、生产环境按需(如错误监控场景 50-100,配合采样与截断——上报前按"帧数上限 + 字节上限"双重截断、忽略框架帧);注意:stackTraceLimit 只影响新捕获的 Error(已存在实例的 stack 不变)、设 0 可禁用栈捕获(性能场景:高频率 throw 不需要栈时)、跨环境(非 V8 引擎不支持——特性检测)、异步帧的"可读性"开销(每个 async 帧的 Promise 关联记录)。工程实践:错误监控 SDK 在上报时结合 stackTraceLimit(设高 + 服务端截断)或保持默认并聚合(指纹基于截断后栈——需归一化保证一致性);性能敏感路径(高频失败重试)评估禁用或限制栈捕获。

本题考察栈捕获的调优参数。核心是"帧数上限控制捕获成本与信息量"的权衡模型,开发/生产差异与上报截断是落点;能说明指纹归一化与截断的配合即展示监控工程思维。

#

85. 错误监控 SDK 的采样、去重(fingerprint/堆栈归一化)与面包屑(breadcrumb)设计

错误监控 SDK 的采样、去重(fingerprint/堆栈归一化)与面包屑(breadcrumb)如何设计?

  • 采样策略与成本控制
  • 指纹聚合与堆栈归一化
  • 面包屑的上下文记录

错误监控 SDK 三块设计:采样——按比例/按规则抽样(随机采样 10%、错误类型定向全采(致命错误全量)、用户分层采样(新版本全采、旧版本降采样)),控制上报量与成本(QPS 配额、突发保护——相同指纹的错误限流上报),采样在 SDK 内完成(本地去重后再发)。去重与指纹——fingerprint 是"错误分组的稳定标识":对堆栈做归一化(去掉行号/列号/函数偏移(minified 内容差异)、忽略框架内部帧、规范化 module 路径、函数名去 minify 后缀)后按"错误类型 + 归一化栈 + 消息模板"哈希——同一 bug 的多次出现聚合为一条(错误列表按 fingerprint 分组、计数展示);归一化注意:压缩代码的还原(source map 后指纹)、异步栈的一致性(async 帧)、变量值不参与指纹(相同栈不同值应合并)。面包屑(breadcrumb)——记录错误发生前的事件序列(用户操作、路由变化、网络请求(URL/状态码/耗时)、点击元素、控制台日志、存储变更),环形缓冲(默认 20-50 条,覆盖旧记录),错误上报时附加面包屑——为错误提供"前因上下文"(复现线索:用户做了什么导致崩溃),设计要点:低开销(同步记录、固定容量、字符串化限制)、隐私过滤(敏感字段脱敏)、优先级(关键事件保底不覆盖)。三者配合:面包屑提供上下文、指纹去重控量、采样控总成本。

本题考察监控 SDK 的整体设计。核心是"采样控成本 + 归一化指纹控噪音 + 面包屑控上下文"的三层模型,堆栈归一化的细节(行号剥离)与环形缓冲是工程要点;能给出指纹生成的完整流程即展示可观测性设计。

#

86. 静态成员的继承与实例成员继承在属性查找路径上的差异

静态成员与实例成员的继承在属性查找路径上有何差异?

  • 实例成员的原型链查找
  • 静态成员的构造函数链查找
  • 继承的双链结构

实例成员(实例属性与方法)继承沿"实例 → 类 prototype → 父类 prototype → … → Object.prototype"的实例原型链查找:instance.method 在自身找不到时沿链找父类 prototype。静态成员(static 属性/方法)挂在"类构造函数"上,继承沿"子类 → 父类 → Function.prototype → Object.prototype"的构造函数链查找:Derived.staticMethod 在 Derived 自身找不到时找其原型——而"类的原型"是父类(class Derived extends Base 时 Derived.proto === Base),因此静态继承通过"构造函数链"(与实例原型链平行)实现:Derived 可访问 Base 的静态成员,但 Derived 的实例不能直接访问静态成员(instance.staticMethod 不存在——查找路径不同)。差异要点:两条链独立——实例链(prototype 链)与静态链(proto 链)平行不交叉;静态成员不参与实例的属性查找(实例无法经原型链读到类静态属性)、Object 的静态方法(Object.keys)不继承给子类实例;静态链也终止于 Function.prototype(Derived.constructor 等来自 Function.prototype 附近)、类表达式继承的静态绑定(extends 表达式非类时静态链更复杂——extends null/函数时差异)。工程影响:静态工厂方法(Derived.create())经静态链调用父类实现(用 this 区分当前类——new this() 返回当前类实例)、静态成员的重写(Derived 定义同名 static 遮蔽 Base)、实例与静态同名成员的隔离;调试时区分"instance.method 找不到"与"Derived.method 找不到"的查找路径。

本题考察继承双链的结构差异。核心是"实例链(prototype)与静态链(proto)平行且不交叉"的模型,静态工厂与遮蔽是工程落点;能画出双链图即展示系统性理解。

#

87. ES class 与“组合优于继承”理念的取舍,何时应放弃继承改用组合

ES class 与"组合优于继承"理念的取舍是什么?何时应放弃继承改用组合?

  • 继承的优势与代价
  • 组合的模式(委托、注入)
  • 决策标准

继承(extends)的价值:代码复用(is-a 关系)、多态(子类覆写、接口统一)、与框架契约(生命周期钩子);代价:紧耦合(子类依赖父类实现细节——脆弱的基类问题)、深层链的僵化(改父类影响全部子类、方法冲突仲裁难)、类型膨胀(mixin/钻石问题)、测试困难(需构造父类上下文)。组合(composition)模式:委托(对象持有依赖对象、转发调用,行为可替换)、依赖注入(构造/方法传入能力——策略/适配器)、函数式组合(高阶函数包装)。决策标准:关系是"is-a"(子类确实可替换父类、共享"类型语义")时用继承(且深度 ≤ 2-3、父类稳定);关系是"has-a / 使用"(复用能力而非类型身份)、父类易变、需要运行时替换行为、跨层级横切能力(日志/缓存/事件化)时用组合;"继承用于多态、组合用于复用"是经验法则——若继承只为拿方法(不重写、无多态),应改为组合(组合优于继承的典型场景);工程信号:改父类导致无关子类出错(脆基类)、子类覆写大量方法只为绕过父类(继承不当)、需要同时复用多个类的能力(mixin 复杂化 → 组合)。ES class 提供两者:class 继承 + 委托组合(class Service { constructor(dep) {...} })都是一等公民,按决策标准选择而非教条。

本题考察继承与组合的决策模型。核心是"is-a 用继承、has-a/复用用组合"的判断标准与继承的脆弱信号,多态与复用的分工是落点;能识别"只为复用方法而继承"的反模式即展示工程判断。

#

88. 手写 compose/pipe 函数组合,执行顺序、单参约束、错误传播与 TypeScript 类型推导(Variadic Tuple Types)如何实现?

手写 compose/pipe 时,执行顺序、单参约束、错误传播与 TypeScript 类型推导(Variadic Tuple Types)如何实现?

  • 执行顺序与 reduce 实现
  • 单参约束与多参适配
  • 类型推导的变长元组

实现:pipe 用 reduce(从左到右):const pipe = (...fns) => (arg) => fns.reduce((acc, fn) => fn(acc), arg);compose 用 reduceRight(或先 reverse):const compose = (...fns) => (arg) => fns.reduceRight((acc, fn) => fn(acc), arg)。单参约束:组合要求每个函数单参(前一个的输出是后一个的输入)——多参函数需先柯里化/偏函数适配(f(a,b) → a => f(a, b) 包装);首函数可多参(初始参数直接传入:(...args) => fns.reduce((acc, fn, i) => i === 0 ? fns0 : fn(acc))——pipe 首函数多参是常见扩展)。错误传播:组合内任一函数抛错(同步)直接向外传播(reduce 中断,后续函数不执行)——错误处理在调用点 try/catch 或把错误处理函数加入管道(管道内错误分支);异步组合(async 函数)返回 Promise 链(await 传播,需管道版本 await 化——pipeAsync:await 每个中间值)。TypeScript 类型推导:用 Variadic Tuple Types(ES2020 TS 4.0 的 [...T])表达"任意数量的函数,且相邻函数输入输出类型链式匹配":type Pipe = <A, F extends any[], R>(arg: A, ...fns: [...F]) => ... 精确推导需要递归类型/函数重载(经典实现用递归 conditional types 或链式调用类型:pipe(f1)(f2)... 的柯里化类型更易推导);简化方案:重载若干固定长度签名(2-8 参)+ 泛型 fallback(any 链),工程常用(lodash fp/ramda 的类型)——推导精度与复杂度取舍。

本题考察组合的工程实现与类型系统。核心是"reduce/reduceRight 顺序 + 单参约束适配 + 同步抛错传播"与"变长元组类型推导的复杂度取舍",重载简化是务实落点;能说明首函数多参的扩展即展示细节。

#

89. Atomics.pause(ES2025)在自旋等待(spin-wait)时的节能提示与编程模式,与 Atomics.wait/notify 的区分

Atomics.pause(ES2025)在自旋等待时的节能提示与编程模式是什么?与 Atomics.wait/notify 有何区分?

  • 自旋等待的忙等问题
  • pause 的 CPU 让步提示
  • 与阻塞等待的分工

Atomics.pause()(ES2025)是"自旋等待(spin-wait)时的节能提示":在循环读取共享内存(如 while (Atomics.load(sab, i) !== expected) Atomics.pause();)的忙等循环中调用,向 CPU 提示"当前是短暂自旋,可降低指令执行强度/散热/能耗"(x86 pause 指令、ARM yield 的语义,同构于其他语言的自旋提示),缓解忙等的功耗与资源竞争(超线程场景提升整体吞吐);返回 void、无阻塞语义、可带可选毫秒参数(规范细节:参数用于未来扩展/提示时长,当前实现忽略)。编程模式:短临界区/极短等待的同步(共享内存的生产者标记、锁的自旋阶段——先自旋片刻再考虑阻塞)、配合"自旋阈值"策略(自旋 N 次后切换 wait/notify 或让出线程)。与 Atomics.wait/notify 的区分:wait 是"阻塞等待"(线程挂起、由 notify 唤醒,功耗低但切换开销大);pause 是"不阻塞的自旋提示"(线程仍在运行,仅降低功耗)——分工:预期等待极短(ns-μs 级)用自旋 + pause(避免线程切换开销),预期等待较长(ms 级或不确定)用 wait/notify(避免浪费 CPU);pause 不能替代 wait 的同步语义(不保证原子性、无唤醒机制);主线程场景:wait 不可用(主线程只能 waitAsync),自旋 + pause 在主线程会阻塞事件循环——共享同步逻辑应放 Worker。工程上:共享内存同步的首选模式是 wait/notify(或信号量封装),pause 仅用于精心设计的短自旋(如锁的乐观阶段),需结合基准验证。

本题考察自旋同步的节能机制。核心是"pause 的 CPU 让步提示 vs wait 的阻塞挂起"的语义分工与"短等待自旋、长等待阻塞"的模式选择,主线程限制是边界;能给出自旋阈值策略即展示并发设计经验。

#

90. String.prototype.isWellFormed/toWellFormed(ES2024)在外部文本、URL 与查询参数中孤立代理项(lone surrogate)清洗与编码安全的工程价值

String.prototype.isWellFormed/toWellFormed(ES2024)在外部文本、URL 与查询参数中如何应对孤立代理项(lone surrogate)?有何工程价值?

  • 孤立代理项的成因与风险
  • isWellFormed 的检测语义
  • toWellFormed 的清洗(替换 U+FFFD)

孤立代理项(lone surrogate)是"不成对的 UTF-16 代理码元"(单独的高位/低位代理项),成因:外部数据(用户输入、第三方 API、损坏的字节流转字符串、二进制中按码元截断)——JS 字符串本身允许孤立代理项存在(码元合法、但非 Unicode 标量值)。风险:编码安全——孤立代理项经 TextEncoder(UTF-8)编码会变为 U+FFFD 替换符(数据损坏静默)、传给 URL/encodeURIComponent 时抛 URIError(encodeURI 系对孤立代理项抛错)、写入存储/协议(JSON 传输、DB 字符串)可能被替换或拒绝;展示问题(渲染为乱码方块)。isWellFormed() 检测字符串是否包含孤立代理项(返回布尔,无遍历手写),toWellFormed() 返回"把每个孤立代理项替换为 U+FFFD"的新字符串(不修改原串)。工程价值:外部文本入口清洗——用户输入、文件读取、WebSocket 消息先 toWellFormed() 归一(防下游编码异常);URL/查询参数安全——构造 query/URL 前清洗或检测(encodeURIComponent 前 isWellFormed 校验,非法输入拒绝或替换,防 URIError 崩溃与异常行为);存储一致性——数据库/日志写入前归一,保证"写入的内容可逆读回"(编码往返一致);注意:替换为 U+FFFD 是"有损"(原代理项信息丢弃,若需保留需转义方案)、toWellFormed 性能(长字符串遍历,仅入口做一次)、与 normalize 的配合(先 toWellFormed 再 normalize 避免 normalize 对孤立代理项的行为差异)。

本题考察 Unicode 健全性 API 的工程应用。核心是"孤立代理项的编码风险(URIError/替换符)+ 检测与清洗的分工 + 入口归一化"的模式,有损替换的取舍是细节;能给出 URL 构造前的校验流程即展示工程价值。