JavaScript 核心:原型链、闭包与事件循环

2026年 阅读约 8 分钟 面试指南 · 前端基础

JavaScript 核心面试指南:原型链与继承(prototype/__proto__)、闭包与作用域、事件循环与微任务/宏任务、Promise 与 async/await 异步编程,附代码示例。 JavaScript 原型链 闭包 事件循环 异步 面试

一句话总结

JS 的"怪"大多来自三个底层机制:原型链决定"属性去哪找",闭包决定"变量活多久",事件循环决定"代码何时跑"。吃透这三者,异步、继承、内存泄漏都迎刃而解。

机制回答的核心问题
原型链访问一个属性时去哪里找?
闭包函数记住了哪些变量?
事件循环同步/异步谁先执行?

原型链与继承

JS 对象通过内部属性 [[Prototype]](可用 __proto__ 访问)链接到原型对象;访问属性时若自身没有,就沿原型链向上查找,直到 null。

function Animal(name) { this.name = name; }
Animal.prototype.speak = function () { return this.name + " makes sound"; };

function Dog(name) { Animal.call(this, name); }
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

const d = new Dog("Rex");
console.log(d.speak());          // Rex makes sound(沿原型链找到 Animal.prototype)
console.log(d instanceof Dog);   // true

class 语法糖:ES6 class 只是原型继承的语法包装,底层依旧是原型链;extends 对应 Object.create 链接原型。

闭包与作用域

闭包:函数与其定义时的词法作用域的组合。即使外层函数已返回,内部函数仍持有对当时变量的引用。

function createCounter() {
  let n = 0;
  return function () { return ++n; };  // 记住了 n
}
const c = createCounter();
console.log(c(), c(), c());   // 1 2 3
  • 用途:数据私有化、函数工厂、防抖节流、模块化(IIFE)。
  • 陷阱:循环中用 var 创建闭包会共享同一变量;用 let 块级作用域或立即执行解决。

事件循环

JS 单线程,靠事件循环调度:先清空调用栈(同步任务),再处理微任务队列(Promise.then、queueMicrotask),最后取一个宏任务(setTimeout、I/O、UI 事件)执行,每轮宏任务后清空所有微任务。

console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// 输出顺序:1 4 3 2
// 同步(1,4) → 微任务(3) → 宏任务(2)

关键:微任务优先级高于宏任务;await 之后的代码等价于 .then 回调,属于微任务。

Promise 与 async/await

Promise 是异步结果容器,三种状态:pending / fulfilled / rejected,状态一旦确定不可变。async/await 是 Promise 的同步风格语法糖,让异步代码更易读。

async function loadUser(id) {
  try {
    const res = await fetch("/api/user/" + id);
    if (!res.ok) throw new Error("HTTP " + res.status);
    return await res.json();
  } catch (e) {
    console.error("加载失败", e);
    throw e;
  }
}
  • Promise.all 并发等全部;Promise.allSettled 不因单个失败而中断。
  • 并发控制:大量请求用分批 + Promise.all 或 p-limit,避免瞬间打满连接。
  • 避免 await 在循环里串行:应先用数组收集 Promise 再 Promise.all。

面试模拟

Q1:prototype 和 __proto__ 的区别?
答:prototype 是构造函数上的属性(指向原型对象);__proto__ 是实例上的内部指针,指向其构造函数的 prototype。

Q2:以下输出顺序? setTimeout(fn,0) 与 Promise.then 谁先?
答:微任务(Promise.then)先于宏任务(setTimeout),所以 Promise 先。

Q3:闭包会导致内存泄漏吗?
答:闭包本身不泄漏,但它会延长变量生命周期;若闭包长期持有大对象且不再需要,应及时解除引用。

Q4:async 函数返回什么?
答:永远返回一个 Promise;return 的值被包裹为 resolved Promise,throw 变为 rejected。