【基础】关于函数式编程的知识

什么是函数式编程

如果让你写一个这个需求你会怎么写?

写一个计算数组中所有偶数平方和的例子

是这样?

js 复制代码
function imperativeSumOfSquaredEvens(arr) {
  let sum = 0;
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] % 2 === 0) {
      sum += arr[i] * arr[i];
    }
  }
  return sum;
}

还是这样?

js 复制代码
function functionalSumOfSquaredEvens(arr) {
  return arr
    .filter(num => num % 2 === 0)
    .map(num => num * num)
    .reduce((acc, num) => acc + num, 0);
}

命令式编程是一种基于存储程序概念的编程范式,通过修改可变状态来改变程序行为,核心是关注how(如何做)即详细描述操作步骤,使用可变状态,允许副作用,使用循环、复制和控制流语句。

函数式编程是一种基于函数的强大的编程范式,将计算机运算视为函数运算,避免使用程序状态以及易变对象。核心是关注what(需要什么)即函数组合,强调不可变性,使用纯函数没有副作用,广泛使用高阶函数。

在实际开发中,两者常常结合使用,根据具体场景选择最适合的编程方式

核心基础概念

纯函数(Pure Function)

定义:相同输入始终产生相同输出,且没有副作用的函数。可预测、便于测试、调试和并行处理(无状态,不会发生竞态条件)

js 复制代码
// 纯函数示例
function add(a, b){
    return a + b
}

// 非纯函数示例
let counter = 0
function increment() {
	counter++
	return counter
}

不可变数据(Immutability)

定义:数据一旦创建,就不能被修改,任何"修改"操作都会创建新数据。避免意外修改数据,简化状态管理,便于实现函数式编程的其他特性

js 复制代码
// 不可变操作示例
const originalArray = [1, 2, 3]
const newArray = [...originalArray, 4]

// 可变操作(不推荐)
const mutableArray = [1, 2, 3]
mutableArray.push(4)

函数作为一等公民(First-class Functions)

定义:函数可以像普通变量一样被传递、存储和操作

js 复制代码
// 函数作为变量
const add = (a, b) => a + b

// 函数作为参数
const process = (array, func) => array.map(func)
const result = process([1, 2, 3], add)

// 函数作为返回值
const createMultiplier = factor => number => number * factor
const double = createMultiplier(2) // 相当于 function(number) { return number * 2 }
console.log(double(5)) // 10

高阶函数(Higher-order Functions)

什么是高阶函数

  • 接受一个或多个函数作为参数的函数
  • 或返回一个函数作为结果的函数

JavaScript中的常见高阶函数

map、filter、reduce、forEach .....

js 复制代码
const numbers = [1, 2, 3, 4, 5]

const sequared = numbers.map(number => number * number)

const evens = numbers.filter(number => number % 2 === 0)

const sum = sequared.reduce((total, number) => total + number, 0)

numbers.forEach(number => console.log(number))

函数组合(Function Composition)

什么是函数组合

将多个函数串联起来,形成新的功能,使代码更简洁、可读性更高

函数组合的实现方式

手动实现

js 复制代码
// 简单示例
function compose(f, g) {
	return function (x) {
		return f(g(x));
	}
}

// 多个组合示例
function compose(...funcs) {
	return funcs.reduce((aFun, b) => (...args) => a(b(...args)))
}

使用函数组合库

js 复制代码
const R = require('ramda');
const addOneAfterDouble = R.compose(R.add(1), R.multiply(2));
console.log(addOneAfterDouble(5)); // 11

实践应用

js 复制代码
function compose(...funcs) {
	return funcs.reduce((aFun, b) => (...args) => a(b(...args)))
}

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  
// 定义处理函数
const isEven = n => n % 2 === 0;
const square = n => n * n;
const addOne = n => n + 1;
const toNegative = n => -n; 

// 创建数字处理管道
const numberProcessor = compose(
	toNegative,
	addOne,
	square
); 

// 应用管道到偶数

const processedNumbers = numbers
	.filter(isEven)
	.map(numberProcessor);
	
console.log(processedNumbers); // [-5, -17, -37, -65, -101]

高级概念

柯里化(Currying)

将接受多个参数的函数转换成一系列接受单个参数的函数

js 复制代码
// 普通函数
function add(a, b) {
  return a + b
}

// 柯里化版本
function curriedAdd(a) {
  return function (b) {
    return a + b
  }
}

// 使用柯里化
const add5 = curriedAdd(5)
console.log(add5(10)) // 15

// 使用Ramda实现柯里化
const R = require('ramda');
const curriedAdd = R.curry(add);
const add5 = curriedAdd(5);
console.log(add5(3)); // 8

递归(Recursion)

函数式编程中常用的控制流程,替代循环

js 复制代码
function sum(arr) {
  if (arr.length === 0) {
    return 0;
  }

  return arr[0] + sum(arr.slice(1));
}

console.log(sum([1, 2, 3, 4, 5])); // 15

惰性求值(Lazy Evaluation)

只有需要时才计算表达式,有助于优化性能

js 复制代码
// 惰性求值示例
class LazyArray {
  constructor(generator) {
    this.generator = generator;
    this.operations = [];
  }

  filter(predicate) {
    return new LazyArray(function* () {
      for (const item of this.generator()) {
        if (predicate(item)) {
          yield item;
        }
      }
    }.bind(this));
  }

  map(transform) {
    return new LazyArray(function* () {
      for (const item of this.generator()) {
        yield transform(item);
      }
    }.bind(this));
  }

  take(n) {
    return new LazyArray(function* () {
      let count = 0;
      for (const item of this.generator()) {
        if (count >= n) break;
        yield item;
        count++;
      }
    }.bind(this));
  }

  // 执行所有操作并返回结果数组
  evaluate() {
    const result = [];
    for (const item of this.generator()) {
      result.push(item);
    }
    return result;
  }
}

// 创建一个无限自然数序列的生成器
function* naturalNumbers() {
  let i = 1;
  while (true) {
    yield i++;
  }
}

// 创建惰性数组的工厂函数
function createLazyArray() {
  return new LazyArray(naturalNumbers);
}

// 使用示例
const lazyArray = createLazyArray();
const result = lazyArray
  .filter(x => x % 2 === 0)
  .map(x => x * 2)
  .take(5)
  .evaluate();

console.log(result);

总结

掌握函数式编程需要循序渐进,从基础概念开始,逐步深入到高级应用。以下是关键点:

  1. 纯函数是函数式编程的基石
  2. 高阶函数是函数式编程的"语法糖"
  3. 函数组合让代码更简洁、可读
  4. 不可变数据确保代码的可预测性
  5. 递归是函数式编程的控制流程
相关推荐
蓝莓味的口香糖2 小时前
【JS】JS基础-对象处理方法整合
开发语言·前端·javascript
sanx183 小时前
一站式电竞平台解决方案:数据、直播、源码,助力业务飞速启航
前端·数据库·apache·数据库开发·时序数据库
余防3 小时前
存储型XSS,反射型xss
前端·安全·web安全·网络安全·xss
ObjectX前端实验室3 小时前
从零到一:系统化掌握大模型应用开发【目录】
前端·llm·agent
guoyp21263 小时前
前端实验(二)模板语法
前端·vue.js
葡萄城技术团队3 小时前
Excel 转在线协作难题破解:SpreadJS 纯前端表格控件的技术方案与实践
前端·excel
我的xiaodoujiao3 小时前
Windows系统Web UI自动化测试学习系列3--浏览器驱动下载使用
前端·windows·测试工具·ui
一只小风华~3 小时前
学习笔记:Vue Router 中的嵌套路由详解[特殊字符]概述
前端·javascript·vue.js
泻水置平地3 小时前
若依前后端分离版实现前端国际化步骤
前端