ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

前端必会能力 - 底层核心原理

2026/8/9 14:28:16 拓冰建站 浏览量
前端必会能力 - 底层核心原理

模块一:基石与底层核心原理

本模块为前端所有上层能力的底层支撑,所有框架、工程、业务问题最终均可溯源到底层原理,是面试与架构落地的核心内功。

目录

模块一:基石与底层核心原理

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

标准执行顺序
  1. 执行所有同步代码
  1. 清空全部微任务队列
  1. 取出一个宏任务执行
  1. 循环往复,完成事件循环调度
经典推演题
console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4'); // 输出顺序:1 → 4 → 3 → 2
async/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); // true

4.2 五种继承方案(优劣对比)

  1. 原型链继承:Child.prototype = new Parent();缺点:引用类型属性共享、无法向父类传参
  1. 构造函数继承:Parent.call(this);缺点:无法继承原型方法,无法复用
  1. 组合继承:原型链 + 构造函数;缺点:父类构造执行两次,存在冗余
  1. 寄生组合继承(最优原生继承方案):无冗余、可传参、方法复用
  1. 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、递归解析实现;

防抖节流依托闭包缓存变量实现频率控制;

并发调度器通过队列+计数器实现流量管控,在文件批量上传、接口限流、异步任务调度等业务场景中可以直接落地使用。