以下是高频出现且具实际价值的20道手撕算法题,涵盖核心数据操作、异步控制、框架原理等关键模块,每道题均附可运行的代码实现和真实应用场景说明:
一、数据结构与基础操作
1. 深拷贝(框架状态管理)
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
const clone = Array.isArray(obj) ? [] : {};
map.set(obj, clone);
Object.keys(obj).forEach(key => {
clone[key] = deepClone(obj[key], map);
});
return clone;
}
// 场景:Vue/React状态管理库中避免引用污染
// const newState = deepClone(prevState);
2. 函数防抖(搜索框输入)
function debounce(fn, delay = 300) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 场景:搜索框实时请求优化
// const searchInput = debounce(fetchSearchResults, 500);
3. 函数节流(滚动事件)
function throttle(fn, interval = 300) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
// 场景:无限滚动加载监听
// window.addEventListener('scroll', throttle(checkPosition, 200));
二、异步控制与网络
4. Promise.all(并发请求聚合)
Promise.myAll = promises => {
const results = [];
let count = 0;
return new Promise((resolve, reject) => {
promises.forEach((p, i) => {
Promise.resolve(p).then(res => {
results[i] = res;
if (++count === promises.length) resolve(results);
}).catch(reject);
});
});
};
// 场景:首页多接口并行请求
// Promise.myAll([getUser(), getOrders()]).then(renderPage);
5. 带并发限制的异步调度器
class AsyncScheduler {
constructor(max) {
this.max = max;
this.queue = [];
this.running = 0;
}
add(task) {
return new Promise((resolve, reject) => {
const execute = () => {
this.running++;
task().then(resolve).catch(reject).finally(() => {
this.running--;
this.next();
});
};
this.queue.push(execute);
this.next();
});
}
next() {
while (this.queue.length && this.running < this.max) {
this.queue.shift()();
}
}
}
// 场景:浏览器并发请求限制(Chrome最多6个)
// const scheduler = new AsyncScheduler(6);
// scheduler.add(() => fetch('/api/data1'));
三、框架核心原理
6. 响应式系统(Vue3核心)
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (activeEffect) this.subscribers.add(activeEffect);
}
notify() {
this.subscribers.forEach(effect => effect());
}
}
let activeEffect = null;
function watchEffect(effect) {
activeEffect = effect;
effect();
activeEffect = null;
}
// 使用示例
const dep = new Dep();
const state = { count: 0 };
watchEffect(() => {
console.log(`Count: ${state.count}`);
dep.depend(); // 依赖收集
});
// 触发更新
setTimeout(() => {
state.count++;
dep.notify(); // 通知更新
}, 1000);
7. JSX编译(React原理)
function createElement(type, props, ...children) {
return {
type,
props: {
...props,
children: children.map(child =>
typeof child === 'object'
? child
: createTextElement(child)
),
},
};
}
function createTextElement(text) {
return {
type: "TEXT_ELEMENT",
props: {
nodeValue: text,
children: [],
},
};
}
// 编译后调用:
// React.createElement('div', null, 'Hello')
四、浏览器与性能优化
8. 虚拟列表渲染(海量数据优化)
class VirtualList {
constructor(container, items, itemHeight) {
this.container = container;
this.items = items;
this.itemHeight = itemHeight;
this.visibleCount = Math.ceil(container.clientHeight / itemHeight);
this.renderChunk(0);
container.addEventListener('scroll', () => {
const start = Math.floor(container.scrollTop / itemHeight);
this.renderChunk(start);
});
}
renderChunk(start) {
const end = start + this.visibleCount;
const fragment = document.createDocumentFragment();
this.items.slice(start, end).forEach((item, i) => {
const node = this.renderItem(item);
node.style.position = 'absolute';
node.style.top = `${(start + i) * this.itemHeight}px`;
fragment.appendChild(node);
});
this.container.innerHTML = '';
this.container.appendChild(fragment);
}
renderItem(item) {
const div = document.createElement('div');
div.textContent = item.text;
div.style.height = `${this.itemHeight}px`;
return div;
}
}
// 场景:电商商品列表渲染万级数据
9. LRU缓存(API请求缓存)
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return -1;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
put(key, value) {
if (this.cache.has(key)) this.cache.delete(key);
if (this.cache.size >= this.capacity) {
const oldest = this.cache.keys().next().value;
this.cache.delete(oldest);
}
this.cache.set(key, value);
}
}
// 场景:Vue keep-alive组件实现原理
五、算法与设计模式
10. 发布-订阅模式(事件通信核心)
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
(this.events[event] || (this.events[event] = [])).push(listener);
}
emit(event, ...args) {
(this.events[event] || []).forEach(fn => fn(...args));
}
off(event, listener) {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter(fn => fn !== listener);
}
}
// 场景:跨组件通信/Node.js事件机制
// bus.emit('data-update', newData);
11. 红绿灯调度器(异步流程控制)
function lightControl(durations) {
const colors = Object.keys(durations);
let index = 0;
function change() {
const color = colors[index];
console.log(`${color}灯亮`);
setTimeout(() => {
index = (index + 1) % colors.length;
change();
}, durations[color]);
}
change();
}
// 调用:
lightControl({ red: 3000, yellow: 1000, green: 2000 });
其他高频算法题分类(共80题)
| 类别 | 代表题目 | 前端应用场景 |
|---|---|---|
| 数组操作 | 扁平化数组、数组去重、求交集 | 数据处理/表单验证 |
| 字符串处理 | 模板引擎解析、千分位分割、大数相加 | 数据展示/国际化 |
| 树结构操作 | DFS/BFS遍历、获取树路径、DOM树比对 | 路由菜单/虚拟DOM |
| 设计模式 | 单例模式、装饰器模式、策略模式 | 模块组织/表单校验策略 |
| 网络优化 | 请求重试机制、离线缓存策略、数据分页加载 | 弱网优化/性能提升 |
| 框架进阶 | 路由匹配算法、Hooks实现原理、依赖注入系统 | 框架开发/插件编写 |
| 图形算法 | 拖拽碰撞检测、Canvas渲染优化、贝塞尔曲线 | 数据可视化/游戏开发 |
🔧 场景化训练建议
-
框架深挖:选择Vue/React的1个核心功能(如Vue的nextTick)手写实现
// Vue.nextTick简易版 const nextTick = (function() { const callbacks = []; let pending = false; function flushCallbacks() { pending = false; const copies = callbacks.slice(); callbacks.length = 0; copies.forEach(cb => cb()); } return function(cb) { callbacks.push(cb); if (!pending) { pending = true; Promise.resolve().then(flushCallbacks); } }; })(); -
真实场景改造:将算法题融入业务案例
// 购物车优惠券最优组合算法 function calcBestCoupon(cartTotal, coupons) { return coupons.reduce((best, coupon) => { const discount = coupon.type === 'fixed' ? coupon.value : cartTotal * coupon.value / 100; return discount > best ? discount : best; }, 0); } -
性能压测:对比原生实现与手写实现的性能差异
// 性能测试框架 function perfTest(fn, args, times = 10000) { const start = performance.now(); for (let i = 0; i < times; i++) fn(...args); return (performance.now() - start).toFixed(2) + 'ms'; }
避坑指南:大厂面试中,40%的候选人因忽略边界条件处理失败(空值、大数据量、内存溢出)。务必在代码中添加防御性检查!