函数式编程

169 阅读2分钟

为什么要学习函数式编程:

  • 函数式编程是随着 React 的流行受到越来越多的关注
  • vue3.0也往函数式编程靠
  • 函数式编程可以抛弃this
  • 打包过程中可以更好的利用 tree shaking 过滤无用代码
  • 方便测试、方便并行处理
  • 有很多库可以帮助我们进行函数式开发:lodash、underscore、ramda

函数式编程:

个人认为是函数式编程关心数据的映射关系。类似数学函数y= sin(x),x和y的关系。它把一个程序中的所有部分通过抽离,分解成一个个独立的存在着某种映射关系的纯函数,通过这些纯函数组合起来,完成一个程序的运转。

函数式一等公民:

函数可以作为变量,参数和返回值

闭包:

只有函数内部的子函数才能访问函数的局部变量,所以闭包可以理解成“定义在一个函数内的函数”。

// 闭包的例子
function fn1(){
    let a = 1;
    function fn2(){
        console.log(a)
    }
}

闭包的用途:

  • 减少全局变量,防止全局变量过多,导致难以维护
  • 防止变量被修改,只能在当前函数内才能被修改

闭包的缺点: 由于闭包会导致函数内的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,在ie中可能导致内存泄漏,解决办法是在退出函数之前,将不用的变量删除,即设置成null,系统会自动回收

纯函数:

输入相同的值,永远会返回同一个结果,没有明显的副作用。纯函数可以用来缓存已经执行过的函数的结果

//可缓存的例子
let memoize = function(fn) {
  let cache = {}
  return function() {
    //把传入的参数作为对象的key值
    let key = JSON.stringify(arguments)
    //如果有对应的key,则直接取缓存cache里的值,没有则执行函数
    //fn.apply(fn, arguments) 利用apply是防止fn的指向出现问题,直接把fn绑在自己身上。函数式编程尽量不要使用this
    cache[key] = cache[key] || fn.apply(fn, arguments)
    return cache[key]
  }
}

柯里化:

对于一个有多个参数的函数,当只传入其中部分函数时,会返回一个新的函数来接收剩余的参数,直到返回结果

//自己模拟实现柯里化函数
function summation(a, b, c) {
  return a + b + c
}

// 模拟  _.curry()柯里化 的实现
function curry(fn) {
  return function part(...arg) {
    // 判断实参和形参的个数是否相等
    if(arg.length < fn.length) {//实参少了
      return function() {
        //把前一次的参数和后一次的参数合并起来,再执行part函数,去判断实参和形参的个数是否相等
        let arr = [...arg, ...arguments]
        return part(...arr)
      }
    }
    return fn(...arg) //实参和形参的个数相等
  }
}

let summaSky = curry(summation)

函数组合:

如果一个函数在执行的过程中会使用多个函数,这个时候可以把这些函数整合成一个函数,其中的函数就相当于一个个小水管,组合成之后形成一个大水管,最后获得运行结果。函数组合默认是从右往左执行

//把字符串‘hello sky’,添加‘任意字符’,全部变大写,并用'-'分割

const _ = require('lodash')
const add = _.curry((addStr, str)=>{
  console.log('执行添加事件', str + ' ' + addStr)
  return str + ' ' + addStr
})
const toUpper = _.curry(str=>{
  console.log('执行变大写事件', str.toLocaleUpperCase())
  return str.toLocaleUpperCase()
})
const split = _.curry((sep, str)=>{
  console.log('执行分割成数组事件', str.split(sep))
  return str.split(sep)
})
const join = _.curry((sep, array)=>{
  console.log('执行分割成字符串事件', array.join(sep))
  return array.join(sep)
})

// let fn = _.flowRight( join('-'),  _.flowRight( split(' '), toUpper ), add('welcome') )
// console.log(fn('hello sky'))

// 模拟实现 flowRight
function flowRight(...arrFn) {
  return function (value) {
    return arrFn.reverse().reduce((total, fn)=>{
      return fn(total)
    }, value)
  }
}

let fn = flowRight( join('-'),  flowRight( split(' '), toUpper ), add('welcome') )
console.log(fn('hello sky'))