每日前端手写题实战:深度解析Day2挑战
2025.09.19 12:47浏览量:1简介:本文聚焦"每日前端手写题--day2",通过手写实现防抖函数、深拷贝算法及虚拟DOM树生成三大核心功能,结合代码示例与性能优化策略,助力开发者掌握前端底层原理。
每日前端手写题实战:深度解析Day2挑战
一、防抖函数(Debounce)的手写实现
防抖是前端性能优化的核心技能之一,尤其在处理高频事件(如窗口resize、输入框联想)时能显著减少不必要的计算。Day2的挑战要求我们实现一个带立即执行选项的防抖函数。
1.1 基础防抖实现原理
防抖的核心思想是:在事件触发后,等待N毫秒再执行回调。若在等待期间再次触发,则重新计时。
function debounce(func, delay) {let timer = null;return function(...args) {if (timer) clearTimeout(timer);timer = setTimeout(() => {func.apply(this, args);}, delay);};}
1.2 进阶版:支持立即执行
实际应用中常需要首次触发立即执行,后续触发才防抖。通过添加immediate参数实现:
function debounce(func, delay, immediate = false) {let timer = null;return function(...args) {const context = this;if (timer) clearTimeout(timer);if (immediate && !timer) {func.apply(context, args);}timer = setTimeout(() => {if (!immediate) {func.apply(context, args);}timer = null;}, delay);};}// 使用示例const inputHandler = debounce(() => {console.log('Input processed');}, 300, true);
1.3 性能优化要点
- 内存管理:确保在组件卸载时清除定时器(React中可在useEffect的cleanup函数中执行)
- 参数传递:使用剩余参数(…)和apply确保参数完整传递
- this绑定:通过保存context解决函数内this指向问题
二、深拷贝算法的递归实现
深拷贝是处理复杂数据结构的必备技能,Day2要求实现一个能处理循环引用的深拷贝函数。
2.1 基础递归实现
function deepClone(obj, hash = new WeakMap()) {// 处理基本类型和null/undefinedif (obj === null || typeof obj !== 'object') {return obj;}// 处理循环引用if (hash.has(obj)) {return hash.get(obj);}// 处理Date和RegExpif (obj instanceof Date) return new Date(obj);if (obj instanceof RegExp) return new RegExp(obj);// 创建对应类型的空对象const cloneObj = Array.isArray(obj) ? [] : {};hash.set(obj, cloneObj);// 递归拷贝属性for (let key in obj) {if (obj.hasOwnProperty(key)) {cloneObj[key] = deepClone(obj[key], hash);}}// 处理Symbol属性const symbolKeys = Object.getOwnPropertySymbols(obj);for (let symKey of symbolKeys) {cloneObj[symKey] = deepClone(obj[symKey], hash);}return cloneObj;}
2.2 关键技术点解析
- WeakMap应用:使用WeakMap存储已拷贝对象,解决循环引用问题
- 类型判断:通过
instanceof和typeof准确识别Date、RegExp等特殊对象 - Symbol属性处理:使用
Object.getOwnPropertySymbols获取Symbol键名 - 性能考虑:WeakMap的弱引用特性避免内存泄漏
2.3 测试用例设计
// 测试循环引用const obj = { a: 1 };obj.self = obj;const cloned = deepClone(obj);console.log(cloned.self === cloned); // true// 测试特殊对象const date = new Date();const clonedDate = deepClone(date);console.log(clonedDate instanceof Date); // true
三、虚拟DOM树生成与Diff算法基础
Day2的高级挑战要求实现一个简化版虚拟DOM生成器,这是理解现代框架(如React、Vue)的核心基础。
3.1 虚拟DOM节点结构
function createElement(type, props, ...children) {return {type,props: {...props,children: children.map(child =>typeof child === 'object' ? child : createTextVNode(child))}};}function createTextVNode(text) {return {type: 'TEXT',props: {nodeValue: text,children: []}};}
3.2 渲染函数实现
function render(vnode, container) {// 处理文本节点if (vnode.type === 'TEXT') {return container.appendChild(document.createTextNode(vnode.props.nodeValue));}// 创建DOM元素const dom = document.createElement(vnode.type);// 设置属性Object.keys(vnode.props).filter(key => key !== 'children').forEach(name => {dom[name] = vnode.props[name];});// 递归渲染子节点vnode.props.children.forEach(child =>render(child, dom));container.appendChild(dom);}
3.3 基础Diff算法实现
function diff(oldVNode, newVNode) {const patches = {};walk(oldVNode, newVNode, patches, 0);return patches;}function walk(oldVNode, newVNode, patches, index) {const currentPatch = [];// 节点替换if (!newVNode) {currentPatch.push({ type: 'REMOVE' });}// 文本节点更新else if (isText(oldVNode) && isText(newVNode)) {if (oldVNode.props.nodeValue !== newVNode.props.nodeValue) {currentPatch.push({ type: 'TEXT', content: newVNode.props.nodeValue });}}// 元素节点更新else if (oldVNode.type === newVNode.type) {// 属性更新const attrPatches = diffProps(oldVNode.props, newVNode.props);if (attrPatches.length) {currentPatch.push({ type: 'ATTR', attrs: attrPatches });}// 递归比较子节点diffChildren(oldVNode.props.children,newVNode.props.children,patches,index);}// 节点类型不同,完全替换else {currentPatch.push({ type: 'REPLACE', node: newVNode });}if (currentPatch.length) {patches[index] = currentPatch;}}
四、实战建议与进阶方向
- 测试驱动开发:为每个手写函数编写单元测试(使用Jest等框架)
- 性能基准测试:使用
console.time对比不同实现的执行时间 - TypeScript改造:为所有函数添加类型定义,提升代码健壮性
- 浏览器兼容性:特别注意Symbol、WeakMap等新特性的兼容方案
五、常见问题解决方案
- 防抖函数中的this丢失:始终通过
apply(this, args)保持上下文 - 深拷贝中的函数处理:明确需求决定是否拷贝函数(通常函数不拷贝)
- 虚拟DOM的key属性:为列表项添加唯一key提升Diff效率
通过Day2的这三个核心挑战,开发者不仅能巩固JavaScript基础,更能深入理解前端框架的底层原理。建议每天固定时间练习,逐步建立自己的前端工具库。

发表评论
登录后可评论,请前往 登录 或 注册