2025前端面试核心手撕算法题精选

656 阅读3分钟

以下是高频出现且具实际价值的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渲染优化、贝塞尔曲线数据可视化/游戏开发

🔧 场景化训练建议

  1. 框架深挖:选择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);
        }
      };
    })();
    
  2. 真实场景改造:将算法题融入业务案例

    // 购物车优惠券最优组合算法
    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);
    }
    
  3. 性能压测:对比原生实现与手写实现的性能差异

    // 性能测试框架
    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%的候选人因忽略边界条件处理失败(空值、大数据量、内存溢出)。务必在代码中添加防御性检查!