每日前端手写题--day1:从基础到进阶的实战演练
2025.09.19 12:47浏览量:0简介:本文围绕"每日前端手写题--day1"主题,通过手写实现经典前端功能(如防抖节流、数组扁平化、深拷贝),结合理论解析与代码实践,帮助开发者提升编码能力与问题解决思维。
每日前端手写题—day1:从基础到进阶的实战演练
一、为什么需要”每日前端手写题”?
在前端开发领域,框架与工具的迭代速度极快,React、Vue、Angular等主流框架不断更新,TypeScript、Webpack等配套工具链持续演进。然而,无论技术栈如何变化,底层编码能力始终是区分开发者水平的核心指标。手写代码不仅能加深对语言特性的理解,更能培养解决复杂问题的思维模式。
以”每日前端手写题”为形式的练习,具有以下价值:
- 强化基础:通过重复实现常见功能(如防抖、节流、数组操作),巩固JavaScript/TypeScript核心语法。
- 提升调试能力:手写过程中暴露的边界问题(如循环引用、类型转换),能倒逼开发者完善测试用例。
- 优化代码风格:在无框架约束下,更关注变量命名、函数拆分、注释规范等细节。
- 模拟面试场景:许多前端面试题直接考察手写能力(如实现Promise、虚拟DOM),日常练习可降低临场压力。
二、Day1核心题目解析
题目1:实现防抖函数(debounce)
需求:输入框实时搜索时,避免每次输入都触发请求,需在用户停止输入后延迟300ms再发送请求。
基础实现
function debounce(fn, delay) {
let timer = null;
return function(...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
关键点:
- 使用
clearTimeout
清除未执行的定时器。 - 通过
apply
绑定上下文,确保this
指向正确。 - 参数传递使用剩余参数语法
...args
,兼容任意数量参数。
进阶优化
- 立即执行版:首次触发时立即执行,后续触发仍防抖。
function debounce(fn, delay, immediate = false) {
let timer = null;
return function(...args) {
if (timer) clearTimeout(timer);
if (immediate && !timer) {
fn.apply(this, args);
}
timer = setTimeout(() => {
if (!immediate) fn.apply(this, args);
timer = null;
}, delay);
};
}
- 取消功能:添加
cancel
方法手动终止防抖。function debounce(fn, delay) {
let timer = null;
const debounced = function(...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
debounced.cancel = () => clearTimeout(timer);
return debounced;
}
题目2:数组扁平化(flatten)
需求:将嵌套数组[1, [2, [3, 4]], 5]
转换为[1, 2, 3, 4, 5]
。
方法1:递归实现
function flatten(arr) {
let result = [];
for (const item of arr) {
if (Array.isArray(item)) {
result = result.concat(flatten(item));
} else {
result.push(item);
}
}
return result;
}
优点:逻辑清晰,兼容任意深度嵌套。
缺点:递归可能导致栈溢出(极端情况下)。
方法2:reduce+展开运算符
function flatten(arr) {
return arr.reduce((acc, val) =>
acc.concat(Array.isArray(val) ? flatten(val) : val),
[]);
}
// 或使用ES6展开运算符
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
性能对比:展开运算符版本在浅层嵌套时更快,递归版本在深层嵌套时更稳定。
题目3:深拷贝对象
需求:完整复制一个对象,包括嵌套对象、数组、日期等特殊类型。
基础实现
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj); // 处理循环引用
let clone;
if (obj instanceof Date) {
clone = new Date(obj);
} else if (obj instanceof RegExp) {
clone = new RegExp(obj);
} else if (Array.isArray(obj)) {
clone = [];
} else {
clone = Object.create(Object.getPrototypeOf(obj));
}
hash.set(obj, clone);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash);
}
}
return clone;
}
关键处理:
- 使用
WeakMap
避免循环引用导致的无限递归。 - 区分
Date
、RegExp
、Array
等特殊对象类型。 - 通过
Object.create
保持原型链。
简化版(仅处理普通对象)
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
局限性:
- 无法处理函数、Symbol、循环引用。
- 会丢失
undefined
和对象原型。
三、实践建议
- 每日一题:固定时间(如晨间15分钟)完成一道手写题,记录耗时与错误点。
- 对比优化:实现基础版本后,参考开源库(如Lodash)的源码,学习性能优化技巧。
- 场景扩展:为每个函数添加TypeScript类型定义,或编写单元测试(如Jest)。
- 建立题库:按类型分类(算法、工具函数、设计模式),形成个人知识体系。
四、总结
“每日前端手写题—day1”不仅是编码练习,更是思维训练。通过防抖、数组扁平化、深拷贝等经典题目的实现,开发者能深入理解闭包、递归、原型链等核心概念。坚持每日练习,将基础能力转化为解决复杂问题的自信,最终在技术面试与实际项目中脱颖而出。
发表评论
登录后可评论,请前往 登录 或 注册