logo

每日前端手写题--day1:从基础到进阶的实战演练

作者:carzy2025.09.19 12:47浏览量:0

简介:本文围绕"每日前端手写题--day1"主题,通过手写实现经典前端功能(如防抖节流、数组扁平化、深拷贝),结合理论解析与代码实践,帮助开发者提升编码能力与问题解决思维。

每日前端手写题—day1:从基础到进阶的实战演练

一、为什么需要”每日前端手写题”?

在前端开发领域,框架与工具的迭代速度极快,React、Vue、Angular等主流框架不断更新,TypeScript、Webpack等配套工具链持续演进。然而,无论技术栈如何变化,底层编码能力始终是区分开发者水平的核心指标。手写代码不仅能加深对语言特性的理解,更能培养解决复杂问题的思维模式。

以”每日前端手写题”为形式的练习,具有以下价值:

  1. 强化基础:通过重复实现常见功能(如防抖、节流、数组操作),巩固JavaScript/TypeScript核心语法。
  2. 提升调试能力:手写过程中暴露的边界问题(如循环引用、类型转换),能倒逼开发者完善测试用例。
  3. 优化代码风格:在无框架约束下,更关注变量命名、函数拆分、注释规范等细节。
  4. 模拟面试场景:许多前端面试题直接考察手写能力(如实现Promise、虚拟DOM),日常练习可降低临场压力。

二、Day1核心题目解析

题目1:实现防抖函数(debounce)

需求:输入框实时搜索时,避免每次输入都触发请求,需在用户停止输入后延迟300ms再发送请求。

基础实现

  1. function debounce(fn, delay) {
  2. let timer = null;
  3. return function(...args) {
  4. if (timer) clearTimeout(timer);
  5. timer = setTimeout(() => {
  6. fn.apply(this, args);
  7. }, delay);
  8. };
  9. }

关键点

  • 使用clearTimeout清除未执行的定时器。
  • 通过apply绑定上下文,确保this指向正确。
  • 参数传递使用剩余参数语法...args,兼容任意数量参数。

进阶优化

  1. 立即执行版:首次触发时立即执行,后续触发仍防抖。
    1. function debounce(fn, delay, immediate = false) {
    2. let timer = null;
    3. return function(...args) {
    4. if (timer) clearTimeout(timer);
    5. if (immediate && !timer) {
    6. fn.apply(this, args);
    7. }
    8. timer = setTimeout(() => {
    9. if (!immediate) fn.apply(this, args);
    10. timer = null;
    11. }, delay);
    12. };
    13. }
  2. 取消功能:添加cancel方法手动终止防抖。
    1. function debounce(fn, delay) {
    2. let timer = null;
    3. const debounced = function(...args) {
    4. if (timer) clearTimeout(timer);
    5. timer = setTimeout(() => fn.apply(this, args), delay);
    6. };
    7. debounced.cancel = () => clearTimeout(timer);
    8. return debounced;
    9. }

题目2:数组扁平化(flatten)

需求:将嵌套数组[1, [2, [3, 4]], 5]转换为[1, 2, 3, 4, 5]

方法1:递归实现

  1. function flatten(arr) {
  2. let result = [];
  3. for (const item of arr) {
  4. if (Array.isArray(item)) {
  5. result = result.concat(flatten(item));
  6. } else {
  7. result.push(item);
  8. }
  9. }
  10. return result;
  11. }

优点:逻辑清晰,兼容任意深度嵌套。
缺点:递归可能导致栈溢出(极端情况下)。

方法2:reduce+展开运算符

  1. function flatten(arr) {
  2. return arr.reduce((acc, val) =>
  3. acc.concat(Array.isArray(val) ? flatten(val) : val),
  4. []);
  5. }
  6. // 或使用ES6展开运算符
  7. function flatten(arr) {
  8. while (arr.some(item => Array.isArray(item))) {
  9. arr = [].concat(...arr);
  10. }
  11. return arr;
  12. }

性能对比:展开运算符版本在浅层嵌套时更快,递归版本在深层嵌套时更稳定。

题目3:深拷贝对象

需求:完整复制一个对象,包括嵌套对象、数组、日期等特殊类型。

基础实现

  1. function deepClone(obj, hash = new WeakMap()) {
  2. if (obj === null || typeof obj !== 'object') return obj;
  3. if (hash.has(obj)) return hash.get(obj); // 处理循环引用
  4. let clone;
  5. if (obj instanceof Date) {
  6. clone = new Date(obj);
  7. } else if (obj instanceof RegExp) {
  8. clone = new RegExp(obj);
  9. } else if (Array.isArray(obj)) {
  10. clone = [];
  11. } else {
  12. clone = Object.create(Object.getPrototypeOf(obj));
  13. }
  14. hash.set(obj, clone);
  15. for (const key in obj) {
  16. if (obj.hasOwnProperty(key)) {
  17. clone[key] = deepClone(obj[key], hash);
  18. }
  19. }
  20. return clone;
  21. }

关键处理

  • 使用WeakMap避免循环引用导致的无限递归。
  • 区分DateRegExpArray等特殊对象类型。
  • 通过Object.create保持原型链。

简化版(仅处理普通对象)

  1. function deepClone(obj) {
  2. return JSON.parse(JSON.stringify(obj));
  3. }

局限性

  • 无法处理函数、Symbol、循环引用。
  • 会丢失undefined和对象原型。

三、实践建议

  1. 每日一题:固定时间(如晨间15分钟)完成一道手写题,记录耗时与错误点。
  2. 对比优化:实现基础版本后,参考开源库(如Lodash)的源码,学习性能优化技巧。
  3. 场景扩展:为每个函数添加TypeScript类型定义,或编写单元测试(如Jest)。
  4. 建立题库:按类型分类(算法、工具函数、设计模式),形成个人知识体系。

四、总结

“每日前端手写题—day1”不仅是编码练习,更是思维训练。通过防抖、数组扁平化、深拷贝等经典题目的实现,开发者能深入理解闭包、递归、原型链等核心概念。坚持每日练习,将基础能力转化为解决复杂问题的自信,最终在技术面试与实际项目中脱颖而出。

相关文章推荐

发表评论