返回

XDM,JS 如何函数式编程?看完你就明白了!(四)

前端

XDM,JS 如何函数式编程?看完你就明白了!(四)

不知不觉,我们已经来到了《JS 如何函数式编程》系列的【第四篇】。本篇会将这个蓝图再具象一下,谈谈函数编程中一个很重要的细节 —— “副作用”!

一、函数式编程与副作用

在函数式编程中,我们经常强调“纯函数”的概念。所谓纯函数,就是指一个函数不会对外部产生任何影响,其返回值只取决于函数的参数,而不会依赖于任何外部状态或变量。

与纯函数相对的,就是“不纯函数”,也称为“有副作用的函数”。副作用是指函数在执行过程中对外部产生了一些影响,比如修改了外部变量的值、产生了新的输出等。

二、副作用的成因和影响

副作用的成因有很多,最常见的是以下几种:

  • 修改外部变量的值: 比如,以下代码中的 increment 函数就会修改外部变量 count 的值:
function increment() {
  count++;
}
  • 产生新的输出: 比如,以下代码中的 print 函数就会产生新的输出:
function print(message) {
  console.log(message);
}
  • 引发异常: 比如,以下代码中的 divide 函数就会引发异常:
function divide(a, b) {
  if (b === 0) {
    throw new Error("Cannot divide by zero");
  }
  return a / b;
}

副作用会导致代码的可预测性和可维护性降低。因为当我们调用一个有副作用的函数时,我们需要考虑它对外部产生的影响,这会增加代码的复杂度和理解难度。

三、如何控制和消除副作用

为了控制和消除副作用,我们可以采用以下几种方法:

  • 柯里化: 柯里化可以将一个多参数的函数转换为一个一系列单参数的函数,从而减少函数的参数个数,降低函数的复杂度。
const add = (a, b) => a + b;

const add1 = curry(add);

add1(1)(2) // 3
  • 高阶函数: 高阶函数可以将函数作为参数或返回值,从而使我们可以对函数进行组合和操作,从而减少函数的嵌套层次,提高代码的可读性和可维护性。
const map = (fn, list) => {
  const result = [];
  for (let i = 0; i < list.length; i++) {
    result.push(fn(list[i]));
  }
  return result;
};

const numbers = [1, 2, 3, 4, 5];

const doubledNumbers = map((x) => x * 2, numbers);

console.log(doubledNumbers); // [2, 4, 6, 8, 10]
  • 不可变数据结构: 不可变数据结构是指一旦创建后就不能再被修改的数据结构。使用不可变数据结构可以避免副作用,因为当我们对不可变数据结构进行操作时,不会对原数据结构产生影响,从而保证了代码的安全性。
const list = [1, 2, 3, 4, 5];

const newList = list.filter((x) => x % 2 === 0);

console.log(list); // [1, 2, 3, 4, 5]
console.log(newList); // [2, 4]

四、结束语

副作用是函数式编程中一个很重要的细节,它会对代码的可预测性和可维护性产生很大的影响。通过理解副作用的成因和影响,并采用柯里化、高阶函数和不可变数据结构等方法来控制和消除副作用,我们可以编写出更可靠、更可维护的代码。