# 场景篇
这套题从项目里真会碰到的事讲起:进度条为什么不动、流式输出怎么不卡、配置合并怎么串了数据,再落到背后的原理。一共 550 道,分在 11 个模块、61 个分类里。每道题先给一张 30 秒能说完的速记卡,再展开讲原理和代码,最后附上面试官顺着问下去的几个追问。
不知道从哪开始的话,挑自己最没底的分类先看,看完一个分类再回头对照速记卡复述一遍。下面是全部模块和分类,点分类名直接跳过去:
- 语言基础:JavaScript 核心、JavaScript 数据处理、TypeScript、HTML 与语义化、CSS、布局与移动端适配
- 浏览器与网络:浏览器渲染原理、事件与页面生命周期、网络协议、浏览器缓存与存储、异步请求与数据获取、Web 安全
- 框架:React 核心、React 工程实践、Vue 核心、Vue 工程实践、SSR 与同构、状态管理
- 工程化:构建工具原理、包管理与 Monorepo、代码规范与 CI/CD、前端测试、部署与运维
- 性能与体验:加载性能、运行时性能、无障碍、国际化
- 业务场景:登录鉴权与权限、表单与表格、文件上传与下载、实时通信、组件设计与组件库、富文本与在线编辑器、低代码与可视化搭建、数据可视化、埋点与监控
- 大前端与跨端:移动端 H5、小程序、React Native、Flutter、鸿蒙 HarmonyOS、跨端框架、桌面端、Hybrid 与 JSBridge
- 图形与多媒体:Canvas 与 SVG、WebGL 与 Three.js、音视频与 WebRTC、WebAssembly
- 服务端与架构:Node.js 核心、BFF 与服务端、Serverless 与边缘计算、微前端、前端架构与设计模式
AI专题:流式输出与对话界面、AI 对话产品工程、Agent 与工具调用、RAG 与知识库、AI 应用安全与成本、端侧 AI、AI 辅助研发- 综合:项目经验与技术选型
# JavaScript 核心
语言机制、异步模型与内存管理的核心原理
# 数据类型与类型判断
30 秒速记
- 8 种类型:7 种原始类型(
undefined、null、boolean、number、string、symbol、bigint)加object,函数和数组都属于对象 typeof只适合判断原始类型和函数,typeof null === 'object'是历史遗留buginstanceof查的是原型链,跨iframe、跨Node的vm上下文就失效,数组请用Array.isArray- 想拿到精确类型用
Object.prototype.toString.call(v),但它会被Symbol.toStringTag改写,不要拿它做安全校验
先列 8 种类型,再按「typeof → instanceof → toString」对比各自的盲区,最后落到项目里怎么组合使用。
低代码编辑器把画布放在 iframe 里,外层的属性面板直接调用画布暴露的方法拿选中的组件列表。某次重构后属性面板永远显示「未选中组件」,打日志一看,数组明明有 3 个元素:
const frame = document.querySelector('iframe')
const list = frame.contentWindow.getSelectedNodes() // iframe 里创建的数组
console.log(list.length) // 3
console.log(list instanceof Array) // false
console.log(Array.isArray(list)) // true
每个 iframe 都有一套自己的全局对象,Array、Object、Date 这些构造函数也各有一份。instanceof 的原理是沿着 list.__proto__ 一路往上找,看能不能碰到右边的 Array.prototype。iframe 里的数组原型链上挂的是它自己那份 Array.prototype,和外层的不是同一个对象,自然找不到。Node 里用 vm.runInNewContext('[1, 2, 3]') 能复现出一模一样的结果。
类型判断的几种手段各有盲区,面试时把这张表讲清楚基本就够了:
| 方式 | 能判断 | 盲区 |
|---|---|---|
typeof |
原始类型、函数 | null、数组、Date 都返回 'object' |
instanceof |
自定义类、内置对象 | 原始类型不行;跨 realm 失效;原型能被改 |
Array.isArray |
数组 | 只管数组,但跨 realm 可靠 |
Object.prototype.toString.call |
几乎所有内置类型 | 被 Symbol.toStringTag 覆盖;自定义类统一是 Object |
typeof 有两个行为要记住:
console.log(typeof null) // 'object'
console.log(typeof []) // 'object'
console.log(typeof function () {}) // 'function'
console.log(typeof 10n) // 'bigint'
console.log(typeof undeclaredVar) // 'undefined',未声明的变量也不报错
null 返回 'object' 是第一版引擎里用类型标签判断留下的 bug,改了会破坏大量线上代码,就一直保留着。对未声明变量不报错这一点,以前常被用来做环境探测,比如 typeof window !== 'undefined' 判断是不是在浏览器里。
Object.prototype.toString 返回的是 [object Xxx],大部分工具库的 isDate、isRegExp 都基于它:
const tag = v => Object.prototype.toString.call(v).slice(8, -1)
console.log(tag(null), tag([]), tag(new Date()), tag(/a/), tag(new Map()))
// Null Array Date RegExp Map
console.log(tag({ [Symbol.toStringTag]: 'Array' })) // 'Array'
最后一行说明它能被伪造。ES6 之后这个标签读的是对象的 Symbol.toStringTag 属性,任何人都能改,所以判断数组请老老实实用 Array.isArray。
NaN 也常被拎出来问。NaN === NaN 是 false,全局的 isNaN('abc') 会先把参数转成数字所以返回 true,判断时用 Number.isNaN;要同时区分 0 和 -0 就用 Object.is。
console.log(isNaN('abc'), Number.isNaN('abc')) // true false
console.log(Object.is(NaN, NaN), Object.is(0, -0)) // true false
项目里的写法一般是组合起来:原始类型用 typeof,数组用 Array.isArray,内置对象用 toString 标签,判断「是不是自己写的某个类」才用 instanceof,并且确保对象不会跨 realm 传递。
相关阅读:进阶篇 · 类型及检测方式
面试官追问
追问 1怎么判断一个值是纯对象(plain object)?
先用 toString 标签确认是 Object,再看原型:原型是 null(Object.create(null) 创建的),或者原型的原型是 null(也就是原型为某个 realm 的 Object.prototype)。这样跨 iframe 的字面量对象也能通过,类实例会被排除。Redux 的 isPlainObject 用的就是这个思路。
追问 2手写 instanceof 怎么写?
拿到右侧的 prototype,用 Object.getPrototypeOf 沿着左侧对象的原型链循环往上找,相等返回 true,找到 null 返回 false。左侧是原始类型直接返回 false。另外右侧如果定义了 Symbol.hasInstance,原生 instanceof 会优先调用它。
追问 3postMessage 传过来的数组,instanceof Array 也会失败吗?
不会。postMessage 走结构化克隆,接收方拿到的是在自己 realm 里新建的数组,原型就是本地的 Array.prototype。只有直接跨窗口访问对象引用的时候才会出问题。
# 隐式类型转换
30 秒速记
- 转换只有三个目标:转布尔、转数字、转字符串;假值只有
false、0、-0、0n、''、null、undefined、NaN这几个,[]、{}、'0'都是真值 - 默认值用
??而不是||:||会把0和空字符串一起替换掉;??(ES2020)只对null、undefined生效 ==的规则:null只和undefined相等;有布尔先转数字;字符串和数字比转数字;对象和原始值比先把对象转原始值- 对象转原始值先看
Symbol.toPrimitive,没有就按hint调valueOf/toString;+
