第4章 技术基础高频面试题(JavaScript)
闭包、原型链、异步、事件循环、ES6+

本章聚焦 JS 核心机制的高频考察点。面试官常通过 3~5 轮追问探测原理深度。下面先用两张图建立全局视角,接着用对比表格梳理差异,最后用 3 个典型问答卡片拆解回答技巧。

高频 JS 面试题 闭包 · 作用域链 原型链 · 继承 异步 · Promise 事件循环 · 队列 ES6+ · 新特性 回答结构:定义 → 原理 → 应用 → 示例 → 延伸 事件循环 宏任务 (MacroTask) 微任务 (MicroTask) setTimeout / setInterval DOM 事件 / I/O Promise.then queueMicrotask async/await 执行顺序 加分点:能手画队列图 + 指出 requestAnimationFrame 不属于宏/微任务

核心特性对比表

特性 底层原理 典型应用 常见面试误区
闭包函数 + 小状态引用模块封装、防抖节流误认为闭包只用于 return
原型链\_\_proto\_\_ → prototype对象继承、方法复用混淆隐式原型与显式原型
异步回调 → Promise → async/await请求、定时器、事件忽略错误处理链
事件循环宏任务 ↔ 微任务 ↔ 渲染性能优化、顺序控制误以为微任务先于宏任务抢执行
ES6+箭头函数、class、模块代码简洁、树摇箭头函数不能用 new

🧩 面试问题 1:请谈谈 JavaScript 中的闭包,并举例说明其作用。

🎯 面试官考察点

  • 是否理解闭包的定义(函数 + 其创建时的词法环境)
  • 能否说出闭包的内存影响及常见用途
  • 是否知道闭包在代码模块化中的实践

📝 回答框架

  1. 定义:闭包指函数能够记住并访问其词法作用域,即使函数执行在该作用域之外。
  2. 原理:函数内部保留对外部变量的引用,形成“闭包”链。
  3. 应用:防抖/节流、模块封装、循环中的异步回调。
  4. 注意:可能造成内存泄漏,需及时释放引用。

💡 简短示例回答

闭包是函数与其创建时作用域的组合。例如:const add = (() => { let count = 0; return () => ++count; })();。每次调用 add() 都能访问并修改 count,实现了私有变量。常用于保持状态、实现模块化。

🔍 可能追问

  • 闭包中的变量是值传递还是引用?
  • 如何避免闭包造成的内存泄漏?
  • 对比 ES6 class 私有字段与闭包的差异。
⚠️ 避坑提醒:不要只背概念不举例;使用闭包时务必注意循环中 var 造成的问题(改为 let 或 IIFE)。

🧩 面试问题 2:解释 JavaScript 原型链,以及它与 class 继承的关系。

🎯 面试官考察点

  • 是否清楚 \_\_proto\_\_ 与 prototype 的区别
  • 能否画出完整的原型链查找路径
  • 对 ES6 class 底层实现的理解

📝 回答框架

  1. 核心概念:每个对象都有一个 \_\_proto\_\_ 指向其构造函数的 prototype,层层连接形成链。
  2. 查找机制:访问属性时沿链向上搜索,直到 null。
  3. 与 class 关系:ES6 class 本质上是原型链的语法糖,extends 实现原型链连接。
  4. 应用:方法复用、修改 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)的差异

📝 回答框架

  1. 循环单位:每次取出一个宏任务执行,随后检查微任务队列并全部执行。
  2. 任务分类:宏任务(setTimeout、UI 事件等),微任务(Promise.then、MutationObserver)。
  3. 执行顺序:同步代码 → 微任务 → 渲染 → 下一个宏任务。
  4. 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,常用于回调保持作用域。