函数式编程

前言

如今主流的两种方式,一种是 OOP (Object-oriented programming ) 面向对象编程,另外一种是 FP (Functional Programming)。本文稍微普及一下 FP 这种方式。javascript

特色

与其余编程范式相比,FP的主要区别在于声明性方法(FP)与命令式方法。在咱们深刻了解正式定义以前,让咱们经过查看示例来探索差别。java

🌰声明式写法

// 数组每一个元素乘三
const triple = (arr) => {
 let results = []
 for (let i = 0; i < arr.length; i++){
   results.push(arr[i] * 3)
 }
 return results
}

// 数组求和
const sum = (arr) => {
 let result = 0
 for (let i = 0; i < arr.length; i++){
   result += arr[i]
 }
 return result
}
复制代码
  • 这段代码片断的主要复杂性源于这样一个事实:咱们不是告诉计算机咱们想要它作某些事而是指示如何作到这一点
  • 并且可读性不好(😱😱😱), 这是一个简单示例,但随着程序的增加和功能变得更加复杂,使用像这样的for循环会建立很是简单的代码,而且须要咱们的大脑分析循环的内部工做,同时跟踪索引,变量和更多。命令式代码在阅读时会增长认知负荷,而且随着时间的推移会使推理和逻辑更容易出错。

命令式写法

// 第一个例子
const  triple  =(arr)=>  arr.map((currentItem)=> currentItem *  3)

//第二个例子
const  sum  =(arr)=>  arr.redeuce((prev,current)=> prev + current,0)
复制代码
  • 首先,我保证在给定相同输入的状况下,这两种方法每次都会产生相同的输出
  • 很明显,声明性片断比命令式片断更简,它也更容易阅读。在咱们的例子中是一个匿名函数,它告诉程序我但愿它对数组中的每一个元素作什么,而不是指示程序我但愿它访问哪些索引等等

函数做为一等公民

所谓"第一等公民"(first class),指的是函数与其余数据类型同样,处于平等地位,能够赋值给其余变量,也能够做为参数,传入另外一个函数,或者做为别的函数的返回值。react

// 好比咱们要先请求然数据而后渲染模板,可能会这么写
const render = (json) => {}
httpGet('/post/2', json => render(json))
复制代码

若是这个时候咱们除了拿数据还要拿 error 错误信息怎么办,你可能会想再加个参数编程

const render = (json, err) => {}
httpGet('/post/2', (json, err) => render(json, err))
复制代码

可是之后若是可还有其余参数怎么办??若是用一等公民的写法这样json

const render = (json, err) => {}
httpGet('/post/2', render)
复制代码

纯函数

纯的意思是引用透明没有任何反作用 更多概念性的东西能够参考阮一峰大大的入门教程数组

// 不纯的
const minimum = 21
const checkAge = age => {
 return age  >= minimum
}
// 纯的
const checkAge = age => {
 const minimum = 21
 return age  >= minimum
}
复制代码

再看下引用类型:闭包

const data = [1, 2, 3]
const a = data.slice(0, 1)
// [1]
const b = data.slice(0, 1)
// [1]
const c = data.slice(0, 1)
// 此处是为了对比因此用了const,正常确定会报错的
// [1]
const a = data.splice(0, 1)
// [1]
const b = data.splice(0, 1)
// [2]
const c = data.splice(0, 1)
复制代码

柯里化 Curry

函数柯⾥里里化就是只传递给函数⼀一部分参数来调⽤用它,让它返回⼀一个函数去处 理理剩下的参数函数式编程

举个例子函数

function add (x, y) {
  return x + y
}
function add (x) {
  return function (y) {
return x + y }
}
const add = x => y => x + y
// add(2,3) 等价于 add(2)(3)
//舒服了
复制代码

这里咱们定义了一个 add 函数,它接受一个参数并返回一个新的函数。调用 add 以后,返回的函数就经过闭包的方式记住了 add 的第一个参数。一次性地调用它实在是有点繁琐,好在咱们可使用三方函数式编程库一个特殊的 curry 帮助函数使这类函数的定义和调用更加容易,后面我会把函数式编程库不错的列出来post

代码组合 Compose

const b = f(a)
const c = g(b)

不用函数式:const c = g(f(a))
用了函数式:const c = compose(g, f)(a)

吃我个🌰

const data = [1, 2, 3]
const head = arr => arr[0]
const reverse = arr =>
  arr.reduce((acc, item) => [item].concat(acc), [])
const last = compose(
  head,
reverse
)
last(data) // 3
 
复制代码

这里只是举个例子取数组最后的一项,真正写起来不会这么麻烦那么我接下来举个你们用的比较多对象判空的例子

const user = {
    name: 'cai',
    address: {
        city: 'hangzhou'
    }
}
const city =
 user &&
 user.address  &&
 user.address.city
 // 或者这样
 const city = !user
? undefined
: !user.address
? undefined
: user.address.city
复制代码

用了函数式之后

const prop = key => obj  => (obj   === undefined  || obj   === null)
  ? null
: obj[key]
const getUserCity = compose(
  prop('city'),
  prop('address')
)
getUserCity(user)  // hangzhou
复制代码

用对象的形式

class Maybe {
  constructor(val) {
    this. __val = val
  }
  static of(val) {
    return new Maybe(val)
  }
  isNothing() {
    return (this. __val   === null  || this. __val   === undefined)
  }
  map(f) {
    return (
      this.isNothing()
        ? new Maybe(null)
        : new Maybe(f(this. __val))
) }
}
/* 调用 */
// props :: s  -> {s: a}  -> a
const prop = key  => obj  => obj[key]
Maybe
  .of(user)
  .map(prop('address'))
  .map(prop('city'))
 // Maybe {  __val: 'hangzhou' }
复制代码

题外话

用过 react 童鞋值到 react 有 hoc (高阶组件)的概念 其实也是把组件当成参数的形式传进来返回一个新的组件这点是共通的

推荐

  • ramda、lodash/fp :函数式编程库
  • Recompose:React 的 Lodash
  • RxJS: 用来处理理事件的 lodash

QA

我的理解系的很差望指证

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息