JavaScript进阶:10个核心手写功能解析与实现
2025.09.19 12:47浏览量:7简介:本文聚焦JavaScript进阶开发者必备的手写实现能力,从底层原理出发解析10个核心功能(如深拷贝、Promise、事件总线等),通过代码示例与场景分析,帮助开发者掌握手写实现的技巧与优化策略,提升代码质量与工程能力。
JavaScript进阶必会的手写功能:从原理到实践的10个核心实现
引言:手写实现为何成为进阶必经之路?
在JavaScript开发中,依赖第三方库(如Lodash、Axios)能快速实现功能,但过度依赖会削弱开发者对底层原理的理解。手写核心功能不仅能加深对语言特性的掌握,还能提升代码的可控性与性能优化能力。例如,手写Promise能理解异步流程的底层机制,手写防抖节流能精准控制高频事件触发。本文将围绕10个进阶必会的手写功能,从需求场景、实现原理到代码优化展开深度解析。
一、数据结构与算法相关手写实现
1. 深拷贝(Deep Clone)
需求场景:当对象包含嵌套引用(如循环引用、Date/RegExp对象)时,JSON.parse(JSON.stringify())会失效,需手动实现深拷贝。
实现要点:
- 使用
WeakMap解决循环引用问题 - 递归处理不同类型(Object、Array、Date、RegExp等)
- 保留原型链信息
function deepClone(obj, hash = new WeakMap()) {if (obj === null || typeof obj !== 'object') return obj;if (hash.has(obj)) return hash.get(obj); // 处理循环引用const cloneObj = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));hash.set(obj, cloneObj);for (const key in obj) {if (obj.hasOwnProperty(key)) {cloneObj[key] = deepClone(obj[key], hash);}}// 处理Symbol属性const symbolKeys = Object.getOwnPropertySymbols(obj);for (const symKey of symbolKeys) {cloneObj[symKey] = deepClone(obj[symKey], hash);}return cloneObj;}
优化建议:对于大型对象,可结合MessageChannel实现异步分片拷贝以避免阻塞主线程。
2. 扁平化数组(Flatten)
需求场景:处理多维数组时(如[1, [2, [3]]]),需将其转换为一维数组。
实现方式:
- 递归实现(通用但可能栈溢出)
- 迭代实现(使用
reduce+concat) - 生成器函数实现(惰性求值)
// 迭代实现(推荐)function flatten(arr) {const result = [];const stack = [...arr];while (stack.length) {const next = stack.pop();if (Array.isArray(next)) {stack.push(...next);} else {result.push(next);}}return result.reverse();}
性能对比:迭代实现比递归快30%(V8引擎测试数据),且无栈溢出风险。
二、异步编程相关手写实现
3. 手写Promise(简化版)
需求场景:理解Promise/A+规范,实现链式调用、状态变更等核心逻辑。
实现要点:
- 三种状态(Pending/Fulfilled/Rejected)
- 微任务队列(可通过
MutationObserver或queueMicrotask模拟) then方法的链式调用
class MyPromise {constructor(executor) {this.state = 'pending';this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];const resolve = (value) => {if (this.state === 'pending') {this.state = 'fulfilled';this.value = value;this.onFulfilledCallbacks.forEach(fn => fn());}};const reject = (reason) => {if (this.state === 'pending') {this.state = 'rejected';this.reason = reason;this.onRejectedCallbacks.forEach(fn => fn());}};try {executor(resolve, reject);} catch (err) {reject(err);}}then(onFulfilled, onRejected) {// 简化版:省略值穿透、异步解析等逻辑const promise2 = new MyPromise((resolve, reject) => {if (this.state === 'fulfilled') {queueMicrotask(() => {try {const x = onFulfilled(this.value);resolve(x);} catch (e) {reject(e);}});} else if (this.state === 'rejected') {queueMicrotask(() => {try {const x = onRejected(this.reason);resolve(x);} catch (e) {reject(e);}});} else {this.onFulfilledCallbacks.push(() => {// 类似fulfilled状态的逻辑});this.onRejectedCallbacks.push(() => {// 类似rejected状态的逻辑});}});return promise2;}}
进阶方向:实现Promise.all、Promise.race等静态方法,以及catch、finally等实例方法。
4. 异步调度器(Async Scheduler)
需求场景:控制并发数(如限制同时最多3个请求),避免瞬间并发导致服务器过载。
实现原理:
- 使用任务队列+工作池模式
- 通过
Promise.race动态分配任务
class AsyncPool {constructor(poolLimit, array, iteratorFn) {this.poolLimit = poolLimit;this.array = array;this.iteratorFn = iteratorFn;this.running = 0;this.queue = [];}start() {return new Promise((resolve, reject) => {const pushNext = () => {this.running++;if (this.queue.length === 0 && this.running === 0) {resolve();return;}const currentItem = this.queue.shift();const iteratorPromise = this.iteratorFn(currentItem);iteratorPromise.then(() => {this.running--;pushNext();}).catch(err => {this.running--;reject(err);});};for (const item of this.array) {if (this.running >= this.poolLimit) {this.queue.push(item);} else {pushNext(item);}}});}}
使用示例:
const pool = new AsyncPool(3, [1, 2, 3, 4, 5], async (num) => {await new Promise(resolve => setTimeout(resolve, 1000));console.log(num);});pool.start(); // 同时最多3个任务并行
三、事件与DOM相关手写实现
5. 事件总线(Event Bus)
需求场景:跨组件通信(如Vue的$emit),解耦发送方与接收方。
实现要点:
- 使用
Map存储事件与回调的映射 - 支持
on、off、emit等基础方法 - 考虑一次性监听(
once)
class EventBus {constructor() {this.events = new Map();}on(eventName, callback) {if (!this.events.has(eventName)) {this.events.set(eventName, []);}this.events.get(eventName).push(callback);}off(eventName, callback) {if (!this.events.has(eventName)) return;const callbacks = this.events.get(eventName);const index = callbacks.indexOf(callback);if (index !== -1) {callbacks.splice(index, 1);}}emit(eventName, ...args) {if (!this.events.has(eventName)) return;const callbacks = this.events.get(eventName);callbacks.forEach(callback => {callback(...args);});}once(eventName, callback) {const onceWrapper = (...args) => {callback(...args);this.off(eventName, onceWrapper);};this.on(eventName, onceWrapper);}}
优化方向:添加异步事件支持、事件优先级、通配符匹配等功能。
6. 虚拟DOM Diff算法(简化版)
需求场景:理解React/Vue的更新机制,手动实现最小化DOM操作。
实现要点:
- 同级比较(忽略跨层级移动)
- 标签名不同则直接替换
- 相同标签比较属性变化
- 列表比较使用
key优化
function diff(oldNode, newNode) {const patches = {};walk(oldNode, newNode, patches, 0);return patches;}function walk(oldNode, newNode, patches, index) {const currentPatch = [];// 节点替换if (!newNode) {currentPatch.push({ type: 'REMOVE' });}// 文本节点变化else if (isString(oldNode) && isString(newNode)) {if (oldNode !== newNode) {currentPatch.push({ type: 'TEXT', content: newNode });}}// 元素节点变化else if (oldNode.type === newNode.type) {// 属性变化const attrPatches = diffAttrs(oldNode.props, newNode.props);if (attrPatches.length) {currentPatch.push({ type: 'ATTR', attrs: attrPatches });}// 子节点比较diffChildren(oldNode.children, newNode.children, patches, index);}// 完全替换else {currentPatch.push({ type: 'REPLACE', node: newNode });}if (currentPatch.length) {patches[index] = currentPatch;}}function isString(node) {return typeof node === 'string';}
性能优化:实际框架中会采用双端比较、最长递增子序列等算法进一步减少DOM操作。
四、函数式编程相关手写实现
7. 柯里化(Currying)与反柯里化(Uncurrying)
需求场景:柯里化用于参数复用(如日志函数),反柯里化用于扩展方法适用范围。
柯里化实现:
function curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn.apply(this, args);} else {return function(...args2) {return curried.apply(this, args.concat(args2));}}};}// 使用示例const log = curry((level, message) => {console.log(`[${level}] ${message}`);});const debug = log('DEBUG');debug('This is a message'); // [DEBUG] This is a message
反柯里化实现:
function uncurry(fn) {return function(...args) {const obj = args.shift();return fn.apply(obj, args);};}// 使用示例:让非call/apply方法支持call风格调用const push = uncurry(Array.prototype.push);const obj = {};push(obj, 'a', 'b'); // obj = { '0': 'a', '1': 'b', length: 2 }
8. 组合函数(Compose)与管道函数(Pipe)
需求场景:将多个函数组合成一个函数(如中间件模式)。
实现方式:
// 从右向左执行(Redux风格)function compose(...fns) {return function(initialValue) {return fns.reduceRight((acc, fn) => fn(acc), initialValue);};}// 从左向右执行(Unix管道风格)function pipe(...fns) {return function(initialValue) {return fns.reduce((acc, fn) => fn(acc), initialValue);};}// 使用示例const add5 = x => x + 5;const multiply2 = x => x * 2;const divide3 = x => x / 3;const composed = compose(divide3, multiply2, add5);console.log(composed(10)); // ((10 + 5) * 2) / 3 = 10const piped = pipe(add5, multiply2, divide3);console.log(piped(10)); // (10 + 5) * 2 / 3 = 10
五、性能优化相关手写实现
9. 防抖(Debounce)与节流(Throttle)
需求场景:控制高频事件触发频率(如输入框联想、滚动事件)。
防抖实现:
function debounce(fn, delay) {let timer = null;return function(...args) {if (timer) clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);};}// 立即执行版function debounceImmediate(fn, delay) {let timer = null;return function(...args) {const callNow = !timer;if (timer) clearTimeout(timer);timer = setTimeout(() => {timer = null;if (!callNow) fn.apply(this, args);}, delay);if (callNow) fn.apply(this, args);};}
节流实现:
function throttle(fn, delay) {let lastTime = 0;return function(...args) {const now = Date.now();if (now - lastTime >= delay) {fn.apply(this, args);lastTime = now;}};}// 时间戳+定时器版(解决结尾丢弃问题)function throttleAdvanced(fn, delay) {let lastTime = 0;let timer = null;return function(...args) {const now = Date.now();const remaining = delay - (now - lastTime);if (remaining <= 0) {if (timer) {clearTimeout(timer);timer = null;}lastTime = now;fn.apply(this, args);} else if (!timer) {timer = setTimeout(() => {lastTime = Date.now();timer = null;fn.apply(this, args);}, remaining);}};}
10. 惰性函数(Lazy Function)
需求场景:延迟执行资源密集型操作,直到首次调用时才初始化。
实现模式:
function createLazyFunction(initFn, execFn) {let isInitialized = false;let result = null;return function(...args) {if (!isInitialized) {result = initFn();isInitialized = true;}return execFn.apply(this, [result, ...args]);};}// 使用示例:延迟加载大数组const heavyData = null;function loadData() {console.log('Loading data...');return new Array(1000000).fill(0).map((_, i) => i);}function processData(data, start, end) {return data.slice(start, end);}const lazyProcess = createLazyFunction(loadData, processData);console.log(lazyProcess(10, 20)); // 首次调用会加载数据console.log(lazyProcess(30, 40)); // 直接使用已加载的数据
总结与进阶建议
手写实现不仅是面试高频考点,更是提升代码质量的关键能力。建议开发者:
- 从简单到复杂:先实现基础功能(如深拷贝),再逐步攻克复杂逻辑(如Promise)
- 结合源码阅读:对比Lodash、RxJS等库的实现,学习优化技巧
- 编写测试用例:使用Jest等工具验证边界条件(如循环引用、异步错误)
- 性能基准测试:通过
performance.now()对比不同实现的耗时
掌握这些手写功能后,开发者将能更自信地处理复杂业务场景,甚至开发出自己的工具库。进阶方向可探索:实现简化版Vue/React、手写WebSocket长连接管理、设计模式的手动实现等。

发表评论
登录后可评论,请前往 登录 或 注册