跳到正文
前端知识库
面试真题

JavaScript 语言边界与工程表达真题

整理 Symbol.iterator、箭头函数、柯里化、Map/Set、Vue/React 状态和不同责任范围的项目表达题。

5 分钟JavaScript · ES6 · Symbol.iterator · 箭头函数 · 柯里化 · Vue · React · 项目表达

JavaScript 语言边界与工程表达真题

1. Symbol.iterator 解决什么问题?

可迭代协议把“如何逐项取值”从具体数据结构中抽出来。一个对象实现 [Symbol.iterator]() 后,for...of、展开运算符、解构和 Array.from 就可以消费它;该方法必须返回带 next() 的迭代器,next() 返回 { value, done }

const range = {
  start: 1,
  end: 3,
  *[Symbol.iterator]() {
    for (let value = this.start; value <= this.end; value += 1) {
      yield value
    }
  },
}

console.log([...range]) // [1, 2, 3]

return() 是可选的提前终止钩子,生成器会在 break 或异常离开时有机会清理资源。数组、字符串、Map、Set 和生成器是常见内置可迭代对象;普通对象默认不可迭代,不能因为“有 length”就直接展开。深入阅读:Iterator 与 GeneratorES6 真题补充

2. 箭头函数的 this 有哪些边界?

箭头函数没有自己的 thisargumentsprototype,不能用 new,也不能通过 call/apply/bind 改变 this;它捕获定义位置的词法 this。因此它适合需要固定外层实例的回调,但不适合依赖调用者动态 this 的对象方法。

const counter = {
  value: 1,
  method() {
    setTimeout(() => console.log(this.value), 0) // 1
  },
  // arrow: () => this.value // 不会把 this 绑定到 counter
}
counter.method()

类字段箭头函数可以避免手动 bind,但每个实例都会创建一个函数,实例很多时要评估内存成本。回答 call 题时先说“词法绑定不可改”,再说明普通函数的 this 取决于调用形式;不要把箭头函数和普通函数的规则混在一起。

3. 柯里化、偏函数和组合如何区分?

  • 柯里化:把 f(a, b, c) 变成 f(a)(b)(c),直到参数数目达到目标才执行。
  • 偏函数:预先固定部分参数,剩余参数一次传入,不要求每次只传一个。
  • 组合/管道:把多个函数连接成一个函数,pipe 通常从左到右传递结果,compose 常从右到左。
const curry = (fn) => {
  const collect = (args) => (...next) => {
    const all = [...args, ...next]
    return all.length >= fn.length ? fn(...all) : collect(all)
  }
  return collect([])
}

const add = curry((a, b, c) => a + b + c)
add(1)(2)(3) // 6

const pipe = (...fns) => (value) => fns.reduce((current, fn) => fn(current), value)
pipe((x) => x + 1, (x) => x * 2)(3) // 8

通用实现还要决定:一次传多个参数、fn.length 不准确、占位符、this、异步返回值和错误传播。柯里化会增加闭包和调用层级,热路径中应以 profile 证明收益,不要为了“函数式”牺牲可读性。深入阅读:函数柯里化与组合

4. Map、WeakMap、Set、WeakSet 怎么选?

类型 键/值限制 可遍历/size 常见用途
Map 键可以是任意值 需要对象身份、插入顺序、计数或遍历
WeakMap 键必须是对象,弱引用 DOM/实例元数据,避免缓存阻止回收
Set 值唯一,按 SameValueZero 判断 去重、成员判断、集合运算
WeakSet 只能存对象,弱引用 标记对象是否处理过

弱引用集合不可枚举,代码不能依赖垃圾回收的具体时间。需要统计数量、导出全部键或稳定迭代时不能用 WeakMap/WeakSet。Set 去重只保证按规范的相等规则,不会深度比较两个对象;对象列表要按业务 ID 或序列化策略去重。深入阅读:ES6 真题补充

5. Vue/React 状态题怎么答?

5.1 Vue refreactivenextTick

ref 通过 .value 包装值,适合基本类型或需要替换整个引用;reactive 代理对象,解构时可能丢失响应式,需用 toRefs 或保留对象引用。两者都不等于“任何异步更新都立即反映到 DOM”。

nextTick 应用于“状态已更新、需要读取下一次 DOM”的场景;它通常把回调安排到当前更新批次之后。不要用它修复数据竞态,也不要把“微任务一定先于所有渲染”当成跨浏览器绝对保证。

const state = reactive({ count: 0 })
state.count += 1
await nextTick()
// 此处读取 DOM 才能观察到本轮更新后的结果

5.2 React useState 是同步还是异步?

setState/setCount 是请求一次更新,React 会按事件边界和优先级批量处理;调用后当前 render 中的变量不会立刻改变。函数式更新能基于队列中的最新值,副作用应放在 useEffect 或事件处理的明确阶段。

setCount((previous) => previous + 1)
setCount((previous) => previous + 1) // 最终增加 2

不要简单回答“React setState 异步”。在 React 18+,批量范围比旧版本更广,但 flushSync、外部 store、并发渲染和浏览器事件边界会影响观察结果。深入阅读:React 真题补充Vue 真题补充

6. 性能和项目题如何从短答升级?

资料中的“P6/P7/P8”可作为责任范围的讨论框架,不是任何公司的统一标准。回答同一个技术问题时,按自己能举证的范围逐层增加内容:

范围 先说明 进一步证据
模块交付 需求边界、实现、测试和线上排障 PR、回归用例、错误/性能记录
跨模块/跨团队 约束、选型、接口契约、迁移和协作 ADR、契约测试、灰度与回滚记录
系统/组织影响 长期演进、容量、风险、投入产出和机制 版本趋势、事故复盘、治理指标和责任边界

例如被问“为什么用虚拟列表”:

  1. 先给模块结论:节点数超过可接受预算,只渲染可视窗口。
  2. 解释机制:占位高度、滚动测量、key、动态高度和键盘焦点。
  3. 说明系统边界:服务端分页/游标、缓存、取消和权限;虚拟化不能减少传输量。
  4. 给验证:Performance 录制、INP/长任务、内存和业务完成率,对照真实设备与数据量。

项目表达模板:

背景与影响 -> 本人负责边界 -> 约束与替代方案
-> 关键机制 -> 失败/止血/回滚 -> 指标与证据 -> 复盘

项目案例可从 项目亮点总览 选择,再回链 项目面试追问清单。不要把团队结果冒充个人贡献,也不要把资料里的演示数字写成自己的上线数据。

7. 口述练习

  1. 自定义对象如何实现可迭代?提前 break 时怎样释放资源?
  2. 为什么箭头函数作为对象方法经常得到 undefined
  3. 柯里化和偏函数在请求封装中分别适合什么场景?
  4. 什么时候选 WeakMap,如何证明它没有造成内存泄漏?
  5. React 状态更新后为什么不能立即读取新值?Vue nextTick 又解决了什么?
  6. 一项性能优化怎样从“个人实现”讲到“跨团队治理”?

来源:高频真题解析与9月考点预测中.pdf高频真题解析与9月考点预测下.pdf。语言规则按 ECMAScript/React/Vue 当前边界整理,宣传性职级和收益描述已改写为可验证的责任与证据。