第4章 技术基础高频面试题(JavaScript)
闭包、原型链、异步、事件循环、ES6+
本章聚焦 JS 核心机制的高频考察点。面试官常通过 3~5 轮追问探测原理深度。下面先用两张图建立全局视角,接着用对比表格梳理差异,最后用 3 个典型问答卡片拆解回答技巧。
核心特性对比表
| 特性 | 底层原理 | 典型应用 | 常见面试误区 |
|---|---|---|---|
| 闭包 | 函数 + 小状态引用 | 模块封装、防抖节流 | 误认为闭包只用于 return |
| 原型链 | \_\_proto\_\_ → prototype | 对象继承、方法复用 | 混淆隐式原型与显式原型 |
| 异步 | 回调 → Promise → async/await | 请求、定时器、事件 | 忽略错误处理链 |
| 事件循环 | 宏任务 ↔ 微任务 ↔ 渲染 | 性能优化、顺序控制 | 误以为微任务先于宏任务抢执行 |
| ES6+ | 箭头函数、class、模块 | 代码简洁、树摇 | 箭头函数不能用 new |
🧩 面试问题 1:请谈谈 JavaScript 中的闭包,并举例说明其作用。
🎯 面试官考察点
- 是否理解闭包的定义(函数 + 其创建时的词法环境)
- 能否说出闭包的内存影响及常见用途
- 是否知道闭包在代码模块化中的实践
📝 回答框架
- 定义:闭包指函数能够记住并访问其词法作用域,即使函数执行在该作用域之外。
- 原理:函数内部保留对外部变量的引用,形成“闭包”链。
- 应用:防抖/节流、模块封装、循环中的异步回调。
- 注意:可能造成内存泄漏,需及时释放引用。
💡 简短示例回答
闭包是函数与其创建时作用域的组合。例如:const add = (() => { let count = 0; return () => ++count; })();。每次调用 add() 都能访问并修改 count,实现了私有变量。常用于保持状态、实现模块化。
🔍 可能追问
- 闭包中的变量是值传递还是引用?
- 如何避免闭包造成的内存泄漏?
- 对比 ES6 class 私有字段与闭包的差异。
⚠️ 避坑提醒:不要只背概念不举例;使用闭包时务必注意循环中 var 造成的问题(改为 let 或 IIFE)。
🧩 面试问题 2:解释 JavaScript 原型链,以及它与 class 继承的关系。
🎯 面试官考察点
- 是否清楚
\_\_proto\_\_与prototype的区别 - 能否画出完整的原型链查找路径
- 对 ES6 class 底层实现的理解
📝 回答框架
- 核心概念:每个对象都有一个
\_\_proto\_\_指向其构造函数的prototype,层层连接形成链。 - 查找机制:访问属性时沿链向上搜索,直到
null。 - 与 class 关系:ES6 class 本质上是原型链的语法糖,
extends实现原型链连接。 - 应用:方法复用、修改
prototype批量扩展实例功能。
💡 简短示例回答
构造函数 function Animal() {} 的 prototype 包含 eat 方法。实例 new Cat() 的 \_\_proto\_\_ 指向 Cat.prototype,再指向 Animal.prototype。调用 cat.eat() 即沿此链查找。ES6 的 class Cat extends Animal 实现相同机制。
🔍 可能追问
class的super()在原型链上如何工作?- 手动实现
new操作符的过程。 - 对比寄生组合式继承与 class 继承的性能差异。
⚠️ 避坑提醒:不要混淆
\_\_proto\_\_ 和 prototype;明确 Object.create 创建对象时的原型设置。
🧩 面试问题 3:描述浏览器事件循环机制,尤其是宏任务与微任务的执行顺序。
🎯 面试官考察点
- 能否清晰表述主线程 → 清空微任务 → 取一个宏任务的循环过程
- 对 async/await 在事件循环中的处理是否熟悉
- 是否了解不同宿主(浏览器/Node)的差异
📝 回答框架
- 循环单位:每次取出一个宏任务执行,随后检查微任务队列并全部执行。
- 任务分类:宏任务(setTimeout、UI 事件等),微任务(Promise.then、MutationObserver)。
- 执行顺序:同步代码 → 微任务 → 渲染 → 下一个宏任务。
- async/await:await 后代码被包裹在微任务中恢复执行。
💡 简短示例回答
执行 setTimeout(()=>console.log(1),0); Promise.resolve().then(()=>console.log(2)); 输出顺序是 2 后 1。原因是 Promise.then 作为微任务,在当前宏任务结束前清空。async 函数中 await 后的代码同样属于微任务回调。掌握此顺序可排查异步时序问题。
🔍 可能追问
- 写出
async/await与Promise混用的实际执行顺序。 - Node 中的
process.nextTick与浏览器微任务区别。 - requestAnimationFrame 在事件循环中的位置。
⚠️ 避坑提醒:不要误认为微任务之间不插队;不要混淆浏览器与 Node 的事件循环模型(Node 14+ 与浏览器趋同但仍略有差异)。
💡 迁移提示:遇到 ES6+ 问题(如箭头函数、解构、模块机制),可直接套用“定义 → 原理 → 应用 → 示例 → 延伸”框架。例如箭头函数:没有自己的 this / arguments,不能 new,常用于回调保持作用域。