比如防抖、节流、深度优先遍历和广度优先遍历
的实现,还有Promise、async/await
这些。
提到了数组扁平化、Localstorage缓存系统设计、ES6模板语法
。数组扁平化是一个常见的手写题,应该加入。缓存系统设计可能比较复杂,但作为设计题也是常考的,可以考虑。
编程题有红绿灯Promise实现、bind/call/apply、字符串压缩、new操作符、深拷贝
等。这些都是经典题目。比如实现bind或者new,深拷贝
这些,都是常见的手写题,应该包含进去。
比如事件循环、模块化发展历程
等,但手写代码可能较少。
提供了数组去重、快速排序
的实现,这两个都是基础算法题,适合手写。特别是快速排序的不同实现方式,可以作为一个点。
闭包应用、Promise实现、事件循环
解释等。其中手写Promise是一个高级题目,可以选。
二叉树遍历、对称二叉树、最大深度、有序数组转BST
等。这些算法题在前端面试中也常见,可以考虑加入,比如二叉树的遍历或者对称二叉树的判断。
调整后的列表可能包括:
- 数组扁平化
- 深拷贝
- 实现bind
- 手写Promise
- 快速排序
- 红绿灯交替(Promise)
- 二叉树遍历
- 字符串压缩
- 实现new
- 观察者模式或发布订阅模式实现
以下是2025年前端面试高频手撕代码题目及实现示例(含10个新题型,综合多个技术领域):
一、数组扁平化(多层级嵌套转一维数组)
javascript">// 递归实现(网页2、网页5)
function flatten(arr) {return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flatten(cur) : cur), []);
}
// ES6实现
const flattenES6 = arr => arr.flat(Infinity);
二、深拷贝实现(含循环引用处理)
javascript">// 支持对象/数组/日期/正则等类型(网页3、网页8)
function deepClone(target, map = new WeakMap()) {if (target instanceof Date) return new Date(target);if (target instanceof RegExp) return new RegExp(target);if (typeof target !== 'object' || target === null) return target;if (map.has(target)) return map.get(target);const cloneObj = new target.constructor();map.set(target, cloneObj);for (let key in target) {if (target.hasOwnProperty(key)) {cloneObj[key] = deepClone(target[key], map);}}return cloneObj;
}
三、实现bind方法
javascript">// 兼容new操作符(网页3)
Function.prototype.myBind = function(context, ...args) {const fn = this;return function Fn(...newArgs) {return fn.apply(this instanceof Fn ? this : context,args.concat(newArgs));};
};
四、手写Promise核心实现
javascript">// 含then链式调用(网页7)
class MyPromise {constructor(executor) {this.state = 'pending';this.value = undefined;this.onFulfilledCallbacks = [];const resolve = value => {if (this.state === 'pending') {this.state = 'fulfilled';this.value = value;this.onFulfilledCallbacks.forEach(fn => fn());}};executor(resolve);}then(onFulfilled) {return new MyPromise(resolve => {const wrapped = () => {const result = onFulfilled(this.value);resolve(result instanceof MyPromise ? result : new MyPromise(r => r(result)));};this.state === 'fulfilled' ? wrapped() : this.onFulfilledCallbacks.push(wrapped);});}
}
五、快速排序实现
javascript">// 原地排序版(网页5)
function quickSort(arr, left = 0, right = arr.length - 1) {if (left < right) {const pivot = partition(arr, left, right);quickSort(arr, left, pivot - 1);quickSort(arr, pivot + 1, right);}return arr;function partition(arr, low, high) {const pivot = arr[high];let i = low;for (let j = low; j < high; j++) {if (arr[j] < pivot) {[arr[i], arr[j]] = [arr[j], arr[i]];i++;}}[arr[i], arr[high]] = [arr[high], arr[i]];return i;}
}
六、Promise实现红绿灯控制
javascript">// 红绿黄交替(网页3)
function lightControl() {function light(color, duration) {return new Promise(resolve => {console.log(color);setTimeout(resolve, duration);});}async function run() {while(true) {await light('红', 3000);await light('绿', 2000);await light('黄', 1000);}}run();
}
七、二叉树前序遍历(非递归)
javascript">// 使用栈实现(网页9)
function preorderTraversal(root) {const stack = [], res = [];while(root || stack.length) {while(root) {res.push(root.val);stack.push(root);root = root.left;}root = stack.pop().right;}return res;
}
八、字符串压缩算法
javascript">// "aabcccccaaa" → "a2b1c5a3"(网页3)
function compressString(str) {let count = 1, res = '';for (let i = 0; i < str.length; i++) {if (str[i] === str[i+1]) {count++;} else {res += str[i] + count;count = 1;}}return res.length < str.length ? res : str;
}
九、实现new操作符
javascript">// 完整实现(网页3、网页4)
function myNew(constructor, ...args) {const obj = Object.create(constructor.prototype);const result = constructor.apply(obj, args);return result instanceof Object ? result : obj;
}
十、发布订阅模式实现
javascript">// 支持once/异步触发(网页3)
class EventEmitter {constructor() {this.events = new Map();}on(type, handler) {const handlers = this.events.get(type) || [];handlers.push(handler);this.events.set(type, handlers);}emit(type, ...args) {const handlers = this.events.get(type) || [];handlers.forEach(handler => handler(...args));}off(type, handler) {const handlers = this.events.get(type) || [];this.events.set(type, handlers.filter(h => h !== handler));}once(type, handler) {const wrapper = (...args) => {handler(...args);this.off(type, wrapper);};this.on(type, wrapper);}
}
以上题目覆盖了算法、设计模式、原生API实现、异步编程等核心领域,建议结合具体业务场景理解实现原理。如需完整测试用例或更多扩展实现(如虚拟DOM Diff算法、大文件分片上传等),可参考相关技术文档。
华为OD的题目列表,包括滑动窗口、贪心算法
等,可能适合提取一些新题。涉及大数相加、数组乱序、并发控制
等.网页9和10提到Promise源码、LRU
缓存,这些也是常见题目。
大数相加、大数相乘、数组乱序、对象扁平化
;有并发控制、Promise调度器、链式调用
;有LRU缓存
;
以下是2025年前端面试高频手撕代码题目及实现示例(基于最新技术趋势和面试题库整理):
一、LRU缓存实现(高频算法)
要求:实现一个支持get/put操作的LRU缓存,时间复杂度O(1)
javascript">class LRUCache {constructor(capacity) {this.cache = new Map();this.capacity = capacity;}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);this.cache.set(key, value);if (this.cache.size > this.capacity) {// 删除最久未使用的键(Map的迭代顺序保证)this.cache.delete(this.cache.keys().next().value);}}
}
二、大数相加(精度处理)
场景:解决JS精度丢失问题,如"9999999999999999" + “1”
javascript">function addStrings(num1, num2) {let i = num1.length - 1, j = num2.length - 1, carry = 0;let result = '';while (i >= 0 || j >= 0 || carry) {const n1 = i >= 0 ? num1[i--] - '0' : 0;const n2 = j >= 0 ? num2[j--] - '0' : 0;const sum = n1 + n2 + carry;result = (sum % 10) + result;carry = Math.floor(sum / 10);}return result.replace(/^0+/, '') || '0';
}
三、并发请求控制
要求:实现最多同时执行N个请求的调度器
javascript">class Scheduler {constructor(limit) {this.queue = [];this.running = 0;this.limit = limit;}addTask(task) {return new Promise(resolve => {const execute = () => {this.running++;task().then(resolve).finally(() => {this.running--;this.runNext();});};this.queue.push(execute);this.runNext();});}runNext() {if (this.running < this.limit && this.queue.length) {const task = this.queue.shift();task();}}
}
四、对象扁平化
场景:将嵌套对象转为单层结构
javascript">function flattenObject(obj, prefix = '') {return Object.keys(obj).reduce((acc, key) => {const pre = prefix ? `${prefix}.` : '';if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {Object.assign(acc, flattenObject(obj[key], pre + key));} else {acc[pre + key] = obj[key];}return acc;}, {});
}
// 示例输入:{a: {b: {c: 1}, d: 2}} → {'a.b.c': 1, 'a.d': 2}
五、Lazy链式调用
场景:实现LazyMan('Tony').sleep(10).eat('lunch')
的链式调用
javascript">class LazyMan {constructor(name) {this.tasks = [];console.log(`Hi I'm ${name}`);setTimeout(() => this.run(), 0);}next() {this.tasks.length && this.tasks.shift()();}run() {this.next();}sleep(sec) {this.tasks.push(() => {setTimeout(() => {console.log(`等待了${sec}秒`);this.next();}, sec * 1000);});return this;}eat(food) {this.tasks.push(() => {console.log(`吃了${food}`);this.next();});return this;}
}
六、手写Promise核心
重点:实现基本状态管理和then链式调用
javascript">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 => this.handle(cb));};try {executor(resolve);} catch (e) {reject(e);}}then(onFulfilled) {return new MyPromise(resolve => {this.handle({onFulfilled: onFulfilled || (value => value),resolve});});}handle(callback) {if (this.state === 'pending') {this.callbacks.push(callback);return;}setTimeout(() => {try {const result = callback.onFulfilled(this.value);callback.resolve(result);} catch (e) {callback.reject(e);}}, 0);}
}
七、节流与防抖
高频优化场景:滚动事件/输入框优化
javascript">// 节流(时间窗口内只执行一次)
function throttle(fn, delay) {let last = 0;return function(...args) {const now = Date.now();if (now - last >= delay) {fn.apply(this, args);last = now;}};
}// 防抖(最后一次操作后执行)
function debounce(fn, delay) {let timer = null;return function(...args) {clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);};
}
八、数组乱序(Fisher-Yates算法)
重点:实现真正的随机排序
javascript">function shuffle(arr) {for (let i = arr.length - 1; i > 0; i--) {const j = Math.floor(Math.random() * (i + 1));[arr[i], arr[j]] = [arr[j], arr[i]];}return arr;
}
九、浅拷贝实现
基础考点:区分深浅拷贝差异
javascript">function shallowClone(obj) {if (typeof obj !== 'object' || obj === null) return obj;return Array.isArray(obj) ? [...obj] : { ...obj };
}
十、观察者模式
设计模式考点:实现事件订阅机制
javascript">class Observer {constructor() {this.events = new Map();}on(event, fn) {const handlers = this.events.get(event) || [];handlers.push(fn);this.events.set(event, handlers);}emit(event, ...args) {const handlers = this.events.get(event) || [];handlers.forEach(fn => fn(...args));}off(event, fn) {const handlers = this.events.get(event) || [];this.events.set(event, handlers.filter(h => h !== fn));}
}
建议结合具体业务场景理解实现原理,这些题目覆盖了算法、设计模式、框架原理等核心领域。如需完整测试用例或更多扩展实现,可参考相关技术文档。
以下是对虚拟DOM Diff算法、大文件分片上传、Web Worker通信机制和Canvas图形绘制的详解及代码示例:
一、虚拟DOM Diff算法实现
核心原理(结合网页)
Diff算法通过双端比较策略和key标识优化实现高效节点对比:
- 层级比较:仅对比同层级节点,避免跨层级遍历(复杂度从O(n³)降为O(n))
- 双指针策略:通过新旧子节点列表的头部和尾部指针,快速定位可复用节点
- Key优化:通过唯一key值建立节点映射表,避免顺序错乱导致冗余操作
节点对比实现代码
javascript">function diff(oldVNode, newVNode) {// 1. 类型不同直接替换if (oldVNode.tag !== newVNode.tag) {replaceNode(oldVNode, newVNode);return;}// 2. 比较属性(网页1)const propsPatches = diffProps(oldVNode.props, newVNode.props);if (propsPatches.length) updateProps(oldVNode, propsPatches);// 3. 比较子节点(网页3)if (oldVNode.children && newVNode.children) {diffChildren(oldVNode.children, newVNode.children);}
}function diffChildren(oldChildren, newChildren) {let oldStartIdx = 0, newStartIdx = 0;let oldEndIdx = oldChildren.length - 1;let newEndIdx = newChildren.length - 1;while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {// 双端比较逻辑(网页4)if (sameVNode(oldChildren[oldStartIdx], newChildren[newStartIdx])) {patch(oldChildren[oldStartIdx], newChildren[newStartIdx]);oldStartIdx++;newStartIdx++;} else if (sameVNode(oldChildren[oldEndIdx], newChildren[newEndIdx])) {// ...尾部对比逻辑}}
}
二、大文件分片上传实现(断点续传)
核心流程(网页)
- 文件切片:将文件按固定大小(如5MB)分割
- 哈希生成:使用SparkMD5等库生成文件唯一标识
- 分片上传:并发控制上传切片(通常限制5个并发)
- 断点续传:通过本地存储记录已上传切片
- 服务端合并:所有切片上传完成后触发合并请求
前端实现代码
javascript">// 文件切片(网页5)
function sliceFile(file, chunkSize = 5 * 1024 * 1024) {const chunks = [];let start = 0;while (start < file.size) {chunks.push(file.slice(start, start + chunkSize));start += chunkSize;}return chunks;
}// 分片上传(网页6)
async function uploadChunks(fileHash, chunks) {const maxConcurrent = 5; // 最大并发数const uploaded = await checkUploadedChunks(fileHash);for (let i = 0; i < chunks.length; i += maxConcurrent) {await Promise.all(chunks.slice(i, i + maxConcurrent).map(async (chunk, index) => {if (!uploaded.includes(i + index)) {const formData = new FormData();formData.append('chunk', chunk);formData.append('hash', `${fileHash}-${i + index}`);await fetch('/upload', { method: 'POST', body: formData });}}));}await mergeChunks(fileHash, file.name);
}
三、Web Worker通信机制
主线程与Worker交互(网页)
- 线程创建:
javascript">// 主线程
const worker = new Worker('worker.js');
worker.postMessage({ type: 'CALC', data: [1,2,3] });// Worker线程(worker.js)
self.onmessage = (e) => {if (e.data.type === 'CALC') {const result = e.data.data.reduce((a, b) => a + b, 0);self.postMessage({ type: 'RESULT', result });}
};
- 数据传输限制:
• 采用结构化克隆算法(支持除DOM、Function外的多数类型)
• 大数据量建议使用Transferable Objects减少拷贝开销:
javascript">// 主线程发送二进制数据
const buffer = new ArrayBuffer(1024);
worker.postMessage(buffer, [buffer]); // 转移所有权
- 错误处理:
javascript">worker.onerror = (err) => {console.error('Worker错误:', err.message);
};
四、Canvas画板功能实现
核心功能实现(网页)
<canvas id="drawboard" width="800" height="600"></canvas>
<button onclick="javascript language-javascript">clearCanvas()">清空</button><script>javascript">
const canvas = document.getElementById('drawboard');
const ctx = canvas.getContext('2d');
let isDrawing = false;
let lastX = 0;
let lastY = 0;// 画笔设置
ctx.strokeStyle = '#000';
ctx.lineWidth = 5;
ctx.lineCap = 'round';// 事件监听
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', endDrawing);function startDrawing(e) {isDrawing = true;[lastX, lastY] = [e.offsetX, e.offsetY];
}function draw(e) {if (!isDrawing) return;ctx.beginPath();ctx.moveTo(lastX, lastY);ctx.lineTo(e.offsetX, e.offsetY);ctx.stroke();[lastX, lastY] = [e.offsetX, e.offsetY];
}function endDrawing() {isDrawing = false;
}function clearCanvas() {ctx.clearRect(0, 0, canvas.width, canvas.height);
}
</script>
高级功能扩展
- 笔刷类型:通过修改
ctx.globalCompositeOperation
实现橡皮擦 - 历史记录:使用
ctx.getImageData
保存画布状态 - 移动端适配:添加触摸事件监听(
touchstart/touchmove
)
以上实现方案综合了虚拟DOM的高效更新策略、大文件处理的可靠性方案、多线程通信机制以及Canvas的底层绘图能力,建议根据具体业务场景调整参数和功能扩展。