简单演示
// 硬编码问题
function cheackAge(age){
let min =18 //此处存在硬编码
return age>=min
}
console.log(cheackAge(21)) // true
提出硬编码,作为参数解决。基础值需要定义多次
function cheackAge(min,age){
return age>=min
}
console.log(cheackAge(18,21)) //true
console.log(cheackAge(18,23)) //true
普通版本柯里化
function cheackAge(min){
return function(age){
return age>=min
}
}
const checkAge18=cheackAge(18)
console.log(checkAge18(21)) //true
es6版本柯里化
const cheackAge = (min)=>(age)=>age>=min
const checkAge18=cheackAge(18)
const checkAge13=cheackAge(13)
console.log(checkAge18(21)) //true
console.log(checkAge13(16)) //true
创建一个函数,该函数接收一个或多个func的参数,如果func所需要的参数都被提供则执行func并返回结果,否则会继续返回该函数并等待接收剩余的参数。
const _ = require('loadsh')
const getSum = (a,b,c)=>a+b+c
const curried = _.curry(getSum)
console.log(curried(1,2,3)) // 6
console.log(curried(1)(2)(3)) // 6
console.log(curried(1,2)(3)) //6
案例-判断一个字符串中是否有空白字符
// 定义正则与字符比对的函数
const match = _.curry((reg,str)=>str.match(reg))
// 定义判断是否包含空格的函数
const hasSpace = match(/\s+/g)
// 定义判断是否包含数字的函数
const hasNumber = match(/\d+/g)
// 定义数组过滤的函数
const filter = _.curry((fn,arr)=>arr.filter(fn))
// 查找数组中包含空格的元素
const findSpace = filter(hasSpace)
console.log(hasSpace('hello word')) // [ ' ' ]
console.log(hasNumber('hello 123abc')) // [ '123' ]
console.log(filter(hasSpace,['hello word','John_Donne'])) // [ 'hello word' ]
console.log(findSpace(['hello word','John_Donne'])) // [ 'hello word' ]
普通版本
function curry(fn){
return function curriedFn(){
var args = [].slice.call(arguments)
if(args.length<fn.length){
return function(){
return curriedFn.apply(undefined,args.concat([].slice.call(arguments)))
}
}
return fn.apply(undefined,args)
}
}
const getSum = (a,b,c)=>a+b+c
const curried =curry(getSum)
console.log(curried(1,2,3)) // 6
console.log(curried(1)(2)(3)) // 6
console.log(curried(1,2)(3)) //6
console.log(curried(1)(2)(3)) //6
es6版本
const curry=(fn)=>{
const curriedFn = (...args)=>{
if(args.length<fn.length){
return (..._args)=>{
return curriedFn(...[...args,..._args])
}
}
return fn(...args)
}
return curriedFn
}
const getSum = (a,b,c)=>a+b+c
const curried =curry(getSum)
console.log(curried(1,2,3)) // 6
console.log(curried(1)(2)(3)) // 6
console.log(curried(1,2)(3)) //6
console.log(curried(1)(2)(3)) //6
纯函数和柯里化很容易写出洋葱代码
h(g(f(x)))
获取数组最后一个元素在转大写 .toUpper(.first(_.reverse(array)))
函数的组合可以让我们把细粒度的函数重新组合成一个新的函数
* 如果一个函数要经过多个函数处理才能得到最终的值,这时候可以吧中间过程的函数合并成一个函数
* 函数就是数据的管道,函数的组合就是吧这些管道连接起来,让数据穿过多个管道行程最终的结果
* 函数某人是从右向左执行的
函数组合简单实现——普通代码
function compose (f,g){
return function(val){
return f(g(val))
}
}
// 数组反转
function reverse (arr){
return arr.reverse()
}
// 获取数组第一个值
function first(arr){
return arr[0]
}
// 获取数组中最后的一个元素
const last = compose(first,reverse)
console.log(last([1,2,3])) // 3
函数组合简单实现——ES6
const compose = (f,g)=>val=>f(g(val))
const reverse = arr=>arr.reverse()
const first = arr=>arr[0]
const last = compose(first,reverse)
console.log(last([1,2,3])) // 3
flowRight()
const _ = require('loadsh')
const reverse = arr => arr.reverse()
const first = arr => arr[0]
const toUpper = str => str.toUpperCase()
const fn = _.flowRight(toUpper,first,reverse)
console.log(fn(['one','tow','three'])) // THREE
flow
const _ = require('loadsh')
const reverse = arr => arr.reverse()
const first = arr => arr[0]
const toUpper = str => str.toUpperCase()
// 此处有变化
const fn = _.flow(reverse,first,toUpper)
console.log(fn(['one','tow','three'])) // THREE
常规实现
const flowRight = function(){
const args = [].slice.call(arguments)
return function(val){
return args.reverse().reduce(function(_val,fn){
return fn(_val)
},val)
}
}
const reverse = arr => arr.reverse()
const first = arr => arr[0]
const toUpper = str => str.toUpperCase()
const fn = flowRight(toUpper,first,reverse)
console.log(fn(['one','tow','three'])) // THREE
ES6实现
const flowRight = (...args)=>val=>args.reverse().reduce((_val,fn)=>fn(_val),val)
const reverse = arr => arr.reverse()
const first = arr => arr[0]
const toUpper = str => str.toUpperCase()
const fn = flowRight(toUpper,first,reverse)
console.log(fn(['one','tow','three'])) // THREE
函数的组合要满足结合律例如 (fn1+fn2)+fn3 == fn1+(fn2+fn3)
const _ = require('loadsh')
console.log(_.flowRight(_.toUpper,_.first,_.reverse)([ 'one', 'two','three'] )) //THREE
console.log(_.flowRight(_.flowRight(_.toUpper,_.first),_.reverse)([ 'one', 'two','three'] )) //THREE
console.log(_.flowRight(_.toUpper,_.flowRight(_.first,_.reverse))([ 'one', 'two','three'] )) //THREE
注意 loadsh 中的 _.reverse 会改变原数组
在管道中增加了一段透明的管子,方便调试
// 实现功能
// my name is tom 转化为 MY-NAME-IS-TOM
const _ = require('loadsh')
// 实现打印功能
const log = _.curry((str,data)=>{
console.log(`${'='.repeat(4)}${str}打印开始${'='.repeat(4)}`)
console.log(data)
console.log(`${'='.repeat(4)}${str}打印结束${'='.repeat(4)}`)
return data
})
// 柯里化split
const split = _.curry((sep,str)=>_.split(str,sep))
// 柯里化map
const map = _.curry((fn,arr)=>_.map(arr,fn))
// 柯里化join
const join = _.curry((sep,arr)=>_.join(arr,sep))
const fn = _.flowRight(join('-'),log('map'),map((val)=>_.toUpper(val)),log('split'),split(' '))
console.log(fn('my name is tom'))
// ====split打印开始====
// [ 'my', 'name', 'is', 'tom' ]
// ====split打印结束====
// ====map打印开始====
// [ 'MY', 'NAME', 'IS', 'TOM' ]
// ====map打印结束====
// MY-NAME-IS-TOM
loadsh/fp模块与loadsh对比
loadsh
数据优先函数置后
const _ = require('loadsh')
console.log(_.map(['a', 'b', 'c', 'd'], _.toUpper))
//[ 'A', 'B', 'C', 'D' ]
console.log(_.map(['a', 'b', 'c', 'd']))
//[ 'a', 'b', 'c', 'd' ]
console.log(_.split('hello_word', '_'))
//[ 'hello', 'word' ]
loadsh/fp
函数优先数据置后
const fp = require('loadsh/fp')
console.log(fp.map(fp.toUpper,['a', 'b', 'c', 'd']))
//[ 'A', 'B', 'C', 'D' ]
console.log(fp.map(fp.toUpper)(['a', 'b', 'c', 'd']))
//[ 'A', 'B', 'C', 'D' ]
console.log(fp.split('_','hello_word'))
//[ 'hello', 'word' ]
关于调试时的案例通过fp优化
// 实现功能
// my name is tom 转化为 MY-NAME-IS-TOM
const fp = require('loadsh/fp')
const fn = fp.flowRight(fp.join('-'),fp.map((val)=>fp.toUpper(val)),fp.split(' '))
console.log(fn('my name is tom')) // MY-NAME-IS-TOM
实现一个小功能,将 [‘1’,‘2’,‘3’,‘4’] 转化为 [ 1, 2, 3, 4 ]
const _ = require('loadsh')
console.log(_.map(['1','2','3','4'],parseInt))
// [ 1, NaN, NaN, NaN ]
原因分析
接管执行过程
const _ = require('loadsh')
_.map(['1','2','3','4'],(val,index,arr)=>{
// 执行过程如下 会将三个参数都传入
return parseInt(val,index,arr)
})
//[ 1, NaN, NaN, NaN ]
改写执行过程
const _ = require('loadsh')
_.map(['1','2','3','4'],(val,index,arr)=>{
// 只传入一个参数
return parseInt(val)
})
// [ 1, 2, 3, 4 ]
const fp = require('loadsh/fp')
console.log(fp.map(parseInt)(['1', '2', '3', '4'])) //[ 1, 2, 3, 4 ]
console.log(fp.map(parseInt, ['1', '2', '3', '4'])) //[ 1, 2, 3, 4 ]
分下下执行过程以及一个参数为上限的含义
fp.map((val,index)=>{
console.log(val,index)
// 1 undefined
// 2 undefined
// 3 undefined
// 4 undefined
},['1', '2', '3', '4'])
fp.map 回调函数只传递了一个参数,所以就不会引发 parseInt 与预期结果不相同的问题了