前端函数式编程

88 阅读5分钟

函数式编程是一种编程范式,它强调函数的纯洁性和不可变性,避免副作用和共享状态。在前端开发中,有多种函数式编程的实现方式,下面是一些常见的函数式编程:

1:纯函数: 纯函数是指没有副作用(对外部状态没有影响)且对于相同的输入总是返回相同的结果的函数。它可以避免副作用和共享状态,使程序更容易理解和测试。

// 纯函数的定义是:相同的输入会产生相同的输出,并且没有副作用。
function add(x, y) { // 定义了一个函数 add,接收两个参数 x 和 y
  return x + y; // 返回 x 和 y 的和
}

2:高阶函数: 高阶函数是指接受一个或多个函数作为参数或返回一个函数的函数。它可以用来实现函数的组合和柯里化等功能,提高代码的复用性和可读性。

// 高阶函数的定义是:以函数作为参数或返回值的函数。
function double(fn) {
  // 返回一个新函数,该函数将传入的参数乘以 2 后再调用传入的函数。
  return function(x) {
    return fn(x * 2);
  };
}
function square(x) {
  return x * x;
}
// 创建一个新函数 doubleSquare,它将传入的参数先乘以 2,然后再求平方。
const doubleSquare = double(square);
console.log(doubleSquare(3)); // 36

3:函数组合:函数组合是指将多个函数组合成一个新的函数的过程。它可以避免回调函数的嵌套和提高代码的可读性。

// 函数组合的定义是:将多个函数组合起来,形成一个新函数。
function compose(...fns) {
  // 返回一个新函数,该函数从右到左依次调用传入的函数,将每个函数的结果作为下一个函数的参数。
  return function(x) {
    return fns.reduceRight((acc, fn) => fn(acc), x);
  };
}
function double(x) {
  return x * 2;
}
function square(x) {
  return x * x;
}
// 创建一个新函数 doubleThenSquare,它先将传入的参数乘以 2,然后再求平方。
const doubleThenSquare = compose(square, double);
console.log(doubleThenSquare(3)); // 36

4:柯里化: 柯里化是指将一个接受多个参数的函数转化为一系列接受单个参数的函数的过程。它可以简化函数的调用和组合,提高代码的可读性和复用性。

// 柯里化的定义是:将一个多参数函数转化为一系列单参数函数。
function add(x, y) {
  return x + y;
}
function curry(fn) {
  // 返回一个新函数,该函数接受任意数量的参数,如果参数个数达到原函数的参数个数,则调用原函数并返回结果,否则返回一个新函数。
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...moreArgs) {
        return curried.apply(this, args.concat(moreArgs));
      };
    }
  };
}
// 使用柯里化,将多参数函数转化为单参数函数。
const curriedAdd = curry(add);
console.log(curriedAdd(2)(3)); // 5
console.log(curriedAdd(23)); // 5

5:偏函数: 偏函数是指固定一个函数的一些参数,返回一个新的函数的过程。它可以将多个函数组合成一个新的函数,提高代码的可读性和复用性。

// 偏函数的定义是:固定一个函数的一些参数,返回一个新函数。
function add(x, y, z) {
  return x + y + z;
}
function partial(fn, ...args) {
  // 返回一个新函数,该函数接受意数量的参数,将传入的参数与预设的参数合并后调用原函数。
return function(...moreArgs) {
return fn.apply(this, args.concat(moreArgs));
};
}
// 使用偏函数,固定函数的第一个参数和第三个参数,返回一个新函数。
const partialAdd = partial(add, 13);
console.log(partialAdd(2)); // 6
console.log(partialAdd(4)); // 8

6:函数式组件:函数式组件是指基于函数式编程思想实现的React组件。它可以避免状态共享和副作用,提高组件的可复用性和可测试性。

import React, { useState } from "react"// 导入 React 和 useState Hook
function Counter({ initialCount }) { // 定义 Counter 组件,接收 initialCount 属性
  const [count, setCount] = useState(initialCount); // 定义 count 状态和更新 count 状态的函数 setCount,并使用 useState Hook 初始化状态
  const increment = () => setCount(count + 1); // 定义增加 count 值的函数
  const decrement = () => setCount(count - 1); // 定义减少 count 值的函数
  // 返回一个包含 h1 标签和两个按钮的 div 元素
  return (
    <div>
      <h1>{count}</h1> // 显示当前的 count 值
      <button onClick={increment}>+</button> // 点击按钮增加 count 值
      <button onClick={decrement}>-</button> // 点击按钮减少 count 值
    </div>
  );
}
export default Counter// 导出 Counter 组件供其他模块使用

7:RxJS: RxJS是一个函数响应式编程库,它提供了一系列函数式编程的API,可以实现异步数据流的处理和管理。

import { fromEvent } from "rxjs"// 导入 fromEvent 方法用于将 DOM 事件转换为可观察的 Observable 对象
import { map, filter } from "rxjs/operators"// 导入操作符 map 和 filter,用于转换和过滤 Observable 对象的数据流
const button = document.getElementById("myButton"); // 获取 id 为 myButton 的按钮元素
fromEvent(button, "click"// 将按钮的 click 事件转换为可观察的 Observable 对象
  .pipe// 使用 pipe 方法将操作符链接起来
    map(event => event.target), // 将事件转换为目标元素,并返回 Observable 对象
    filter(target => target.tagName === "BUTTON"// 过滤出标签名为 BUTTON 的元素,并返回 Observable 对象
  )
  .subscribe(target => console.log(target.textContent)); // 订阅 Observable 对象,并在控制台输出目标元素的文本内容

8. 函数节流:

// 函数节流的定义是:在一段时间内只能触发一次函数。
function throttle(fn, delay) {
  let timer = null;
  return function(...args) {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args);
        timer = null;
      }, delay);
    }
  };
}
// 创建一个新函数,使其只能在一秒钟内触发一次。
const throttled = throttle(() => console.log('Hello, world!'), 1000);
setInterval(throttled, 500); // 只会输出一次

9:函数防抖

// 函数防抖的定义是:在一段时间内多次触发函数,只执行最后一次。
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}
// 创建一个新函数,使其只执行最后一次调用。
const debounced = debounce(() => console.log('Hello, world!'), 1000);
setInterval(debounced, 500); // 不会输出
setTimeout(debounced, 1500); // 输出一次