为什么要学习函数式编程:
- 函数式编程是随着 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'))