相关资料
函数式编程
编程范式之一,与面向过程编程、面向对象编程并列 是一种编程思想 函数式编程中的函数不是指程序中的函数(方法),而是数学中的映射关系(函数)
函数是一等公民
MDN解释 函数可以储存在变量中 函数作为参数 函数作为返回值
函数
高阶函数
函数作为参数传递给另一个函数 函数作为另一个函数的返回结果
闭包
从另一个作用域中调用一个函数的内部函数,并且该内部函数可以访问到其作用域下的成员属性 本质:
函数在执行时,会被放到一个执行栈上。等执行完毕后,该函数会从栈上移除。 但,该函数中的作用域成员若被外部引用,则该成员在堆中不会被释放。 因此,内部函数依然可以访问该函数的成员。
function makePower ( power) {
return function ( x) {
return Math. pow ( x, power)
}
}
let power2 = makePower ( 2 )
let power3 = makePower ( 3 )
console. log ( power2 ( 4 ) )
console. log ( power2 ( 5 ) )
console. log ( power3 ( 4 ) )
console. log ( power3 ( 5 ) )
纯函数
概念:相同的输入永远有相同的输出,类似于数学中的函数
const _ = require ( "lodash" )
let array = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
console. log ( array. slice ( 0 , 3 ) )
console. log ( array. slice ( 0 , 3 ) )
console. log ( array. slice ( 0 , 3 ) )
console. log ( array. splice ( 0 , 3 ) )
console. log ( array. splice ( 0 , 3 ) )
console. log ( array. splice ( 0 , 3 ) )
作用:因输入输出始终保持对应关系,所以可以对输出结果缓存,提高程序性能问题
function getArea ( r) {
console. log ( `半径为: ${ r} ` )
return Math. PI * Math. pow ( r, 2 )
}
console. log ( getArea ( 3 ) )
console. log ( getArea ( 3 ) )
console. log ( getArea ( 3 ) )
let afterMemorize = _. memoize ( getArea)
console. log ( afterMemorize ( 3 ) )
console. log ( afterMemorize ( 3 ) )
console. log ( afterMemorize ( 3 ) )
function memoize ( fn) {
let cache = { }
return function ( ) {
let key = JSON . stringify ( arguments)
cache[ key] = cache[ key] || fn. apply ( fn, arguments)
return cache[ key]
}
}
function getArea ( r) {
console. log ( `半径为: ${ r} ` )
return Math. PI * Math. pow ( r, 2 )
}
let afterMemorize = memoize ( getArea)
console. log ( afterMemorize ( 3 ) )
console. log ( afterMemorize ( 3 ) )
console. log ( afterMemorize ( 3 ) )
副作用
当程序收到外部状态的影响时,可能会出现不纯函数 副作用来源:
柯里化
将多元函数转化成一元函数 对函数参数的缓存 让函数更灵活,让函数粒度更小
console. log ( `-------------普通函数` )
const checkAge = ( min, max, age) => {
return ` ${ age} 岁范围在 ${ min} ~ ${ max} 之间: ${ age >= min && age <= max} `
}
console. log ( checkAge ( 18 , 40 , 56 ) )
console. log ( checkAge ( 18 , 40 , 34 ) )
console. log ( checkAge ( 18 , 40 , 40 ) )
console. log ( `-------------柯里化函数` )
function checkAgeCurry ( min, max) {
return function ( age) {
return ` ${ age} 岁范围在 ${ min} ~ ${ max} 之间: ${ age >= min && age <= max} `
}
}
const ageRange1 = checkAgeCurry ( 18 , 40 )
const ageRange2 = checkAgeCurry ( 30 , 40 )
console. log ( ageRange1 ( 56 ) )
console. log ( ageRange1 ( 20 ) )
console. log ( ageRange1 ( 40 ) )
console. log ( ageRange2 ( 56 ) )
console. log ( ageRange2 ( 20 ) )
console. log ( ageRange2 ( 40 ) )
console. log ( `-------------案例1:检查年龄是否在范围内` )
const _ = require ( "lodash" )
const checkAgeCurry = _. curry ( ( min, max, age) => {
return ` ${ age} 岁范围在 ${ min} ~ ${ max} 之间: ${ age >= min && age <= max} `
} )
const ageRange1 = checkAgeCurry ( 18 , 40 )
const ageRange2 = checkAgeCurry ( 30 , 40 )
console. log ( ageRange1 ( 56 ) )
console. log ( ageRange1 ( 20 ) )
console. log ( ageRange1 ( 40 ) )
console. log ( ageRange2 ( 56 ) )
console. log ( ageRange2 ( 20 ) )
console. log ( ageRange2 ( 40 ) )
console. log ( `-------------案例2:返回符合正则表达式的值` )
const _ = require ( "lodash" )
const matchReg = _. curry ( ( reg, str) => {
return str. match ( reg)
} )
const matchSpace = matchReg ( /\s+/g )
const matchNumber = matchReg ( /\d+/g )
console. log ( "#匹配单个字符" )
console. log ( matchSpace ( 'Marry_Jeckson' ) )
console. log ( matchSpace ( 'Marry Jeckson' ) )
console. log ( matchNumber ( '$twenty-two' ) )
console. log ( matchNumber ( '$22' ) )
console. log ( "#匹配数组" )
let array = [ 'Marry_Jeckson' , 'Cheliry Curry' , "32" , "4" ]
const filterCurry = _. curry ( ( fn, array) => {
return array. filter ( fn)
} )
let filterSpace = filterCurry ( matchSpace)
let filterNumber = filterCurry ( matchNumber)
console. log ( filterSpace ( array) )
console. log ( filterNumber ( array) )
const curry = ( fn) => {
return function fnSub ( ... args) {
if ( args. length < fn. length) {
return ( ... currArgs) => {
return fnSub ( ... args. concat ( currArgs) )
}
} else {
return fn ( ... args)
}
}
}
const checkAgeCurry = curry ( ( min, max, age) => {
return ` ${ age} 岁范围在 ${ min} ~ ${ max} 之间: ${ age >= min && age <= max} `
} )
const ageRange1 = checkAgeCurry ( 18 , 40 )
const ageRange2 = checkAgeCurry ( 30 , 40 )
console. log ( ageRange1 ( 56 ) )
console. log ( ageRange1 ( 20 ) )
console. log ( ageRange1 ( 40 ) )
console. log ( ageRange2 ( 56 ) )
console. log ( ageRange2 ( 20 ) )
console. log ( ageRange2 ( 40 ) )
函数组合
是指将过程函数合并成一个函数的过程 默认是从有到左执行 需满足结合律
console. log ( `------------------------------\n----函数组合概念\n------------------------------` )
const compose = ( f, g) => {
return ( x) => {
return f ( g ( x) )
}
}
const first = ( array) => {
return array[ 0 ] ;
}
const reverse = ( array) => {
return array. reverse ( )
}
let result = compose ( first, reverse)
console. log ( result ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 100 ] ) )
const _ = require ( "lodash" )
console. log ( `------------------------------\n----lodash中的组合函数\n------------------------------` )
const first = array => array[ 0 ] ;
const reverse = array => array. reverse ( ) ;
const toUpper = str => str. toUpperCase ( ) ;
const f = _. flowRight ( toUpper, first, reverse)
console. log ( f ( [ "one" , "two" , "three" ] ) )
console. log ( `------------------------------\n----模拟lodash中的flowRight方法\n------------------------------` )
const compose = ( ... fns) => {
return ( value) => {
return fns. reverse ( ) . reduce ( ( result, fn) => {
return fn ( result)
} , value)
}
}
const first = array => array[ 0 ] ;
const reverse = array => array. reverse ( ) ;
const toUpper = str => str. toUpperCase ( ) ;
const f = compose ( toUpper, first, reverse)
console. log ( f ( [ "one" , "two" , "three" ] ) )
const _ = require ( "lodash" )
console. log ( `------------------------------\n----函数组合要满足结合律\n------------------------------` )
const first = array => array[ 0 ] ;
const reverse = array => array. reverse ( ) ;
const toUpper = str => str. toUpperCase ( ) ;
const f1 = _. flowRight ( toUpper, _. flowRight ( first, reverse) )
const f2 = _. flowRight ( _. flowRight ( toUpper, first) , reverse)
console. log ( f1 ( [ "one" , "two" , "three" ] ) )
console. log ( f2 ( [ "one" , "two" , "three" ] ) )
const _ = require ( "lodash" )
console. log ( `------------------------------\n----调试组合函数\n------------------------------` )
const split = _. curry ( ( sep, str) => str. split ( sep) )
const join = _. curry ( ( sep, array) => array. join ( sep) )
const map = _. curry ( ( fn, array) => _. map ( array, fn) )
const log = _. curry ( ( type, val) => {
console. log ( type, val)
return val
} )
const f = _. flowRight ( join ( "-" ) , log ( "after map:" ) , map ( _. toLower) , log ( "after split:" ) , split ( " " ) , log ( "origin:" ) )
console. log ( f ( "ONE TWO THREE FOUR" ) )
Point Free(管道)
是指,将数据处理的过程定义成与数据无关的合成运算 也就是不会用到使用代表数据的变量 基本上就是组合函数 特点:
不需要指明处理的数据 只需要合成运算过程 需要一些基本的运算函数来辅助
函子
是一个特殊的容器,这个容器是由普通对象来实现 该对象有map方法,用来运行一个函数对值进行处理 最终返回一个包含新值的盒子(函子,或对象)
console. log ( `------------------------------\n----函子概念\n------------------------------` )
class Container {
constructor ( value) {
this . _value = value
}
map ( fn) {
return new Container ( fn ( this . _value) )
}
}
let result = new Container ( 5 )
. map ( x => x + 1 )
. map ( x => x * x)
console. log ( result)
console. log ( `------------------------------\n----另一种方式调用函子\n------------------------------` )
class Container {
constructor ( value) {
this . _value = value
}
static of ( value) {
return new Container ( value)
}
map ( fn) {
return Container. of ( fn ( this . _value) )
}
}
let result = Container. of ( 5 )
. map ( x => x + 1 )
. map ( x => x * x)
console. log ( result)
console. log ( `------------------------------\n----MayBe函子:对null的处理\n------------------------------` )
class MayBe {
constructor ( value) {
this . _value = value
}
static of ( value) {
return new MayBe ( value)
}
map ( fn) {
return this . isNothing ( this . _value) ? MayBe. of ( null ) : MayBe. of ( fn ( this . _value) )
}
isNothing ( value) {
return value === null || value === undefined
}
}
let result = MayBe. of ( "hello world" )
. map ( x => x. split ( " " ) )
. map ( x => x. reverse ( ) )
console. log ( result)
let result2 = MayBe. of ( null )
. map ( x => x. split ( " " ) )
. map ( x => x. reverse ( ) )
console. log ( result2)
console. log ( `------------------------------\n----Either函子:对异常进行处理\n------------------------------` )
class Error {
constructor ( value) {
this . _value = value
}
static of ( value) {
return new Error ( value)
}
map ( fn) {
return this ;
}
}
class Normal {
constructor ( value) {
this . _value = value
}
static of ( value) {
return new Error ( value)
}
map ( fn) {
return Normal. of ( fn ( this . _value) ) ;
}
}
const parseJson = ( json) => {
try {
return Normal. of ( JSON . parse ( json) )
} catch ( e ) {
return Error. of ( { error: e. message } )
}
}
console. log ( parseJson ( '{"name":"123"}' ) )
console. log ( parseJson ( "{name:213}" ) )
IO函子:处理操作的值为函数时
处理的值为函数 该值可能为不纯的函数 延迟执行这个不纯的操作 把不纯的操作交给调用者来处理
console. log ( `------------------------------\n----IO函子:处理操作的值为函数时\n------------------------------` )
class IO {
constructor ( fn) {
this . _value = fn
}
static of ( value) {
return new IO ( ( ) => {
return value
} )
}
map ( fn) {
return new IO ( fp. flowRight ( fn, this . _value) )
}
}
let io = IO . of ( process) . map ( p => p. execPath)
console. log ( io. _value ( ) )
console. log ( `------------------------------\n----task函子:异步执行\n------------------------------` )
const { curry } = require ( "folktale/core/lambda" )
const { task } = require ( "folktale/concurrency/task" )
const fs = require ( "fs" )
const { split, find, includes } = require ( "lodash/fp" )
const readFile = ( filename) => {
return task ( resolver => {
fs. readFile ( filename, "utf-8" , ( err, data) => {
if ( err) resolver. reject ( err)
resolver. resolve ( data)
} )
} )
}
const log = curry ( 2 , ( type, value) => {
console. log ( type, value)
return value
} )
readFile ( "./package.json" )
. map ( split ( "\n" ) )
. map ( log ( "after \\n" ) )
. map ( find ( x => x. includes ( "version" ) ) )
. run ( )
. listen ( {
onRejected: err => {
console. log ( err)
} ,
onResolved: value => {
console. log ( value)
}
} )
Monad函子:简化个别函子调用
简化 IO(IO()) 这种洋葱函子的调用 同时具有 of 和 join 方法并遵守一些定律的函子
console. log ( `------------------------------\n----Monad函子:简化个别函子调用\n------------------------------` )
const fp = require ( "lodash/fp" )
const fs = require ( "fs" )
{
console. log ( `----使用普通IO函子处理` )
class IO {
constructor ( fn) {
this . _value = fn
}
static of ( value) {
return new IO ( ( ) => {
return value
} )
}
map ( fn) {
return new IO ( fp. flowRight ( fn, this . _value) )
}
}
const readFile = ( filename) => {
return new IO ( ( ) => {
return fs. readFileSync ( filename, 'utf-8' )
} )
}
const printFile = ( x) => {
return new IO ( ( ) => {
console. log ( x)
return x
} )
}
let cat = fp. flowRight ( printFile, readFile)
let result = cat ( "package.json" ) . _value ( ) . _value ( )
console. log ( result)
}
{
console. log ( `----使用Monad函子处理` )
const fp = require ( "lodash/fp" )
const fs = require ( "fs" )
class IO {
constructor ( fn) {
this . _value = fn
}
static of ( value) {
return new IO ( ( ) => {
return value
} )
}
map ( fn) {
return new IO ( fp. flowRight ( fn, this . _value) )
}
join ( ) {
return this . _value ( )
}
flatMap ( fn) {
return this . map ( fn) . join ( )
}
}
const readFile = ( filename) => {
return new IO ( ( ) => {
return fs. readFileSync ( filename, "utf-8" )
} )
}
const printFile = ( x) => {
return new IO ( ( ) => {
console. log ( x)
return x
} )
}
const cat = readFile ( "package.json" )
. flatMap ( printFile)
. join ( )
console. log ( cat)
}