每日前端手写题实战:深度解析Day2挑战
2025.09.19 12:47浏览量:0简介:本文聚焦"每日前端手写题--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/undefined
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 处理循环引用
if (hash.has(obj)) {
return hash.get(obj);
}
// 处理Date和RegExp
if (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基础,更能深入理解前端框架的底层原理。建议每天固定时间练习,逐步建立自己的前端工具库。
发表评论
登录后可评论,请前往 登录 或 注册