6.0 KiB
6.0 KiB
JS 基础与手写实现
1. 手写 Promise
场景:理解异步编程核心机制,处理 Ajax 请求、文件读取、定时器等异步操作的链式调用和错误处理。 解决:回调地狱问题,提供优雅的异步流程控制。
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.callbacks = [];
const resolve = (value) => {
if (this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = value;
this.callbacks.forEach(cb => cb.onFulfilled(value));
};
const reject = (reason) => {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.value = reason;
this.callbacks.forEach(cb => cb.onRejected(reason));
};
try { executor(resolve, reject); } catch (e) { reject(e); }
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
const handle = (callback, fallback) => {
try {
const result = (callback || fallback)(this.value);
result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);
} catch (e) { reject(e); }
};
if (this.state === 'fulfilled') handle(onFulfilled, v => v);
else if (this.state === 'rejected') handle(onRejected, e => { throw e; });
else this.callbacks.push({
onFulfilled: () => handle(onFulfilled, v => v),
onRejected: () => handle(onRejected, e => { throw e; })
});
});
}
}
2. Promise.all / Promise.race
场景:
Promise.all用于并行请求多个接口后统一处理(如同时加载用户信息和订单列表);Promise.race用于请求超时控制、竞速取最快响应。 解决:多异步任务协调与超时兜底问题。
// 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);
}, reject);
});
});
};
// Promise.race - 第一个完成就返回
Promise.myRace = (promises) => {
return new Promise((resolve, reject) => {
promises.forEach(p => Promise.resolve(p).then(resolve, reject));
});
};
3. 防抖与节流
场景:防抖用于搜索框输入(停止输入后才请求)、窗口 resize 结束后计算布局;节流用于滚动事件监听、按钮防重复点击。 解决:高频事件触发导致的性能问题和重复请求。
// 防抖:停止触发后执行
function debounce(fn, delay) {
let timer = null;
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) {
last = now;
fn.apply(this, args);
}
};
}
4. 深拷贝(处理循环引用)
场景:Redux/Vuex 状态管理中复制 state、表单数据备份与重置、避免修改原始数据造成副作用。 解决:引用类型浅拷贝导致的数据污染问题,特别是循环引用场景。
function deepClone(obj, map = new WeakMap()) {
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);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], map);
}
}
return clone;
}
5. 函数柯里化
场景:参数复用(如日志函数固定模块名)、延迟执行、函数式编程中的组合与管道操作。 解决:减少重复传参,提高函数复用性和可读性。
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 指向问题,实现函数借用和预设参数。
// call
Function.prototype.myCall = function(ctx, ...args) {
ctx = ctx || window;
const key = Symbol();
ctx[key] = this;
const result = ctx[key](...args);
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)、插件系统、微前端应用间消息传递、解耦模块依赖。 解决:发布-订阅模式实现松耦合的事件驱动架构。
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
(this.events[event] ||= []).push(listener);
return 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);
}
}