函数式编程

201 阅读2分钟

函数式编程

一、函数式编程的出现

  • 发展史:命令(脚本)式 -> 面向对象 -> 函数式编程

1. 从实际问题开始

// 1.数组在 `url` 中展示形式
location.search => '?name[]=progressive$%coding&name[]=objective$%coding&name[]=functional$%coding'
// 2.参数提取拼接成数组
['progressive$%coding', 'objective$%coding', 'functional$%coding']
// 3. 手写方法,转换成数组对象
// [{name: 'Progressive Coding'}, {name: 'Objective Coding'}, {name: 'Functional Coding'}]
const _array = ['progressive$%coding', 'objective$%coding', 'functional$%coding']
const _objArr = []
​
const nameParser(array, objArr){
  array.forEach(item=>{
    let names = item.split('$%')
    let newName = []
    
    names.forEach(name=>{
      let nameItem = name[0].toUpperCase() + name.slice(1)
      
      newName.push(nameItem)
    })
    objArr.push({
      name: newName.join(' ')
    })
  })
  return objArr
}
​
console.log(nameParser(_array, _objArr))

2. 存在的问题:

  • 过程中存在逻辑包裹 -> 看完整段代码,才能明白逻辑
  • 存在临时变量,且首尾封闭 -> 迭代难度高

3. 解决方案:函数式编程

  • 需求分析:数组 => 数组对象 => [ 字符串 => 对象 ],即 nameParser => [objHelper : string => object]
  • 模块功能明确:objHelper => formatName + assembleObj
  • 功能拆分: objHelper = [(split + captialize + join) + assembleObj]
  • 代码实现:
const _array = ['progressive$%coding', 'objective$%coding', 'functional$%coding']// 原子操作
const assembleObj = (key, x) => {
  const obj = {}
  obj[key] = x
  return obj
}
const capitalize = name => name[0].toUpperCase() + name.slice(1)
​
// 组装描述,组装合并函数为compose,下文中提到
const formatName = 组装合并(join(' '), map(capitalize), split('$%'))
const objHelper = 组装合并(assembleObj('name'), formatName)
const nameParser = map(objHelper)
​
// 使用
nameParser(_array)

4. 拓展:数组的遍历方法的比较

本质作用:

  • forEach注重逻辑处理;
  • map用于生成处理后的数组;
  • filter用于生成过滤后的数组;
  • sort用于生成排序后的数组;

二、函数式编程的原理与特点

1. 函数式编程的原理

  • 加法结合律 | 因式分解 | 完全平方公式

2. 理论思想

a. 函数是一等公民

  • 函数是逻辑功能的落脚点
  • (实现 + 拼接) 函数

b. 声明式编程

函数式编程通过声明式编程来实现。声明需求 => 语义化。

c. 惰性执行 - 无缝连接、性能节约

// 惰性函数
const program = name => {
  const nameList = ['progressive', 'objective', 'functional']
  if(nameList.indexOf(name) !== -1){
    return program = () => {
      console.log(name)
    }
  }
}
​
program('progressive')()
console.log('lazy')
program()
​
// 依次打印 'progressive' 'lazy' 'progressive'

3. 无状态与无副作用

  • 无状态 - 幂等
  • 无副作用 - 不对外部数据做操作,只对传入的参数做处理,必要时需要深拷贝

三、实际开发

1. 纯函数的改造

纯函数:无状态、无副作用的函数。

2. 流水线组装 — 加工 & 组装

  • 加工 - 柯里化:传入一 个参数,返回一个函数接收剩下的参数
const sum = (x, y) => x + y
sum(1, 2)
// 函数柯里化
const add = x => {
  return y => {
    return x + y
  }
}
add(1)(2)
// 体系 = 加工 + 组装, 单个加工输入输出应单值化 -> 需求
  • 函数组装

    const compose = (f, g) => f(g(x))
    ​
    const sum1 = x => x + 1
    const sum2 = x => x + 2
    compose(sum1, sum2)
    
  • 编程思想比较
// 命令式
trim(reverse(toUpperCase(map(arr))))
// 面向对象
arr.map().toUpperCase().reverse().trim()
// 函数式: 从右往左执行
const result = compose(trim, reverse, toUpperCase, map)

四、BOX 与 函子

// 一封信
class Mail {
  constructor(content){
    this.content = content
  }
  map(fn){
    return new Mail(fn(this.content))
  }
}
​
// 1.拆开信
let mail1 = new Mail('love')
// 2.阅读信
let mail2 = mail1.map((mail) => read(mail))
// 3.烧掉信
let mail3 = mail1.map((mail) => burn(mail))
// 4.老师检查,没有发现
mail3.map((mail) => check(mail))
​
// 链式
new Mail('love')
  .read()
  .burn()
  .check()

\