闭包
- 函数嵌套,内部函数使用到外部函数变量,形成闭包
- 作用 ----------- 延长函数内部变量的生命周期
- 本质 ----------- 函数执行完毕会从执行栈移除函数,但闭包中内部函数的堆内存存在变量的引用,不会立即销毁,可以访问到外部函数的变量
//闭包
function makeFun(){
const msg = 'hello word'
return function(){
console.log(msg)
}
}
makeFun()()
纯函数
//纯函数
let array = [1,2,3,4,5,6]
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 memoize(callback) {
let cache = {
}
return function () {
let key = JSON.stringify(arguments)
cache[key] = cache[key] || callback.apply(callback, arguments)
return cache[key]
}
}
//相同输入函数只执行一次
function getArea(r) {
console.log('执行')
return r * r
}
const res = memoize(getArea)
console.log(res(2))
console.log(res(2))
console.log(res(2))
副作用
//副作用
let mini = 18
function checkAge(age) {
return age>=mini
}
//纯函数
function checkAge(age) {
let mini = 18
return age>=mini
}
- 数据库
- 配置文件
- 用户输入
柯里化
- 柯里化 函数有多个参数,先传递一部分调用,然后返回新的函数接受剩余参数。返回结果
//普通函数
function checkAge(min) {
return function (age) {
return age >= min
}
}
//箭头函数写法
let checkAge = min => (age => age >= min)
let age18 = checkAge(18)
let age20 = checkAge(20)
console.log(age18(24))
console.log(age20(19))
lodash中的柯里化函数curry
- lodash柯里化 curry函数如果接受函数全部参数,则返回函数执行结果,如果接受部分参数,则返回一个函数去接受剩余参数,直到参数接受完毕返回执行结果
function getSum(a, b, c) {
return a + b + c
}
const curry = _.curry(getSum)
console.log(curry(2,3,4)) //输出9
console.log(curry(2)(3,4)) //输出9
console.log(curry(2,3)(4)) //输出9
console.log(curry(2)(3)(4)) //输出9
//数组柯里化案例
const match = _.curry(function (reg, str) {
return str.match(reg)
})
const hasSpace = match(/\s+/g)
const hasNumber = match(/\d+/g)
const filter = _.curry(function (callback, arr) {
return arr.filter(callback)
})
const findSpace = filter(hasSpace)
console.log(filter(hasSpace, ['Tom Smith', 'Bob'])) //输出 [ 'Tom Smith' ]
console.log(findSpace(['Tom Smith', 'Bob'])) //输出 [ 'Tom Smith' ]
loadsh carry函数原理
//loadsh curry原理
function curryied(func) {
return function curriedFn(...args) {
//判断实参与形参个数
if (args.length < func.length) {
return function () {
return curriedFn(...args.concat(Array.from(arguments)))
}
}else{
return func(...args)
}
}
}
function getSum(a, b, c) {
return a + b + c
}
const curry = curryied(getSum)
console.log(curry(2, 3, 4)) //输出9
console.log(curry(2)(3, 4)) //输出9
console.log(curry(2, 3)(4)) //输出9
console.log(curry(2)(3)(4)) //输出9
函数组合
- 一个函数由多个函数处理得到结果,把多个函数合并成一个函数的过程
- 默认从右往左执行
//函数组合
function compose(f, g) {
return function (value) {
return f(g(value))
}
}
function reverse(arr) {
return arr.reverse()
}
function first(arr) {
return arr[0]
}
const last = compose(first,reverse)
console.log(last([1,2,3,8]))
//函数组合
function compose(...args) {
return function (value) {
return args.reverse().reduce(function (acc, f) {
return f(acc)
}, value)
}
}
//箭头函数写法
const compose = (...args) => value => args.reverse().reduce((acc, f) => f(acc),value)
const reverse = arr => arr.reverse()
const first = arr => arr[0]
const toUpper = str => str.toUpperCase()
const fn = compose(toUpper, first, reverse) //满足结合律
const fn1 = compose(compose(toUpper, first), reverse) //满足结合律
const fn2 = compose(toUpper, first, compose(reverse)) //满足结合律
console.log(fn(['join', 'tom', 'hzy'])) //输出HZY
console.log(fn1(['join', 'tom', 'hzy'])) //输出HZY
console.log(fn2(['join', 'tom', 'hzy'])) //输出HZY
}
组合函数调试
//组合函数调试
// NENVER SAY NO => never-say-no
const split = _.curry((sep, str) => _.split(str, sep))
const join = _.curry((sep, arr) => _.join(arr, sep))
const map = _.curry((fn, arr) => _.map(arr, fn))
// const log = str => {
// console.log(str)
// return str
// }
const trace = _.curry((tag, v) => {
console.log(tag, v)
return v
})
//const f= _.flowRight(join('-'),log,_.toLower,split(' '))
const f = _.flowRight(join('-'), trace('在map之后打印'), map(_.toLower), split(' '))
console.log(f('NENVER SAY NO'))
- lodash 里fp模块提供了柯里化函数 函数优先 数据滞后
//loadsh fp模块 函数优先 数据滞后
const fp = require('lodash/fp')
const res = fp.map(fp.toLower, ['A', 'B', 'C']) //fp函数都是柯里化的函数
const fn = fp.map(fp.toLower)
const res2 = fn(['A', 'B', 'C'])
console.log(res)
console.log(res2)
const f = fp.flowRight(fp.join('-'),fp.map(fp.toLower), fp.split(' ')) // never-say-no
console.log(f('NENVER SAY NO'))
//lodash map与fp模块区别
console.log(_.map(['23', '8', '10'], parseInt)) //输出23 NAN 2
//执行过程 parseInt('23',0,['1','2','3']) //十进制 23 转10进制 =>23
//执行过程 parseInt('8',1,['1','2','3']) // 不支持进制
//执行过程 parseInt('10',2,['1','2','3']) //10转二进制 => 2
const fp = require('lodash/fp')
console.log(fp.map(parseInt,['23', '8', '10'])) //fp模块函数参数只接受一个当前处理的元素
- Point Free 本质是函数组合 不需要指明处理的数据 只需要合成运算过程 只需要定义辅助基本运算函数
const fp = require('lodash/fp')
const f = fp.flowRight(fp.join('.'), fp.map(item => {
item = item.substring(0, 1).toUpperCase() + item.substring(1)
return item
}), fp.split(' '))
console.log(f('hello world'))
// hello world => H.W
const f2 = fp.flowRight(fp.join('.'), fp.map(fp.flowRight(fp.first,fp.toUpper)), fp.split(' '))
console.log(f2('hello world'))
函子
- 实现了map的对象,函数式编程不直接操作值,而是由函子完成
//函子
class container {
static of(value){
return new container(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return container.of(fn(this._value))
}
}
const r = container.of(100).map(value=>value+1).map(value=>value+2)
console.log(r)//输出103
MayBe函子 Either函子 IO函子
class MayBe {
static of(value) {
return new MayBe(value)
}
constructor(value) {
this._value = value
}
isNone(){
return this._value===null||this._value===undefined
}
map(fn) {
return this.isNone?MayBe.of(null):MayBe.of(fn(this._value))
}
}
console.log(MayBe.of(null).map(item=>item.toUpperCase())) //MayBe { _value: null }
class Left {
static of(value) {
return new Left(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return this
}
}
class Right extends Left{
static of(value) {
return new Right(value)
}
constructor(value) {
super(value)
}
map(fn) {
return Right.of(fn(this._value))
}
}
function parseJson(str){
try{
return Right.of(JSON.parse(str))
}catch(e){
return Left.of({error:e.message})
}
}
console.log( Right.of(10).map(item=>item+1))
console.log( Left.of(10).map(item=>item+1))
console.log( parseJson('{a:"1"}'))
// Left {_value: { error: 'Unexpected token a in JSON at position 1' } }
- IO函子 把不纯的函数存储到value中,延迟执行,执行交给调用者处理
//IO函子
class IO{
static of(x) {
return new IO(function(){
return x
})
}
constructor(fn) {
this._value = fn
}
map(fn) {
//当前_value和传入的fn组合成新的函数
return new IO(fp.flowRight(fn,this._value))
}
}
let fn = IO.of(10).map(item=>item+1)
console.log( fn._value())
folktale 函数库
//folktale 函数库 提供函数式处理操作 函子
const {compose,curry} = require('folktale/core/lambda')
const {toUpper,first} = require('lodash/fp')
const f = curry(2,(x,y)=>{ //第一个参数是函数参数数量
return x+y
})
console.log(f(1)) // [Function]
console.log(f(1,2)) //3
const f2 = compose(toUpper,first)
console.log(f2(['hello','world'])) //HELLO
// folktale Task异步任务 返回一个函子
const { task } = require('folktale/concurrency/task')
const { split, find } = require('lodash/fp')
const fs = require('fs')
function readFile(fileName) {
return task(resolver => {
fs.readFile(fileName, 'utf-8', (err, data) => {
if(err) resolver.reject()
resolver.resolve(data)
})
})
}
readFile('package.json')
.map(split('\n'))
.map(find(item=>item.includes('version')))
.run()
.listen({
onRejected:err=>{
console.log(err)
},
onResolved:data=>{
console.log(data)
}
})
const fp = require('lodash/fp')
const fs = require('fs')
class IO {
static of(x) {
return new IO(function () {
return x
})
}
constructor(fn) {
this._value = fn
}
map(fn) {
//当前_value和传入的fn组合成新的函数
return new IO(fp.flowRight(fn, this._value))
}
}
const readFile = filename => {
return new IO(() => {
return fs.readFileSync(filename, 'utf-8')
})
}
const print = x => {
return new IO(() => {
console.log('***')
console.log(x)
console.log('***')
return x
})
}
const cat = fp.flowRight(print,readFile)
let r= cat('package.json')._value()._value()
console.log(r)
//Monad函子 包含of join两个方法
const fp = require('lodash/fp')
const fs = require('fs')
class IO {
static of(x) {
return new IO(function () {
return x
})
}
constructor(fn) {
this._value = fn
}
join(){
return this._value()
}
flatMap(fn){
return this.map(fn).join()
}
map(fn) {
//当前_value和传入的fn组合成新的函数
return new IO(fp.flowRight(fn, this._value))
}
}
const readFile = filename => {
return new IO(() => {
return fs.readFileSync(filename, 'utf-8')
})
}
const print = x => {
return new IO(() => {
console.log('***')
console.log(x)
console.log('***')
return x
})
}
let r= readFile('package.json').flatMap(print).join()
// readFile => { _value: () => {return fs.readFileSync(filename, 'utf-8') }}
console.log(r)