面试官:说说你对函数式编程的理解

前言

面试中遇到一部分人对函数式编程不了解,还有一部分认为函数式编程除了面试中能用到,在自己的项目经验中很难看到具体的应用场景。

函数式编程作为一种编程范式由来已久。函数式编程强调不可变性和纯函数,即函数的输出仅由输入决定,没有副作用和对外部状态的依赖。这种特性使得函数式代码更易于理解、测试和调试。

函数式编程强调使用多个函数组合和来处理数据。其核心是将运算过程抽象成函数,好处是可以复用

本文将尝试用通俗易懂的方式讲解函数式编程在前端项目中的应用。会对函数式编程进行简单的“扫盲”,然后结合几个实例方便大家理解。

认识函数式编程

在开始之前,请先看一个函数式编程的实例。

// 纯函数,只做一件事,输入输出,没有副作用
function double(number) {
  return number * 2;
}

function square(number) {
  return number * number;
}

// 不改变原来的数据,创建新的数组
function doubleArray(arr) {
  return arr.map(double);
}

function squareArray(arr) {
  return arr.map(square);
}

// 把多个函数组合成一个新的函数
function compose(...fns) {
  return function (x) {
    return fns.reduceRight(function(acc, fn){
      return fn(acc);
    }, x);
  };
}

const numbers = [1, 2, 3, 4, 5];
const processArray = compose(squareArray, doubleArray);
const result = processArray(numbers);

console.log(result); // [ 4, 16, 36, 64, 100 ]

有没有发现,函数式编程在实践中就像用乐高堆积木。

在这个例子中,先定义了两个纯函数。遵循不可变性原则对需要运算的数组进行新建而不是改变原有数据。最后把多个功能函数组合成一个新的函数,实现需要的功能。

纯函数

上面的实例中定义了两个纯函数 doublesquare。那么什么样的函数是纯函数呢?

根据定义,如果一个函数符合两个条件,它就可以被称为纯函数:

  1. 此函数在相同的输入值时,总是产生相同的输出。
  2. 次函数不会对除了函数返回值之外的任何东西产生可观察的副作用。不会修改传入的参数,也不会对全局状态进行更改,不会进行 I/O 操作,也不会触发网络请求等

由此可见,纯函数的执行仅仅是为了计算并返回一个值

下面是一个纯函数和非纯函数的对比实例。

// 纯函数,不依赖于外部状态,相同的输入总是产生相同的输出
function add(a, b) {
  return a + b;
}

console.log(add(2, 3)); // 输出 5
console.log(add(2, 3)); // 输出 5

// 非纯函数,依赖于外部状态,相同的输入可能产生不同的输出
let result = 0;

function addToResult(value) {
  result += value;
  return result;
}

console.log(addToResult(5)); // 输出 5
console.log(addToResult(3)); // 输出 8

函数是一等公民

在函数式编程中,函数被视为一等公民。函数可以像其他数据类型(如整数、字符串等)一样被操作、传递和赋值。这种特性使得函数式编程具备更高的抽象能力和灵活性,可以更方便地实现函数的组合和重用。

1、函数可以被赋值给变量

const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

const operation = add; // 函数赋值给变量

console.log(operation(2, 3)); // 输出: 5

operation = multiply; // 可以修改变量的值为另一个函数
console.log(operation(2, 3)); // 输出: 6

2、函数可以作为参数传递

const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

const applyOperation = (a, b, operation) => operation(a, b);

console.log(applyOperation(2, 3, add)); // 输出: 5
console.log(applyOperation(2, 3, multiply)); // 输出: 6

3、函数可以作为返回值返回

const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

const createOperation = (operator) => {
  if (operator === '+') {
    return add;
  } else if (operator === '*') {
    return multiply;
  }
};

const operation1 = createOperation('+');
console.log(operation1(2, 3)); // 输出: 5

const operation2 = createOperation('*');
console.log(operation2(2, 3)); // 输出: 6

函数组合

函数式编程中还有一个重要的思想就是组合(compose),它允许我们把逻辑通过函数的形式进行拆分,然后进行函数之间的组合最后得到想要的完整逻辑。文章开篇的实例中已经进行了演示,这里不做重复。

简言之函数式编程的实践就是对业务逻辑进行拆分,变成一个个纯函数,然后在组合成一个完整的逻辑模块。

这样做的好处很明显,简化逻辑,复用代码,方便写单元测试。

函数式编程库推荐

JavaScript 函数式编程的库有很多,以下是几个常用的库:

Ramda

Ramda 是一个专注于函数式编程的 JavaScript 库,提供了许多实用的函数和工具,用于支持函数式编程的开发风格。它强调不可变性、纯函数和柯里化,并提供了丰富的函数来处理和操作数据。

Lodash/fp

Lodash 是一个广泛使用的 JavaScript 实用工具库,而 Lodash/fp 则是 Lodash 的函数式编程版本。它提供了许多函数式风格的函数,并采用自动柯里化和数据优先的方式。

Immutable.js

Immutable.js 是一个用于处理不可变数据的 JavaScript 库。它提供了一组持久化的数据结构,如 List、Map、Set 等,以及一些用于操作这些数据结构的函数。通过使用 Immutable.js,可以更轻松地管理和操作不可变数据,避免副作用和数据修改。

RxJS

RxJS 是一个响应式编程库,它基于观察者模式和可观察序列(Observables)的概念。它提供了丰富的操作符和工具,用于处理异步数据流和事件序列。RxJS 支持函数式编程的思想,提供了一种声明式和组合的方式来处理异步操作。

总结

本文简单介绍了函数式编程的基本概念和特点,包括纯函数、函数是一等公民和函数组合。旨在能够让大家方便快速的理解函数数编程是什么。函数式编程可以提高代码的可读性、可维护性和并发性,非常值得学习和应用。

原文链接:https://juejin.cn/post/7327831782703120435 作者:东方红杉

(0)
上一篇 2024年1月25日 下午4:00
下一篇 2024年1月25日 下午4:10

相关推荐

发表回复

登录后才能评论