模块一:基石与底层核心原理
本模块为前端所有上层能力的底层支撑,所有框架、工程、业务问题最终均可溯源到底层原理,是面试与架构落地的核心内功。
目录
模块一:基石与底层核心原理
1. 词法作用域 & 闭包
1.1 核心本质
词法作用域(静态作用域)
闭包(Closure)
基础示例(计数器闭包)
1.2 实战核心应用场景(项目落地)
防抖/节流
模块化私有变量
循环事件绑定
函数柯里化/函数组合
记忆化缓存
模块化私有变量实战代码
1.3 闭包与内存泄漏
常见泄漏场景
解决方案
1.4 WeakMap / WeakSet 闭包内存优化
WeakMap 实现类私有属性
2. JS 执行机制:执行上下文、调用栈、事件循环
2.1 执行上下文与调用栈(运行底层)
执行上下文
调用栈(Call Stack)
变量提升与 TDZ
2.2 浏览器事件循环(异步核心)
任务分类
宏任务(MacroTask)
微任务(MicroTask)
标准执行顺序
经典推演题
async/await 执行本质
3. V8 垃圾回收机制(内存优化核心)
3.1 核心回收算法:标记清除
3.2 分代回收机制(V8 核心)
新生代(短期存活对象)
老生代(长期存活对象)
3.3 弱引用与内存优化实战
开发内存优化准则
4. 原型链、继承与 this 绑定
4.1 原型与原型链核心原理
4.2 五种继承方案(优劣对比)
寄生组合继承标准实现
4.3 this 四大绑定规则(优先级:new > 显式 > 隐式 > 默认)
箭头函数 this 特性
5. 前端高频手写源码
5.1 防抖 Debounce(基础版 + 立即执行版)
5.2 节流 Throttle(时间戳版 + 定时器版)
5.3 完整版深拷贝(支持循环引用/Date/RegExp)
5.4 Promise/A+ 核心手写实现(完整版骨架)
5.5 Promise 静态方法手写(all / allSettled / race)
5.6 异步并发调度器(限制请求并发数)
1. 词法作用域 & 闭包
1.1 核心本质
词法作用域(静态作用域)
JS 唯一的作用域规则。
函数的作用域在代码定义阶段就已确定,和函数调用位置、调用方式无关。
变量的访问权限由代码书写的层级位置决定。
闭包(Closure)
函数+其定义时的词法环境形成的绑定组合。
当内部函数被外部返回、赋值、传参,脱离原有词法作用域执行时,依然会保留对定义时外层作用域变量的引用,使得外层函数执行完毕后,局部变量不会被 GC 回收。
基础示例(计数器闭包)
function createCounter() { let count = 0; // 外层局部变量,被闭包持有 return function () { return ++count; // 内部函数引用外层变量,形成闭包 }; } const counter = createCounter(); console.log(counter()); // 1 console.log(counter()); // 2 // createCounter 执行栈已弹出,但 count 因闭包持续存在1.2 实战核心应用场景(项目落地)
防抖/节流
- 通过闭包持久保存定时器 ID、上一次执行时间戳,实现函数频率控制
模块化私有变量
- 隐藏内部状态,仅暴露可控 API,防止全局污染
循环事件绑定
- 解决 var 变量全局穿透问题,锁定每一次循环的临时变量
函数柯里化/函数组合
- 利用闭包缓存前置参数,实现参数复用
记忆化缓存
- 缓存高频计算结果、接口请求结果,避免重复执行
模块化私有变量实战代码
const userModule = (function () { // 私有变量,外部无法直接访问 let name = 'default'; // 暴露公共方法,通过闭包访问私有变量 return { getName: () => name, setName: (val) => { name = val; } }; })();1.3 闭包与内存泄漏
核心结论:闭包本身不是内存泄漏,不合理的闭包引用才会导致内存泄漏。
正常闭包:按需持有变量,使用完毕后引用断开,可正常 GC。
泄漏本质:闭包长期持有无用大型对象/变量,引用链无法断开,导致内存无法释放、内存堆积。
常见泄漏场景
- 闭包引用超大对象(DOM、大数组、实例),仅使用部分属性,整体对象被强制保留
- 闭包被全局变量、长期存活的定时器/事件监听持有,作用域无法销毁
- SPA 页面路由切换,未销毁闭包绑定的事件、定时器
解决方案
- 无用闭包引用手动置空,断开引用链
- 内存敏感场景使用 WeakMap/WeakSet 弱引用存储数据
- 页面销毁时清空定时器、移除事件监听
1.4 WeakMap / WeakSet 闭包内存优化
核心特性:键为对象类型,属于弱引用,不影响垃圾回收。当键对象无其他强引用时,会被自动 GC,对应键值对自动销毁。
适用场景:DOM 关联数据存储、类私有属性、临时缓存、闭包临时数据
WeakMap 实现类私有属性
const wm = new WeakMap(); class User { constructor(name) { // 私有数据,实例销毁后自动回收 wm.set(this, { name, createdAt: Date.now() }); } getName() { return wm.get(this).name; } } // 实例被销毁,WeakMap 对应数据自动 GC,无内存残留闭包的核心是函数和其词法环境的绑定,依托 JS 词法作用域特性实现变量持久化。
项目中主要用闭包实现防抖节流、函数缓存和模块化私有状态管理。
同时清楚闭包容易引发内存堆积,在内存敏感场景会结合 WeakMap 弱引用优化,手动断开无用引用,避免内存泄漏问题。
2. JS 执行机制:执行上下文、调用栈、事件循环
2.1 执行上下文与调用栈(运行底层)
执行上下文
JS 代码运行的必备环境,分为 3 种:全局上下文、函数上下文、eval 上下文。
单个上下文核心包含:变量对象、作用域链、this 指向。
调用栈(Call Stack)
遵循后进先出(LIFO)规则。
函数调用时创建执行上下文压栈,函数执行完毕自动弹栈,清空上下文。
栈溢出原因:递归无终止、函数嵌套过深。
变量提升与 TDZ
var、函数声明存在变量提升;
let/const 存在暂时性死区,声明前无法访问,杜绝变量污染。
2.2 浏览器事件循环(异步核心)
JS 单线程运行,通过事件循环实现非阻塞异步,核心是同步代码、微任务、宏任务的优先级调度。
任务分类
宏任务(MacroTask)
setTimeout、setInterval、I/O 操作、UI 渲染、MessageChannel
微任务(MicroTask)
Promise.then/catch/finally、async/await、queueMicrotask、MutationObserver
标准执行顺序
- 执行所有同步代码
- 清空全部微任务队列
- 取出一个宏任务执行
- 循环往复,完成事件循环调度
经典推演题
console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4'); // 输出顺序:1 → 4 → 3 → 2async/await 执行本质
await 会阻塞当前同步代码,await后续所有代码都会被封装为微任务,等价于 Promise.then 回调。
async function foo() { console.log('A'); // 同步 await bar(); console.log('B'); // 微任务 }2.3 Node.js事件循环差异
Node 基于 libuv 实现事件循环,分为 6 个固定阶段:
timers → pending callbacks → idle, prepare → poll → check → close callbacks。
每个阶段拥有独立宏任务队列。
特殊优先级:process.nextTick > Promise微任务>常规宏任务
JS 单线程通过事件循环实现异步,核心是微任务优先于宏任务执行。
每一轮事件循环都会先执行完所有同步代码、清空微任务队列,再执行单个宏任务。
async/await 是 Promise 和 Generator 的语法糖,await 后的代码属于微任务。
Node 环境多了阶段调度和 nextTick 高优先级微任务,和浏览器事件循环存在明显差异。
3. V8 垃圾回收机制(内存优化核心)
3.1 核心回收算法:标记清除
从根对象(全局、栈变量)遍历标记所有可达对象,执行完毕后清除所有未被标记的无效对象,完美解决循环引用无法回收的问题,是现代 JS 主流回收算法。
3.2 分代回收机制(V8 核心)
V8 将堆内存分为新生代、老生代,针对不同生命周期对象采用不同回收算法,提升回收效率、减少卡顿。
新生代(短期存活对象)
- 存储临时变量、短期实例、回调变量
- 使用 Scavenge 复制算法,内存分为 From、To 两个对等半区
- 存活对象复制到 To 区,清空 From 区,效率极高
- 晋升规则:经历一次回收、或 To 区占用超 25%,晋升老生代
老生代(长期存活对象)
- 存储全局对象、闭包变量、长期页面实例、DOM 对象
- 采用 标记清除 + 标记压缩 组合算法
- 标记清除清理无效对象,标记压缩整理内存碎片,避免内存碎片化
3.3 弱引用与内存优化实战
WeakMap、WeakSet、WeakRef 为弱引用类型,不阻止垃圾回收,是前端内存优化的核心方案。
FinalizationRegistry 可监听对象回收,触发后置清理回调(慎用,回调执行时机不固定)。
开发内存优化准则
- 禁止全局无节制挂载冗余属性
- 页面销毁、组件卸载时,清空定时器、移除事件监听、断开闭包引用
- 缓存、临时数据优先使用弱引用存储,自动释放无效内存
4. 原型链、继承与 this 绑定
4.1 原型与原型链核心原理
所有函数拥有 prototype 原型对象,存储实例共享属性和方法;所有实例通过 __proto__ 指向构造函数原型对象。
层层向上查找,最终指向 Object.prototype,终点为 null,构成原型链。
属性查找规则:自身属性 → 原型链逐层向上查找 → 找不到返回 undefined
function Person(name) { this.name = name; } // 原型挂载共享方法 Person.prototype.sayHi = function () { console.log('Hi'); }; const p = new Person('Alice'); p.sayHi(); console.log(p.__proto__ === Person.prototype); // true console.log(Person.prototype.__proto__ === Object.prototype); // true4.2 五种继承方案(优劣对比)
- 原型链继承:Child.prototype = new Parent();缺点:引用类型属性共享、无法向父类传参
- 构造函数继承:Parent.call(this);缺点:无法继承原型方法,无法复用
- 组合继承:原型链 + 构造函数;缺点:父类构造执行两次,存在冗余
- 寄生组合继承(最优原生继承方案):无冗余、可传参、方法复用
- ES6 class extends:语法糖,底层基于寄生组合继承实现,项目首选
寄生组合继承标准实现
function inheritPrototype(Child, Parent) { // 继承父类原型 Child.prototype = Object.create(Parent.prototype); // 修复构造器指向 Child.prototype.constructor = Child; }4.3 this 四大绑定规则(优先级:new > 显式 > 隐式 > 默认)
- 默认绑定:独立函数调用,非严格指向 window,严格模式 undefined
- 隐式绑定:obj.foo(),this 指向调用对象;存在隐式丢失问题
- 显式绑定:call/apply/bind 强制修改 this 指向
- new绑定:构造函数调用,this 指向新建实例对象
箭头函数 this 特性
箭头函数无自身this、无prototype、无法new、无法绑定,其 this 为外层词法作用域的 this,完美解决回调 this 丢失问题。
原型链是 JS 实现面向对象继承的底层基础,现代开发优先使用 ES6 class 语法,但底层依然是寄生组合继承。
this 指向由调用方式而非定义方式决定,遵循固定优先级。
箭头函数的词法 this 特性,能有效解决回调、定时器中的 this 指向丢失问题,简化项目代码。
5. 前端高频手写源码
5.1 防抖 Debounce(基础版 + 立即执行版)
// 基础延迟版 function debounce(fn, delay = 300) { let timer = null; return function (...args) { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; } // 立即执行 + 延迟收尾版 function debounce(fn, delay, immediate = false) { let timer = null; return function (...args) { if (timer) clearTimeout(timer); // 立即执行 if (immediate && !timer) { fn.apply(this, args); } timer = setTimeout(() => { timer = null; // 延迟执行收尾 if (!immediate) fn.apply(this, args); }, delay); }; }5.2 节流 Throttle(时间戳版 + 定时器版)
// 时间戳版:立即响应,尾部不执行 function throttle(fn, delay = 300) { let lastTime = 0; return function (...args) { const now = Date.now(); if (now - lastTime > delay) { lastTime = now; fn.apply(this, args); } }; } // 定时器版:延迟响应,尾部补执行 function throttle(fn, delay = 300) { let timer = null; return function (...args) { if (!timer) { timer = setTimeout(() => { timer = null; fn.apply(this, args); }, delay); } }; }5.3 完整版深拷贝(支持循环引用/Date/RegExp)
function deepClone(obj, map = new WeakMap()) { // 基础类型 / null 直接返回 if (obj === null || typeof obj !== 'object') return obj; // 处理日期、正则特殊对象 if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags); // 解决循环引用 if (map.has(obj)) return map.get(obj); // 区分数组、对象创建新容器 const cloneObj = Array.isArray(obj) ? [] : {}; map.set(obj, cloneObj); // 递归遍历拷贝 for (let key in obj) { if (obj.hasOwnProperty(key)) { cloneObj[key] = deepClone(obj[key], map); } } return cloneObj; }5.4 Promise/A+ 核心手写实现(完整版骨架)
class MyPromise { constructor(executor) { // 三种状态:pending/fulfilled/rejected this.state = 'pending'; this.value = undefined; this.reason = undefined; // 存储异步回调队列 this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state !== 'pending') return; this.state = 'fulfilled'; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn()); }; const reject = (reason) => { if (this.state !== 'pending') return; this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn()); }; // 捕获执行器异常 try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { // 穿透处理 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; }; const promise2 = new MyPromise((resolve, reject) => { // 异步调度,保证微任务时序 const schedule = (fn, val) => { setTimeout(() => { try { const x = fn(val); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }); }; // 同步状态直接执行 if (this.state === 'fulfilled') { schedule(onFulfilled, this.value); } else if (this.state === 'rejected') { schedule(onRejected, this.reason); } else { // 异步状态存入队列 this.onFulfilledCallbacks.push(() => schedule(onFulfilled, this.value)); this.onRejectedCallbacks.push(() => schedule(onRejected, this.reason)); } }); return promise2; } // 异常捕获 catch(onRejected) { return this.then(null, onRejected); } } // 核心解析函数:处理链式调用、Promise 嵌套 function resolvePromise(promise2, x, resolve, reject) { // 禁止循环引用 if (promise2 === x) return reject(new TypeError('Chaining cycle detected for promise')); // 判断是否为 Promise if (x instanceof MyPromise) { x.then(resolve, reject); } else { resolve(x); } }5.5 Promise 静态方法手写(all / allSettled / race)
// Promise.all:全部成功才成功,一个失败直接失败 Promise.myAll = function (promises) { return new Promise((resolve, reject) => { let count = 0; const result = []; const len = promises.length; if (len === 0) resolve([]); for (let i = 0; i < len; i++) { Promise.resolve(promises[i]).then(res => { result[i] = res; count++; if (count === len) resolve(result); }).catch(err => reject(err)); } }); }; // Promise.allSettled:所有任务执行完毕,返回成功/失败结果 Promise.myAllSettled = function (promises) { return Promise.all(promises.map(item => Promise.resolve(item) .then(value => ({ status: 'fulfilled', value })) .catch(reason => ({ status: 'rejected', reason })) )); }; // Promise.race:谁先完成返回谁 Promise.myRace = function (promises) { return new Promise((resolve, reject) => { promises.forEach(item => { Promise.resolve(item).then(resolve, reject); }); }); };5.6 异步并发调度器(限制请求并发数)
class Scheduler { constructor(limit) { this.limit = limit; // 最大并发数 this.queue = []; // 任务等待队列 this.running = 0; // 当前执行任务数 } // 添加异步任务 add(task) { return new Promise((resolve, reject) => { this.queue.push(() => task().then(resolve, reject)); this.run(); }); } // 执行任务 run() { while (this.running < this.limit && this.queue.length) { const task = this.queue.shift(); this.running++; task().finally(() => { this.running--; this.run(); }); } } }Promise 链式调用依靠返回新 Promise、递归解析实现;
防抖节流依托闭包缓存变量实现频率控制;
并发调度器通过队列+计数器实现流量管控,在文件批量上传、接口限流、异步任务调度等业务场景中可以直接落地使用。