快乐编程之旅:了解这20道JavaScript手写题助力提升开发内力

简介: 快乐编程之旅:了解这20道JavaScript手写题助力提升开发内力

1. 实现阶乘函数

阶乘函数是一个常见的数学函数,用于计算一个非负整数的阶乘。阶乘表示从 1 到该整数之间所有整数的乘积。

以 n! 表示 n 的阶乘,其中 n 是一个非负整数。阶乘函数可以通过递归或循环来实现。

递归实现的阶乘函数如下:

function factorialRecursive(n) {
  if (n === 0 || n === 1) {
    return 1;
  }
  return n * factorialRecursive(n - 1);
}

这个函数首先检查输入的整数是否为 0 或 1,因为它们的阶乘都是 1。对于其他大于 1 的整数 n,阶乘等于 n 乘以 (n-1) 的阶乘。

循环实现的阶乘函数如下:

function factorialIterative(n) {
  let result = 1;
  for (let i = 2; i <= n; i++) {
    result *= i;
  }
  return result;
}

这个函数使用一个循环从 2 开始迭代乘法,直到乘到 n。在每次迭代中,将当前结果与当前的整数相乘,并将乘积赋给结果变量。

无论是递归还是循环实现,阶乘函数都可以有效地计算任意非负整数的阶乘。

2. 实现数组平均值计算函数:

function calculateAverage(arr) {
  let sum = 0;
  for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
  return sum / arr.length;
}

3. 实现数组去重函数:

function removeDuplicates(arr) {
  return arr.filter((value, index, self) => self.indexOf(value) === index);
}

4. 实现数组扁平化函数:

function flattenArray(arr) {
  return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flattenArray(val) : val), []);
}

数组扁平化是指将多维数组转换为一维数组的过程。在 JavaScript 中,我们可以使用递归或迭代的方式来实现数组的扁平化。

递归实现的数组扁平化函数如下:

function flattenRecursive(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flattenRecursive(arr[i]));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}

这个函数通过遍历数组中的每个元素,如果当前元素是数组,则递归调用自身将其扁平化,并使用 concat 方法将扁平化后的结果与 result 数组合并。如果当前元素不是数组,则直接将其添加到 result 数组中。

迭代实现的数组扁平化函数如下:

function flattenIterative(arr) {
  const stack = [...arr];
  const result = [];
  while (stack.length) {
    const curr = stack.pop();
    if (Array.isArray(curr)) {
      stack.push(...curr);
    } else {
      result.unshift(curr);
    }
  }
  return result;
}

这个函数使用一个堆栈数据结构来迭代地处理数组。我们首先将初始数组复制到堆栈中,然后循环执行以下操作:从堆栈中取出元素,如果当前元素是数组,则将其展开并推入堆栈中。如果当前元素不是数组,则将其添加到结果数组的开头。

这两种方法都可以将多维数组扁平化为一维数组。你可以根据自己的需求选择递归或迭代的方式来实现数组的扁平化。

5. 实现字符串反转函数:

function reverseString(str) {
  return str.split('').reverse().join('');
}

6. 实现对象深拷贝函数:

function deepCopy(obj) {
  return JSON.parse(JSON.stringify(obj));
}

7. 实现判断回文字符串函数:

function isPalindrome(str) {
  const reversed = str.split('').reverse().join('');
  return str === reversed;
}

8. 实现简单的节流函数:

节流函数在 JavaScript 中用于限制某个函数在一定时间间隔内执行的频率,以减少函数被过度触发的情况。通过节流函数,我们可以控制函数执行的频率,确保在指定的时间间隔内只执行一次。

以下是一个常见的节流函数的实现,使用了定时器来延迟函数的执行:

function throttle(func, delay) {
  let timerId;
  return function(...args) {
    if (!timerId) {
      func.apply(this, args);
      timerId = setTimeout(() => {
        timerId = null;
      }, delay);
    }
  };
}

这个函数接受两个参数:func 是需要进行节流处理的函数,delay 是指定的时间间隔(毫秒)。

节流函数返回了一个新的函数,该函数首先检查是否存在一个定时器ID。如果定时器ID不存在,则表示可以执行传入的函数,并设置一个定时器,在指定的延迟时间后将定时器ID清空,以便下次函数调用时可以再次执行。

这样,当函数被连续触发时,只有在指定的时间间隔过去后才会真正执行一次函数。这样可以有效地控制函数的触发频率,避免函数被过度调用。

示例用法:

function doSomething() {
  console.log("Doing something...");
}
const throttledFunc = throttle(doSomething, 1000);
// 在 1 秒内连续调用函数多次
throttledFunc(); // 函数被执行
throttledFunc(); // 函数被忽略
throttledFunc(); // 函数被忽略
// 1 秒后再次调用函数
setTimeout(() => {
  throttledFunc(); // 函数被执行
}, 1000);

在上面的示例中,doSomething 函数被传入 throttle 函数中,并使用 throttledFunc 作为节流处理后的函数。紧接着,连续调用了多次 throttledFunc,但在指定的时间间隔内,只有第一次触发时函数真正执行了。在 1 秒后,再次调用 throttledFunc,函数会再次被执行。

9. 实现数组排序函数:

function sortArray(arr) {
  return arr.sort((a, b) => a - b);
}

10. 实现判断两个数组是否相等函数:

function arraysAreEqual(arr1, arr2) {
  if (arr1.length !== arr2.length) {
    return false;
  }
  for (let i = 0; i < arr1.length; i++) {
    if (arr1[i] !== arr2[i]) {
      return false;
    }
  }
  return true;
}

11. 实现数组的map函数:

function myMap(arr, callback) {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(callback(arr[i], i, arr));
  }
  return result;
}

12. 实现数组的filter函数:

function myFilter(arr, callback) {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    if (callback(arr[i], i, arr)) {
      result.push(arr[i]);
    }
  }
  return result;
}

13. 实现数组的reduce函数:

function myReduce(arr, callback, initialValue) {
  let accumulator = initialValue !== undefined ? initialValue : arr[0];
  for (let i = initialValue !== undefined ? 0 : 1; i < arr.length; i++) {
    accumulator = callback(accumulator, arr[i], i, arr);
  }
  return accumulator;
}

14. 实现数组的forEach函数:

function myForEach(arr, callback) {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i], i, arr);
  }
}

15. 实现Promise的简单实现(仅包括resolve方法):

class MyPromise {
  constructor(executor) {
    this.value = undefined;
    this.state = 'pending';
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
      }
    };
    executor(resolve);
  }
  then(callback) {
    if (this.state === 'fulfilled') {
      callback(this.value);
    }
  }
}

16. 实现函数柯里化(Currying):

函数柯里化是一种将接受多个参数的函数转换为一系列接受单个参数的函数的过程。通过柯里化,我们可以将一个多参数函数转变为一个接受一个参数并返回一个新函数的函数链,每个新函数都接受下一个参数,并最终返回最终结果。

下面是一个示例,演示了如何实现函数柯里化:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function (...nextArgs) {
        return curried.apply(this, args.concat(nextArgs));
      };
    }
  };
}

这个 curry 函数接受一个多参数函数 fn 作为参数,并返回一个柯里化后的函数。在返回的柯里化函数中,我们首先检查传入的参数数量是否达到原始函数 fn 的参数数量。如果是,则直接调用原始函数并返回结果。否则,返回一个新的函数,该函数接受剩余的参数,并通过递归调用将参数不断累积起来,直到参数数量足够触发原始函数的执行。

以下是一个使用函数柯里化的示例:

function add(a, b, c) {
  return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 输出:6
console.log(curriedAdd(1, 2)(3)); // 输出:6
console.log(curriedAdd(1)(2, 3)); // 输出:6

在上面的示例中,我们使用 curry 函数将 add 函数柯里化为 curriedAdd。然后,我们可以通过连续调用返回的函数,每次传入一个参数,最终得到结果。无论是一次性传入所有参数,还是分多次传入参数,都可以正确计算出最终结果。

函数柯里化可以以简洁的方式处理多参数函数,并且在函数复用和参数传递方面提供了灵活性。它在函数式编程中被广泛使用和应用。

17. 实现自定义的事件触发器:

class EventEmitter {
  constructor() {
    this.events = {};
  }
  on(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }
  emit(eventName, ...args) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(callback => callback.apply(null, args));
    }
  }
}

18. 实现深度优先搜索算法(DFS):

function dfs(node, callback) {
  callback(node);
  node.children.forEach(child => dfs(child, callback));
}

19. 实现广度优先搜索算法(BFS):

function bfs(root, callback) {
  const queue = [root];
  while (queue.length > 0) {
    const node = queue.shift();
    callback(node);
    queue.push(...node.children);
  }
}

20. 实现一个简单的Event Bus:

class EventBus {
  constructor() {
    this.events = {};
  }
  on(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }
  emit(eventName, ...args) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(callback => callback.apply(null, args));
    }
  }
  off(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);
    }
  }
}


相关文章
|
2天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
【8月更文挑战第29天】Node.js,一个基于Chrome V8引擎的JavaScript运行环境,以其非阻塞I/O模型和事件驱动机制,在后端开发领域赢得了广泛关注。本文将从基本概念入手,通过实例讲解Node.js在后端开发中的应用,并分享一些实用的编程技巧。无论你是前端开发者还是后端新手,这篇文章都将为你打开一扇探索Node.js的大门。
|
4天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
【8月更文挑战第27天】本文将通过浅显易懂的方式,引导读者了解并掌握Node.js这一强大的后端开发工具。我们将从基础入手,逐步深入到Node.js的核心概念和应用,最后通过一个实际的项目示例来串联所有知识点,使读者能够在实际开发中运用Node.js解决复杂问题。
|
4天前
|
JavaScript 前端开发 编译器
解锁JavaScript模块化编程新纪元:从CommonJS的基石到ES Modules的飞跃,探索代码组织的艺术与科学
【8月更文挑战第27天】随着Web应用复杂度的提升,JavaScript模块化编程变得至关重要,它能有效降低代码耦合度并提高项目可维护性及扩展性。从CommonJS到ES Modules,模块化标准经历了显著的发展。CommonJS最初专为服务器端设计,通过`require()`同步加载模块。而ES Modules作为官方标准,支持异步加载,更适合浏览器环境,并且能够进行静态分析以优化性能。这两种标准各有特色,但ES Modules凭借其更广泛的跨平台兼容性和现代语法逐渐成为主流。这一演进不仅标志着JavaScript模块化的成熟,也反映了整个JavaScript生态系统的不断完善。
18 3
|
4天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的图像识别技术深入理解Node.js事件循环及其在后端开发中的应用
【8月更文挑战第27天】本文将介绍深度学习中的图像识别技术,包括其原理、应用领域及未来发展。我们将探讨如何通过神经网络实现图像识别,并分析其在医疗、交通等领域的应用。最后,我们将展望图像识别技术的发展前景。
|
3天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
【8月更文挑战第28天】本文以浅显易懂的语言,带你了解Node.js在后端开发中的应用。从Node.js的基本概念入手,逐步深入到实际的项目开发中,让你对Node.js有更深入的理解。无论你是初学者还是有一定基础的开发者,都能在这篇文章中获得新的启示。
|
4天前
|
JavaScript 前端开发 小程序
基于js开发快速学习鸿蒙基础
【8月更文挑战第26天】
16 1
|
1天前
|
JavaScript 开发者 UED
Vue.js 错误处理与调试:跟上技术潮流,摆脱开发困扰,成为代码大神不是梦!
【8月更文挑战第30天】在 Vue.js 开发中,错误处理与调试至关重要。本文将对比 Vue 的全局错误捕获机制 `Vue.config.errorHandler` 和组件内 `watch` 监听数据变化的方式,并介绍 Vue 开发者工具、控制台打印 (`console.log`) 以及代码断点 (`debugger`) 等调试方法。此外,还将探讨如何通过自定义错误页面提升用户体验。通过这些技巧的对比,帮助开发者灵活选择适合的策略,确保应用稳定性和开发效率。
|
1天前
|
JavaScript 前端开发 API
全栈开发革命来临!Vue.js与Node.js联手,打造前后端无缝对接的奇迹之作!
【8月更文挑战第30天】在Web开发领域,前后端分离与协作至关重要。Vue.js以其轻量级和易用性深受前端开发者喜爱,而Node.js则以高性能和事件驱动特性在后端领域崭露头角。二者结合开启了全栈开发新篇章,通过RESTful API或GraphQL实现高效稳定通信。本文以示例说明如何使用Vue.js和Node.js构建全栈应用,从前端Vue组件到后端Express服务器,展示了数据获取与展示的全过程。这种组合提供了一种高效简洁的全栈开发方案,使开发者能更专注于业务逻辑实现。
|
1天前
|
JavaScript 前端开发 开发者
哇塞!Vue.js 与 Web Components 携手,掀起前端组件复用风暴,震撼你的开发世界!
【8月更文挑战第30天】这段内容介绍了Vue.js和Web Components在前端开发中的优势及二者结合的可能性。Vue.js提供高效简洁的组件化开发,单个组件包含模板、脚本和样式,方便构建复杂用户界面。Web Components作为新兴技术标准,利用自定义元素、Shadow DOM等技术创建封装性强的自定义HTML元素,实现跨框架复用。结合二者,不仅增强了Web Components的逻辑和交互功能,还实现了Vue.js组件在不同框架中的复用,提高了开发效率和可维护性。未来前端开发中,这种结合将大有可为。
|
1天前
|
JavaScript 前端开发 UED
揭秘Vue.js高效开发:Vue Router如何让单页面应用路由管理变得如此简单?
【8月更文挑战第30天】随着Web应用复杂性的增加,单页面应用(SPA)因出色的用户体验和高效的页面加载性能而备受青睐。Vue.js凭借简洁的语法和灵活的组件系统成为构建SPA的热门选择,其官方路由管理器Vue Router则简化了路由管理。本文通过实战示例介绍如何利用Vue Router实现高效的SPA路由管理,包括命名路由、动态路由及其核心优势。
下一篇
云函数