🔤 变量与类型
| 声明方式 | 要点 |
| let / const 块级作用域 |
只在块内可见;声明前不可访问,同一作用域禁止重复声明 |
| const 绑定不可变 |
引用绑定不可重指,对象内容仍可修改;需要完全不可变配合 Object.freeze |
| 暂时性死区 TDZ |
声明前访问抛 ReferenceError,连 typeof 也会抛 ⚠ 高频坑 |
| 类型判断 | 说明 |
| typeof |
适合值类型与函数;typeof null 返回 "object"、数组也返回 "object" 是局限 |
| instanceof |
沿原型链判断引用类型;跨 iframe / 跨 realm 场景失效 |
| Object.prototype.toString.call |
最通用准确,返回 [object Array] 等内部标签 兜底方案 |
| Array.isArray |
判断数组首选,跨环境安全 |
| 隐式转换坑 | 说明 |
| == 隐式转换 |
null == undefined 为 true、类型不同先转换再比;一律 === 免踩坑 ⚠ 规范 |
| NaN |
NaN !== NaN 恒成立;判断用 Number.isNaN |
| 数组相加 |
[1,2] + [3] 得 "1,23"——数组先转字符串再拼接;运算前显式转换 |
| 拷贝方式 | 实现要点 |
| 浅拷贝 |
Object.assign / 展开运算符,只复制一层,嵌套对象仍是共享引用 |
| JSON 序列化深拷贝 |
JSON.parse(JSON.stringify(obj)) 简单粗暴;丢 undefined / 函数,循环引用直接报错 |
| structuredClone / 手写递归 |
现代 API 直接克隆;手写时用 WeakMap 缓存处理循环引用,Date / RegExp / Map / Set 特判 面试高频 |
📚 数组与对象高频 API
| API | 要点 | 示例 |
| map |
映射为新数组,不改变原数组 |
arr.map(x => x * 2) |
| filter |
过滤返回新数组 |
arr.filter(x => x > 0) |
| reduce |
聚合为任意结构;务必传初始值,防空数组报错 |
arr.reduce((s, x) => s + x, 0) |
| find / findIndex |
找第一个匹配项,没有返回 undefined / -1 |
arr.find(x => x.id === 1) |
| some / every |
至少一个满足 / 全部满足,短路求值返回布尔 |
arr.some(x => x.done) |
| flat / flatMap |
扁平化默认一层;flatMap 等价 map + flat(1) |
arr.flat(Infinity) 全展开 |
| at |
支持负索引取值 |
arr.at(-1) 取最后一个 |
| 惯用法 | 要点 |
| 数组去重 |
[...new Set(arr)] 一行搞定;对象数组按 key 去重用 Map 缓存 |
| 数组分组 |
reduce 累加器按键归类;新 API Object.groupBy 注意兼容性 |
| sort 排序陷阱 |
默认按字符串比较,数字要传比较器 (a, b) => a - b ⚠ 高频坑 |
| 解构与展开 | 示例 |
| 解构默认值 |
const { name = 'anon', age = 0 } = obj |
| 重命名与嵌套 |
const { name: userName, addr: { city } } = obj |
| 展开合并 |
{ ...defaults, ...opts } 后者覆盖前者;注意是浅合并 |
⏳ 异步编程
| Promise 基础 | 要点 |
| 三态不可逆 |
pending → fulfilled / rejected,状态一旦改变不再回退 |
| 链式调用 |
then 返回新 Promise,返回值向下穿透;抛错沿链传递直到被 catch 接住 |
| 错误处理 |
链尾 catch 兜底;finally 不接收参数、不改变最终结果 |
| async / await | 要点 |
| 错误处理 |
await 抛错用 try / catch 捕获,或对单点用 .catch 链 |
| 串行 vs 并行 |
无依赖的连续 await 是串行浪费;先 Promise.all 打包再 await 性能要点 |
| 组合 API | 行为 | 典型场景 |
| Promise.all |
全部成功才成功,一个失败立即 reject |
强依赖:所有数据齐了才能渲染 |
| Promise.allSettled |
永远 fulfilled,返回 {status, value/reason} 数组 |
各请求独立处理、互不拖垮 |
| Promise.race |
第一个 settle(含失败)决定结果 |
接口超时控制经典用法 常用 |
| Promise.any |
第一个 fulfilled 才返回,全失败抛 AggregateError |
多源请求择最快成功者 |
| 控制思路 | 要点 |
| 并发数控制 |
维护任务队列 + 活跃计数,完成一个补一个(p-limit 思路),防万请求齐发打爆连接 |
| 顺序执行 |
reduce 串联 Promise 链,或 for await...of 逐个 await |
✨ ES6+ 语法糖
| 语法 | 要点 |
| 箭头函数 this |
定义时继承外层 this(词法作用域);不能作构造函数、无 arguments ⚠ 高频考点 |
| 模板字符串 |
反引号支持换行与 ${} 插值,替代加号拼接 |
| 可选链 ?. |
obj?.a?.b、fn?.():null / undefined 短路不报错 |
| 空值合并 ?? |
仅 null / undefined 取右侧;与 || 区别:0 和空串不会被吞 ⚠ 易混淆 |
| 参数解构默认值 |
function f({ a = 1 } = {}):不传参也不报错 |
| 展开与剩余 |
fn(...args) 展开;const [first, ...rest] = arr 收集剩余 |
| 生成器 |
function* + yield 惰性产出,可暂停恢复的迭代器,for...of 消费 |
| 类字段与私有 |
#field 真私有字段;static 静态成员,实例不可访问 |
🌳 DOM 与事件
| 事件流 | 要点 |
| 三阶段 |
捕获(外 → 内)→ 目标 → 冒泡(内 → 外) |
| addEventListener 第三参 |
true 在捕获阶段监听;默认 / false 在冒泡阶段 |
| stopPropagation vs preventDefault |
阻断传播与阻止默认行为(如链接跳转)是两件事,别混用 |
| 事件委托 | 要点 |
| 委托模板 |
父元素统一监听 + e.target.closest('.item') 找真实目标节点 |
| 委托价值 |
动态新增子元素免重复绑定;大批量列表只挂一个监听器省内存 必会 |
| 防抖与节流 | 机制 | 适用 |
| 防抖 debounce |
停止触发 delay 毫秒后只执行一次,重置计时器 |
搜索输入联想、窗口 resize 完成后计算 |
| 节流 throttle |
固定时间间隔内最多执行一次,锁存时间戳 / 定时器 |
scroll / mousemove / 按钮防连点 |
| 常用 API | 一句话 |
| closest / matches |
向上找最近匹配祖先元素 / 当前元素是否匹配选择器 |
| IntersectionObserver |
异步监听元素进入视口,懒加载与曝光埋点首选 常用 |
| MutationObserver |
监听 DOM 结构与属性变更,调试与第三方脚本场景 |
| requestAnimationFrame |
下一渲染帧回调,动画比 setTimeout 更平滑且后台自动暂停 |
📦 模块化与运行
| 维度 | ESM | CJS |
| 语法 |
import / export |
require / module.exports |
| 加载时机 |
编译期静态分析,可 tree-shaking |
运行时动态加载 |
| 导出语义 |
只读动态绑定(引用,随原模块变化) |
值拷贝(导出后与原值脱钩) |
| 顶层特性 |
this 为 undefined;支持顶层 await |
this 为 module.exports;模块同步加载 |
| 事件循环 | 要点 |
| 宏任务 |
setTimeout / setInterval、I/O、UI 渲染;每轮循环只取一个 |
| 微任务 |
Promise.then / queueMicrotask / MutationObserver;当前宏任务结束后清空整个微任务队列 ⚠ 面试必考 |
| 顺序规则 |
同步代码 → 清空微任务 → 一个宏任务 → 再清微任务,循环往复 |
事件循环:同步跑完清空微任务,再取一个宏任务,渲染按帧率插入,周而复始
| strict mode | 要点 |
| 开启方式 |
文件 / 函数首行 'use strict';ESM 与 class 内默认严格模式 |
| 行为变化 |
未声明变量赋值报错、普通调用 this 不再指向全局、禁 with 与八进制字面量 |
🧠 this 指向五规则
| 规则 | 要点 | 示例 |
| 默认绑定 |
独立函数调用:非严格模式指向 window,严格模式与 ESM 下为 undefined |
fn() |
| 隐式绑定 |
谁调用指谁;赋值转存会丢失(const f = obj.fn 后调用回退默认绑定) |
obj.fn() |
| 显式绑定 |
call / apply 立即调用并指定 this,bind 返回绑定了 this 的新函数 |
fn.call(ctx) |
| new 绑定 |
构造调用绑定到新实例;优先级 new > 显式 > 隐式 > 默认 |
new Fn() |
| ⚠ 箭头函数 |
定义时继承外层 this(词法作用域),call / apply / bind 均改不了 |
() => this |
🧬 手写深拷贝要点
简版递归思路:原始值直接返回 → Date 等特殊对象先特判 → WeakMap 缓存斩断循环引用 → 区分数组与对象后逐键递归。
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj; // 原始值直接返回
if (obj instanceof Date) return new Date(obj); // Date 等特殊对象先特判
if (obj instanceof RegExp) return new RegExp(obj); // RegExp 保留 source 与 flags
if (map.has(obj)) return map.get(obj); // 循环引用:命中缓存立即返回
const clone = Array.isArray(obj) ? [] : {}; // 区分数组与普通对象
map.set(obj, clone); // 记录已拷贝引用
for (const key of Reflect.ownKeys(obj)) {
clone[key] = deepClone(obj[key], map); // 递归拷贝每个属性
}
return clone;
}
// 生产环境优先 structuredClone(obj):原生支持 Map / Set / 循环引用,性能也更好