本文为拉勾网大前端高薪训练营第一期笔记
const BlogController = {
create(content){ return Db.create(content) },
}
等效
const BlogController = {
create: Db.create
}
函数作为返回值
意义:
在另一个作用域调用一个函数的内部函数,并访问到该函数的作用域中的成员
function makeFn(){
let msg = 'Hello Function'
return function() {
console.log(msg)
}
}
const fn = makeFn()
fn()
function once(fn) {
let done = false
return function (){
if(!done){
done = true
return fn.apply(this, arguments)
}
}
}
let pay = once(function (money){
console.log(`pay: ${money}`)
})
pay(5)
pay(5)
函数在执行的时候会放在一个执行栈上,当函数执行完毕之后会从执行栈上移除,但是堆上的作用域成员因为被外部引用不能释放,因为内部函数依然可以访问外部函数的成员
相同的输入永远得到相同的输出
纯函数的好处两点
例如
const getAreaWithMemory = _.memoize(getArea)
副作用:如果有外部变量参与纯函数运算,就会把纯函数变成不纯
把多个参数的函数,变成函数包函数,第一次只传部分不怎么变化的参数,然后第二次传剩下函数。
const sum = _.curry((a,b,c)=>a+b+c)
sum(1,2,3)
sum(1)(2,3)
sum(2,3)(1)
const curry = (fn) => {
return function curriedFn(...args){
if(args.length < fn.length){
return function(){
return fn(...args.concat(Array.from(arguments)))
}
} else {
return fn(...args)
}
}
}
const sum = curry((a,b,c)=>a+b+c)
console.log(sum(1,2,3))
console.log(sum(1,2)(3))
fn.length能拿到形参的数量
_.flow() _.flowRight()后者是从右向左执行
const flowRight = function (...args) {
return function (value) {
return args.reverse().reduce((acc, fn)=>{
fn(acc)
}, value)
}
}
//单行写法
const flowRight = (...args) => value=> {return args.reverse.reduce(()=>{fn(acc)}, value)}
compose(f,g,h)等效compose(compose(f,g), h)等效compose(f, compose(g,h))
函数组合传入的都需要是一元函数,如果是二元或者更多就需要柯里化变成一元,调试的时候可以加log在函数组合之中
const log = v => {
console.log(v)
return v
}
//tag版本的log
const trace = _.curry((tag, v) => {
console.log(tag, v)
return v
})
trace('tag')
const fp = require(‘lodash/fp’)
fp.map(_.toLower)(array)
自带柯里化和参数的函数优先,数值后置
fp.map只会把数据传到下一个函数里,不会带上index collection
容器:包含值和值的变形关系(这个变形关系就是函数)
函子:是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系)
我的理解为用容器包数值,然后通过map传运算函数,最后返回新的容器,不断重复这个过程
maybe函子是处理了null和undefined的情况
either函子是处理了有错误的情况,可以把错误传到最后
IO函子是返回处理的function,不传值,最后运行的时候才生成值,函子部分永远是纯的,因为返回的是运算本身,不纯的操作甩锅到最后执行
folktale库可以有compose curry,也制造task函子,使用起来像spark
const { compose, curry } = require('folktale/core/lambda')
// Task 处理异步任务
const fs = require('fs')
const { task } = require('folktale/concurrency/task')
const { split, find } = require('lodash/fp')
function readFile (filename) {
return task(resolver => {
fs.readFile(filename, 'utf-8', (err, data) => {
if (err) resolver.reject(err)
resolver.resolve(data)
})
})
}
readFile('package.json')
.map(split('\n'))
.map(find(x => x.includes('version')))
.run()
.listen({
onRejected: err => {
console.log(err)
},
onResolved: value => {
console.log(value)
}
})
pointed函子是实现了of静态方法的函子
避免使用new创建函子,更深层的含义是of方法用来把值放在上下文Context(把值放在函子,用map处理值)
Monad函子就是变扁的Pointed函子,一个函子如果同时具有join和of两个方法并遵守一些定律就是一个Monad
我的理解是当一个函子返回一个函子时,就flatMap,解决函子嵌套的问题,flatMap里就是把map合并操作和返回函子内部值join连续操作,如果普通的fp.toUpper这样的函数就直接用map就好了
错题分析
函数式编程不能提高性能
副作用也就是外界参数参与纯函数,使得纯函数变得不纯,不可避免
本文为拉勾网大前端高薪训练营第一期笔记