函数式编程理念

200 阅读4分钟

什么是函数式编程

概念:

函数式编程是一种编程范式,强调使用函数来组合和处理数据。将运算过程抽象成成函数,可以复用

常⻅的编程范式

  • 面向过程编程(Procedural Programming)PP:按照步骤 来实现,将程序分解为过程和函数。这些过程和函数按顺序 执行来完成任务。
  • 
面向对象编程(Object-Oriented Programming)OOP: 将程序分解为对象,每个对象都有自己的状态和行为。
  • 面向 对象的核心是(类,实例,继承,封装,多态) 函数式编程(Functional Programming)FP:使用函数来 组合和处理数据,描述数据之间的映射。函数指的并不是编 程语言中的函数,指的是数学意义上的函数 y=f(x) 输入映射 输出 一个函数 f 接收一个参数 x,并根据 x 计算返回一个结果 y。

函数式编程的优势

  • 可维护性:函数式编程的程序通常更加简洁和可读,因为它们避免了状态变化和副作用。这使得代码更易于理解和维护。
  • 可测试性:由于函数式编程程序通常是无副作用的,所以可以很容易地对其进行单元测试。
  • 并发性:函数式编程程序通常是无副作用的,所以可以很容易地并行地执行。

扩展性:函数式编程程序通常是纯函数,可以很容易地组合和重用。

  • 可靠性:函数式编程程序通常是无副作用的,所以可以很容易地预测其行为。
  • Vue3 也开始拥抱函数式编程,函数式编程可以抛弃 this, 打包过程中更好的利用 tree-shaking 过滤无用的代码

高阶函数

概念

一个函数的参数是一个函数,或者一个函数的返回值是一个 函数。则称这个函数是高阶函数。

类型

函数作为参数

函数作为返回值

纯函数

概念

相同的输入永远会得到相同的输出,而且没有任何的副作用。 (不会对外部环境产生影响,并且不依赖于外部状态)

常⻅副作用:

对全局变量或静态变量的修改

对外部资源的访问(如文件、数据库、网络 http 请求)

对系统状态的修改 (环境变量)
对共享内存的修改
DOM 访问,打印/log 等

副作用使得方法通用性降低,让代码难以理解和预测,测试困 难,导致静态问题等。

lodash 库中所有的方法都是纯函数 方法

柯里化

概念

柯里化是一种函数转换技术,它将一个多参数函数转换为一系 列单参数函数。与之类似的偏函数是指对于一个函数,固定其 中一些参数的值,生成一个新函数,这个新函数接受剩下的参 数

实现

function curry(func){ // 高阶函数
  const curried = (...args)=>{
     if( args.length < func.length){ // 传递的参数 不满足函数
      return (...other)=>curried(...args,...other)
     }
      return func(...args)
  }
  return curried
}

const curriedSum =  curry(sum)
let curriedSum =curry(sum)
	curriedSum(1)(2)(4)

函数组合 compose

概念

如果一个函数要经过多个函数处理才能得到最终的值,这个时候我们可以把中间这些过程函数合并成一个新的函数。

解决的问题

之前
// addPrefix(toFiexd(double(10000))) // 洋葱模型
// double(10000) | toFiexd | addPrefix //   过滤器的用法 管道 (滤网)  vue2 filter
现在
flowRight(currency,fixed,double)
避免了层级嵌套,利于函数复用

实现

function flowRight (...fns){
  if(fns.length===1){
    return  fns[1]
  }
  return  fns.reduceRight((a,b)=> (...args)=>b(a(...args)))
  //利用自执行函数接口,可以去掉前面和后面的括号,
  //代码最终addPrefix(toFiexd(double(1000))),与洋葱模型一致
}
function double (n){
   return  n*2
}
function fixed (n){
   return  n.toFixed(2)
}
function currency (n){
  return '$'+n
}
let money =flowRight(currency,fixed,double)
console.log(money(10000));
// $20000.00

解决异步并发问题

哨兵变量

通过在全局设置一个变量,每次异步执行成功进行变量修改,直到满足预定条件,进行输出

缺点: 无法对某一具体异步进行操作。

函数式编程

比哨兵变量的方法稍做优化,通过函数对变量进行缓存,无需在全局进行设置,

缺点: 整个过程我们无法监控,因为在使用函数的时候 我们没有处理的过程

发布订阅

const fs = require("fs");
const path = require("path");
// 发布订阅的核心就是将订阅函数存放到数组中,稍后事情发生 了 循环数组依次调用
// 不订阅也能发布 (订阅和发布之间没有任何关系)
let school = {};
let events = {
  _arr: [],
  on(callback) {
// 将要订阅的函数保存起来
    this._arr.push(callback);
  },
  emit(key, value) {
    school[key] = value;
    this._arr.forEach((callback) =>
callback(school));
  }
};
events.on((data) => {
  if (Object.keys(data).length === 2) {
    console.log(data);
} });
 
events.on((data) => { console.log("读取一个完毕", data);
});
fs.readFile(path.resolve(__dirname, "age.txt"),
"utf8", function (err, data) {
  events.emit("age", data);
});
fs.readFile(path.resolve(__dirname, "name.txt"),
"utf8", function (err, data) {
  events.emit("name", data);
});
// 发布订阅模式,可以监控到每次完成的情况,而且可以自己控 制逻辑