# JS 基础与手写实现 ## 1. 手写 Promise > **场景**:理解异步编程核心机制,处理 Ajax 请求、文件读取、定时器等异步操作的链式调用和错误处理。 > **解决**:回调地狱问题,提供优雅的异步流程控制。 ```js class MyPromise { constructor(executor) { this.state = 'pending'; // 初始状态为 pending this.value = undefined; // 存储成功值或失败原因 this.callbacks = []; // 存储 then 注册的回调函数 // 成功时调用,将状态改为 fulfilled const resolve = (value) => { if (this.state !== 'pending') return; // 状态只能改变一次 this.state = 'fulfilled'; this.value = value; this.callbacks.forEach(cb => cb.onFulfilled(value)); // 执行所有成功回调 }; // 失败时调用,将状态改为 rejected const reject = (reason) => { if (this.state !== 'pending') return; this.state = 'rejected'; this.value = reason; this.callbacks.forEach(cb => cb.onRejected(reason)); // 执行所有失败回调 }; // 立即执行 executor,捕获异常则 reject try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { // 返回新 Promise 实现链式调用 return new MyPromise((resolve, reject) => { const handle = (callback, fallback) => { try { const result = (callback || fallback)(this.value); // 执行回调 // 如果返回值是 Promise,等待其完成 result instanceof MyPromise ? result.then(resolve, reject) : resolve(result); } catch (e) { reject(e); } // 回调报错则 reject }; // 根据当前状态决定立即执行还是存储回调 if (this.state === 'fulfilled') handle(onFulfilled, v => v); else if (this.state === 'rejected') handle(onRejected, e => { throw e; }); else this.callbacks.push({ // pending 状态先存储回调 onFulfilled: () => handle(onFulfilled, v => v), onRejected: () => handle(onRejected, e => { throw e; }) }); }); } } ``` ## 2. Promise.all / Promise.race > **场景**:`Promise.all` 用于并行请求多个接口后统一处理(如同时加载用户信息和订单列表);`Promise.race` 用于请求超时控制、竞速取最快响应。 > **解决**:多异步任务协调与超时兜底问题。 ```js // Promise.all - 所有成功才成功,一个失败则立即失败 Promise.myAll = (promises) => { return new Promise((resolve, reject) => { const results = []; // 存储所有结果 let count = 0; // 记录完成数量 promises.forEach((p, i) => { Promise.resolve(p).then(val => { results[i] = val; // 按索引存储,保证顺序 if (++count === promises.length) resolve(results); // 全部完成则 resolve }, reject); // 任一失败立即 reject }); }); }; // Promise.race - 第一个完成就返回(无论成功失败) Promise.myRace = (promises) => { return new Promise((resolve, reject) => { // 第一个 resolve/reject 的结果会被采纳,后续的会被忽略 promises.forEach(p => Promise.resolve(p).then(resolve, reject)); }); }; ``` ## 3. 防抖与节流 > **场景**:防抖用于搜索框输入(停止输入后才请求)、窗口 resize 结束后计算布局;节流用于滚动事件监听、按钮防重复点击。 > **解决**:高频事件触发导致的性能问题和重复请求。 ```js // 防抖:停止触发后才执行(适合搜索框输入) function debounce(fn, delay) { let timer = null; // 保存定时器 ID return function(...args) { clearTimeout(timer); // 每次触发都清除上一个定时器 timer = setTimeout(() => fn.apply(this, args), delay); // 重新设定定时器 }; } // 节流:固定间隔执行(适合滚动事件) function throttle(fn, delay) { let last = 0; // 记录上次执行时间 return function(...args) { const now = Date.now(); if (now - last >= delay) { // 距离上次执行超过 delay 才执行 last = now; fn.apply(this, args); } }; } ``` ## 4. 深拷贝(处理循环引用) > **场景**:Redux/Vuex 状态管理中复制 state、表单数据备份与重置、避免修改原始数据造成副作用。 > **解决**:引用类型浅拷贝导致的数据污染问题,特别是循环引用场景。 ```js 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); // 处理循环引用:如果已拷贝过,直接返回缓存 if (map.has(obj)) return map.get(obj); // 创建新对象/数组 const clone = Array.isArray(obj) ? [] : {}; map.set(obj, clone); // 先存入 map,防止循环引用 // 递归拷贝每个属性 for (const key in obj) { if (obj.hasOwnProperty(key)) { // 只拷贝自身属性 clone[key] = deepClone(obj[key], map); } } return clone; } ``` ## 5. 函数柯里化 > **场景**:参数复用(如日志函数固定模块名)、延迟执行、函数式编程中的组合与管道操作。 > **解决**:减少重复传参,提高函数复用性和可读性。 ```js function curry(fn) { return function curried(...args) { // 如果参数数量足够,直接执行原函数 if (args.length >= fn.length) { return fn.apply(this, args); } // 参数不够,返回新函数继续收集参数 return (...nextArgs) => curried(...args, ...nextArgs); }; } // 使用示例 const add = (a, b, c) => a + b + c; const curriedAdd = curry(add); curriedAdd(1)(2)(3); // 6 - 每次传一个参数 curriedAdd(1, 2)(3); // 6 - 也可以一次传多个 ``` ## 6. call / apply / bind 实现 > **场景**:改变函数执行上下文(如借用数组方法处理类数组)、React 类组件中绑定事件处理函数的 this。 > **解决**:this 指向问题,实现函数借用和预设参数。 ```js // call - 立即执行,参数逐个传入 Function.prototype.myCall = function(ctx, ...args) { ctx = ctx || window; // ctx 为 null/undefined 时指向 window const key = Symbol(); // 用 Symbol 避免属性名冲突 ctx[key] = this; // 将函数作为 ctx 的方法 const result = ctx[key](...args); // 调用,此时 this 指向 ctx delete ctx[key]; // 清理临时属性 return result; }; // apply - 立即执行,参数以数组传入 Function.prototype.myApply = function(ctx, args = []) { ctx = ctx || window; const key = Symbol(); ctx[key] = this; const result = ctx[key](...args); // 展开数组参数 delete ctx[key]; return result; }; // bind - 返回新函数,不立即执行 Function.prototype.myBind = function(ctx, ...args) { const fn = this; // 保存原函数 return function(...newArgs) { // 合并预设参数和新参数 return fn.apply(ctx, [...args, ...newArgs]); }; }; ``` ## 7. 事件总线 EventEmitter > **场景**:组件间通信(如 Vue 的 EventBus)、插件系统、微前端应用间消息传递、解耦模块依赖。 > **解决**:发布-订阅模式实现松耦合的事件驱动架构。 ```js class EventEmitter { constructor() { this.events = {}; // 存储事件名 -> 监听器数组的映射 } // 订阅事件 on(event, listener) { (this.events[event] ||= []).push(listener); // 添加监听器 return this; // 返回 this 支持链式调用 } // 取消订阅 off(event, listener) { if (this.events[event]) { // 过滤掉指定的监听器 this.events[event] = this.events[event].filter(l => l !== listener); } return this; } // 触发事件 emit(event, ...args) { // 执行该事件的所有监听器 (this.events[event] || []).forEach(listener => listener(...args)); return this; } // 一次性订阅,触发后自动取消 once(event, listener) { const wrapper = (...args) => { listener(...args); // 执行原始监听器 this.off(event, wrapper); // 立即取消订阅 }; return this.on(event, wrapper); } } ```