快乐编程之旅:了解这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);
    }
  }
}


相关文章
|
20天前
|
开发框架 JavaScript 安全
js开发:请解释什么是Express框架,以及它在项目中的作用。
Express是Node.js的Web开发框架,简化路由管理,支持HTTP请求处理。它采用中间件系统增强功能,如日志和错误处理,集成多种模板引擎(EJS、Jade、Pug)用于HTML渲染,并提供安全中间件提升应用安全性。其可扩展性允许选用合适插件扩展功能,加速开发进程。
|
20天前
|
缓存 JavaScript 前端开发
js开发:请解释什么是Webpack,以及它在项目中的作用。
Webpack是开源的JavaScript模块打包器,用于前端项目构建,整合并优化JavaScript、CSS、图片等资源。它实现模块打包、代码分割以提升加载速度,同时进行资源优化和缓存。借助插件机制扩展功能,并支持热更新,加速开发流程。
15 4
|
20天前
|
缓存 JavaScript 数据安全/隐私保护
js开发:请解释什么是ES6的Proxy,以及它的用途。
`ES6`的`Proxy`对象用于创建一个代理,能拦截并自定义目标对象的访问和操作,应用于数据绑定、访问控制、函数调用的拦截与修改以及异步操作处理。
17 3
|
20天前
|
JavaScript
js开发:请解释什么是ES6的类(class),并说明它与传统构造函数的区别。
ES6的类提供了一种更简洁的面向对象编程方式,对比传统的构造函数,具有更好的可读性和可维护性。类使用`class`定义,`constructor`定义构造方法,`extends`实现继承,并可直接定义静态方法。示例展示了如何创建`Person`类、`Student`子类以及它们的方法调用。
20 2
|
21天前
|
自然语言处理 JavaScript 网络架构
js开发:请解释什么是ES6的箭头函数,以及它与传统函数的区别。
ES6的箭头函数以`=&gt;`定义,简化了函数写法,具有简洁语法和词法作用域的`this`。它无`arguments`对象,不能用作构造函数,不支持`Generator`,且不改变`this`、`super`、`new.target`绑定。适用于简短表达式,常用于异步编程和高阶函数。
17 5
|
21天前
|
JavaScript 安全 前端开发
js开发:请解释什么是XSS攻击和CSRF攻击,并说明如何防范这些攻击。
XSS和CSRF是两种常见的Web安全威胁。XSS攻击通过注入恶意脚本盗取用户信息或控制账户,防范措施包括输入验证、内容编码、HTTPOnly Cookie和CSP。CSRF攻击则诱使用户执行未经授权操作,防范手段有CSRF Tokens、双重验证、Referer检查和SameSite Cookie属性。开发者应采取这些防御措施并定期进行安全审计以增强应用安全性。
18 0
|
21天前
|
JavaScript 前端开发 算法
js开发:请解释什么是虚拟DOM(virtual DOM),以及它在React中的应用。
虚拟DOM是React等前端框架的关键技术,它以轻量级JavaScript对象树形式抽象表示实际DOM。当状态改变,React不直接操作DOM,而是先构建新虚拟DOM树。通过高效diff算法比较新旧树,找到最小变更集,仅更新必要部分,提高DOM操作效率,降低性能损耗。虚拟DOM的抽象特性还支持跨平台应用,如React Native。总之,虚拟DOM优化了状态变化时的DOM更新,提升性能和用户体验。
19 0
|
20天前
|
Web App开发 JavaScript 前端开发
js开发:请解释什么是Node.js,以及它的应用场景。
Node.js是基于V8的JavaScript运行时,用于服务器端编程。它的事件驱动、非阻塞I/O模型使其在高并发实时应用中表现出色,如Web服务器、实时聊天、API服务、微服务、工具和跨平台桌面应用(使用Electron)。适用于高性能和实时需求场景。
18 4
|
20天前
|
JavaScript 前端开发 编译器
js开发: 请解释什么是Babel,以及它在项目中的作用。
**Babel是JavaScript编译器,将ES6+代码转为向后兼容版本,确保在旧环境运行。它在前端构建中不可或缺,提供语法转换、插件机制、灵活配置及丰富的生态系统,支持代码兼容性和自定义编译任务。**
17 6
|
20天前
|
JavaScript 前端开发
js开发:请解释什么是ES6的async/await,以及它如何解决回调地狱问题。
ES6的`async/await`是基于Promise的异步编程工具,能以同步风格编写异步代码,提高代码可读性。它缓解了回调地狱问题,通过将异步操作封装为Promise,避免回调嵌套。错误处理更直观,类似同步的try...catch。