导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1.函数式编程优势
  • 2.什么是函数式编程
  • 3.First-class Function(头等函数)
  • 4.闭包(closure)
  • 5. 纯函数
    • 5.1 什么是纯函数
    • 5.2 优点
      • 5.2.1 可缓存
      • 5.2.2 可测试
  • 6. 柯里化
  • 7. 组合
  • 8. Pointfree
  • 9. 函子
    • 9.1 Context
    • 9.2 Pointed Container
    • 9.3 Functor
    • 9.4 Maybe
    • 9.5 Either
    • 9.6 ap
    • 9.7 Monad 函子
    • 9.8 IO函子与副作用
    • 9.9 task函子
  • 10.实际应用
    • 10.1 react
    • 10.2 vue

1.函数式编程优势 #

  • 更少的时间
  • 更少的BUG
  • 更好的测试性
  • 更方便调试
  • 适合并发执行
  • 更高的复用性
  • 支持tree-shaking
  • React和Vue3大量使用函数式编程

2.什么是函数式编程 #

  • 函数式编程是一种编程范式
//面向过程
let a=1;
let b=2;
let result = a+b;


//面向对象
class Sum{
    add(a,b){
        return a+b;
    }
}
let sum = new Sum();
sum.add(1,2);

//函数式编程,这里的函数指的是一种映射关系  y=f(x)

function add(a,b){
    return a+b;
}
add(1,2);

3.First-class Function(头等函数) #

  • 函数是头等函数
    • 函数可以赋值给变量
    • 函数可以作为参数
    • 函数可以作为返回值
  • 作为参数和返回值的函数被称为高阶函数,高阶函数是函数式编程的基础
function add(a,b){
    return a+b;
}
//1.函数可以赋值给变量
let add1 = add;

//2.函数可以作为参数
function exec(fn,a,b){
    fn(a,b);
}

//3.函数可以作为返回值
function exec(fn){
    return function(a,b){
        return fn(a,b);
    }
}

4.闭包(closure) #

  • 一个函数和对其周围状态的引用捆绑在一起,这样的组合就是闭包
  • 闭包让你可以在一个内层函数中访问到其外层函数的作用域的变量
function init() {
    var name = "hello";
    function showName() {
        debugger
        console.log(name);
    }
    return showName;
}
let showName = init();
showName();

20210731161528_1627719351356

5. 纯函数 #

5.1 什么是纯函数 #

  • 函数的返回结果只依赖于它的参数,相同的输入始终得到相同的输出
  • 函数执行过程里面没有副作用(一个函数执行过程对产生了外部可观察的变化那么就可以说这个函数是有副作用)
function add(a,b){
    return a+b;
}

let c = 1;
let d =2;
function add2(a,b){
    d++;//修改了外部变量
    return a+b+c;//计算结果依赖外部变量
}
add2();
console.log(d);

5.2 优点 #

5.2.1 可缓存 #

  • lodash.memoize
  • 创建一个会缓存 func 结果的函数。 如果提供了 resolver ,就用 resolver 的返回值作为 key 缓存函数的结果。 默认情况下用第一个参数作为缓存的 key。 func 在调用时 this 会绑定在缓存函数上
cnpm i lodash -S
let _ = require('lodash');
const add = (a, b) => {
    console.log('add');
    return a + b;
}
const resolver = (...args)=>JSON.stringify(args)

var memoize = function (func,resolver) {
    let cache = {};
    let memoized =  (...args) => {
        const key = resolver?resolver(...args):JSON.stringify(args);
        if (typeof cache[key] !== 'undefined') {
            return cache[key];
        } else {
            cache[key] = func(...args);
            return cache[key];
        }
    }
    memoized.cache = cache;
    return memoized;
};

//let memoizedAdd = _.memoize(add,resolver);
let memoizedAdd = memoize(add,resolver);
console.log(memoizedAdd(1,2));
console.log(memoizedAdd(1,2));
console.log(memoizedAdd.cache);
module.exports = memoizedAdd;

5.2.2 可测试 #

  • jestjs
cnpm install jest --save-dev

5.test.js

const sum = require('./5.js');
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
  expect(sum(1, 2)).toBe(3);
});
{
  "scripts": {
    "test": "jest"
  }
}

6. 柯里化 #

  • lodash.curry创建一个函数,该函数接收 func 的参数,要么调用func返回的结果,如果 func 所需参数已经提供,则直接返回 func 所执行的结果。或返回一个函数,接受余下的func 参数的函数,可以使用 func.length 强制需要累积的参数个数
let _ = require('lodash');
function add(a, b, c) {
    return a + b + c;
}
function curry(func) {
    let curried = (...args) => {
        if(args.length < func.length){
            return (...rest)=>curried(...args,...rest);
        }
        return func(...args);
    }
    return curried;
}
let curriedAdd = curry(add);
console.log(curriedAdd(1, 2, 3));
console.log(curriedAdd(1)(2, 3));
console.log(curriedAdd(1)(2)(3));

7. 组合 #

  • flow创建一个函数,每一个连续调用,传入的参数都是前一个函数返回的结果
  • flowRight类似flow,除了它调用函数的顺序是从右往左的。
  • redux compose
  • lodashjs是一个一致性、模块化、高性能的 JavaScript 实用工具库
  • lodash/fp中的函数数据放在后后
  • ramdajs

compose_1627620541868

手工组合

function add1(str) {
    return str + 1;
}
function add2(str) {
    return str + 2;
}
function add3(str) {
    return str + 3;
}
//手工组合
console.log(add3(add2(add1('hello'))));

flow

//let {flow} = require('lodash');
function add1(str) {
    return str + 1;
}
function add2(str) {
    return str + 2;
}
function add3(str) {
    return str + 3;
}
//手工组合
function flow(...fns) {
    if (fns.length == 0)
        return fns[0];
    return fns.reduceRight((a, b) => (...args) => a(b(...args)));
}
let flowed = flow(add3, add2, add1);
console.log(flowed('zhufeng'));

flowRight

let {flowRight} = require('lodash');
function add1(str) {
    return str + 1;
}
function add2(str) {
    return str + 2;
}
function add3(str) {
    return str + 3;
}
function flowRight(...fns) {
    if (fns.length == 0)
        return fns[0];
    return fns.reduce((a, b) => (...args) => a(b(...args)));
}
let flowed = flowRight(add3, add2, add1);
console.log(flowed('zhufeng'));

//带参数的函数组合

let {split,toUpper,join} = require('lodash');
let str = 'click button';//CLICK_BUTTON

let r1 = split(str,' ');
console.log(r1);//[ 'click', 'button' ]
let r2 = toUpper(r1);
console.log(r2);//CLICK,BUTTON
let r3 = split(r2,',');
console.log(r3);//[ 'CLICK', 'BUTTON' ]
let r4 = join(r3,'_');
console.log(r4);//CLICK_BUTTON

数据先放

let {split,toUpper,join,flowRight} = require('lodash');
let str = 'click button';//CLICK_BUTTON
//loadsh数据放前面
const func = flowRight(join('_'),split(','), toUpper, split(' '));
console.log(func(str));

数据后放

let {split,toUpper,join,flowRight} = require('lodash/fp');
let str = 'click button';//CLICK_BUTTON
//fp数据放后
const func = flowRight(join('_'),split(','), toUpper, split(' '));
console.log(func(str));

过程跟踪

let {split,toUpper,join,flowRight} = require('lodash/fp');
let str = 'click button';//CLICK_BUTTON
const logger = (name) => value => {
    console.log(name, value);
    return value;
}
const func = flowRight(join('_'),logger('afterSplit'),split(','),logger('afterToUpper'), toUpper, split(' '));
console.log(func(str));

8. Pointfree #

  • 把数据处理的过程先定义成一种与参数无关的合成运算就叫Pointfree
  • 先想怎么花钱
const { compose } = require("lodash/fp");
let num = 1;
//Pointed
function calcu(num){
    return num+1+2+3;
}

function add1(num){
    return num+1
}
function add2(num){
    return num+2
}
function add3(num){
    return num+3
}
//Pointfree
let fn = compose(add3,add2,add1);
console.log(fn(1));

9. 函子 #

  • 可以用来管理值和值的变化过程
  • 把异常和异步操作等副作用控制在可控的范围之内

9.1 Context #

  • 如果一个对象内部持有一个值那么它就可以称为容器(Container)
class Container{
    constructor(value){
        this.value = value;
    }
}

9.2 Pointed Container #

  • 如果它有of方法可称为有指向的容器
class PointedContainer{
    constructor(value){
        this.value = value;
    }
    static of (value){
        return new PointedContainer(value);
    }
}

9.3 Functor #

  • 如果它有map方法可称为Functor(函子)
  • 函子一会有一个静态的of方法,用来生成实例
  • 函子内部会保存一个值value
  • 函子提供map方法,接入各种运算函数,从而引发值的变化
class Functor{
    constructor(value){
        this.value = value;
    }
    static of (value){
        return new Functor(value);
    }
    map(fn){
        return  new Functor(fn(this.value));
    }
}

let result = Functor.of('a')
.map(x=>x+1)
.map(x=>x+2)
.map(x=>x+3);
console.log(result.value);

9.4 Maybe #

  • 容器内部的值可能是一个空值,而外部函数未必有处理空值的机制,如果传入空值,很可能就会出错
  • Maybe函子可以过滤空值,能过滤空值的函子被称为Maybe函子
class Maybe  {
    constructor(value){
        this.value = value;
    }
    static of (value){
        return new Maybe(value);
    }
    map(fn){
        return  this.value?new Maybe(fn(this.value)):this;
    }
}
Maybe.of(null).map(x=>x.toString())

9.5 Either #

  • Either 函子内部有两个值: 左值Left)和右值(Right),右值是正常情况下使用的值,左值是右值不存在时使用的默认值
  • 常用来设置默认值和处理异常
class Either{
    constructor(left, right) {
        this.left = left;
        this.right = right;
    }
    static of = function (left, right) {
        return new Either(left, right);
    };
    map(fn) {
        return this.right ?
            Either.of(this.left, fn(this.right)) :
            Either.of(fn(this.left), this.right);
    }
    get value(){
        return this.right||this.left;
    }
}
//处理默认值
let user = {gender:null};
let result = Either.of('男',user.gender)
.map(x=>x.toUpperCase())
console.log(result.value);

//处理异常
function parse(str){
    try{
        return Either.of(null,{data:JSON.parse(str)});
    }catch(error){
        return Either.of({error:error.message},null);
    }
}
console.log(parse("{}").value);
console.log(parse("{x}").value);

9.6 ap #

  • ap(applicative)的函子拥有ap方法
  • ap方法可以让一个函子内的函数使用另一个函子的值进行计算
  • ap方法的参数不是函数,而是另一个函子
class Ap{
    constructor(value){
        this.value = value;
    }
    static of (value){
        return new Ap(value);
    }
    map(fn){
        return  new Ap(fn(this.value));
    }
    ap(functor) {
        return Ap.of(this.value(functor.value));
    }
}

const A = Ap.of(x=>x+1);
const B = Ap.of(1)
let result = A.ap(B);
console.log(result);

9.7 Monad 函子 #

  • 函子的值也可以是函子,这样会出现多层函子嵌套的情况
  • Monad(单子[不可分割的实体]) 函子的作用是,总是返回一个单层的函子
  • 它有一个flatMap方法,与map方法作用相同,唯一的区别是如果生成了一个嵌套函子,它会取出后者内部的值,保证返回的永远是一个单层的容器,不会出现嵌套的情况

函子嵌套

class Functor {
    constructor(value) {
        this.value = value;
    }
    static of(value) {
        return new Functor(value);
    }
    map(fn) {
        return new Functor(fn(this.value));
    }
}

let result = Functor.of('a')
   .map(x=>Functor.of(x+1))
   .map(x=>Functor.of(x.value+2))
   .map(x=>Functor.of(x.value+3))
console.log(result.value.value);
let r1 = [1,2,3].map(item=>[item+1]);
console.log(r1);//[ [ 1, 1 ], [ 2, 2 ], [ 3, 3 ] ]
console.log(r1[0][0]);
let r2 = [1,2,3].flatMap(item=>[item+1]);
console.log(r2);
console.log(r2[0]);
class Monad {
    constructor(value) {
        this.value = value;
    }
    static of(value) {
        return new Monad(value);//a
    }
    map(fn) {
        return new Monad(fn(this.value));
    }
    join() {
        return this.value;
    }
    flatMap(fn){
        return this.map(fn).join();
    }
}

/* let result = Monad.of('a')
   .flatMap(x=>{
       let r = Monad.of(x+1);
       r.name = 'x1';
       return r;
   })//new Monad('a1')); 返回的Monad.value=Monad.of(x+1),后面用的是Monad.of(x+1)
console.log(result); */

let result = Monad.of('a')
   .flatMap(x=>Monad.of(x+1))
   .flatMap(x=>Monad.of(x+2))
   .flatMap(x=>Monad.of(x+3))
console.log(result.value);
//flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组
let r1 = Array.of(1,2,3).map(x=>x+1);
console.log(r1);
let r2 = Array.of(1,2,3).map(x=>[x+1]);
console.log(r2);
let r3 = Array.of(1,2,3).flatMap(x=>[x+1]);
console.log(r3);

9.8 IO函子与副作用 #

  • 副作用就是程序和外部世界的交互,比如读取文件或调用接口
  • 由于外部世界不可控,包含副作用的逻辑往往不要预测
  • 函数式编程提倡把副作用分离出来,让没有副作用的纯逻辑们放在一起远离包含副作用的逻辑,这时就需要IO Monad
  • IO 就是 Input/Output,副作用无非是对外部世界的 Input(读)和 Output(写)
  • IO 函子通过推迟执行的方式来实现对副作用的管理和隔离

过程调用

const { compose } = require("lodash/fp");
let localStorage = {
    getItem(key){
        if(key === 'data')
            return `{"code":0,"userId":"1"}`;
        else if(key === "1"){
            return `{"id":1,"name":"zhangsan","age":18}`;
        }
    }
}
 function printUsers() {
    const response = localStorage.getItem('data');
    const data = JSON.parse(response);
    const users = data.userId;
    const user = localStorage.getItem(data.userId);
    console.log(user);//输出副作用
}
printUsers();

IO函子

const { compose } = require("lodash/fp");
let localStorage = {
    getItem(key){
        if(key === 'data')
            return `{"code":0,"userId":"1"}`;
        else if(key === "1"){
            return `{"id":1,"name":"zhangsan","age":18}`;
        }
    }
}
class IO {
    constructor(value) {
        this.value = value;
    }
    map(fn) {
        return new IO(compose(fn, this.value));
    }
    join() {
        return this.value();
    }
    start(callback) {
        callback(this.value());
    }
}
const readByKey = key => new IO(() => localStorage.getItem(key));
const parseJSON = string => JSON.parse(string);
const writeToConsole = console.log;
readByKey('data')
    .map(parseJSON)
    .start(writeToConsole);

链式调用

const { compose } = require("lodash/fp");
let localStorage = {
    getItem(key){
        if(key === 'data')
            return `{"code":0,"userId":"1"}`;
        else if(key === "1"){
            return `{"id":1,"name":"zhangsan","age":18}`;
        }
    }
}
class IO {
    constructor(value) {
        this.value = value;
    }
    map(fn) {
        return new IO(compose(fn, this.value));
    }
    flatMap(fn) {
        return new IO(compose(x=>x.value(), fn, this.value));
    }
    start(callback) {
        callback(this.value());
    }
}
const readByKey = key => new IO(() => localStorage.getItem(key));
const parseJSON = string =>  JSON.parse(string)
const writeToConsole = console.log;
let ret = readByKey('data')
    .map(parseJSON)
    .map(item=>item.userId)
    .flatMap(readByKey)
    .map(parseJSON)
    .start(writeToConsole);;

9.9 task函子 #

  • Task 函子通过类似 Promise 的 resolve 的风格来声明一个异步流程
  • FP 中除了容器(Container),也可以用上下文(Context)来称呼包裹了一个值的结构
  • Promise的任务是立刻执行的,而Task是在调用的时候才开始执行

异步执行任务

const Task = execute => ({
    execute
});
function get(url) {
    return Promise.resolve({ "code": 0, "userId": "1" });
}
const request = url => Task((resolve,reject) => get(url).then(resolve,reject));
request('data')
    .execute(user => console.log(user),error => console.error(error));

实现map

const Task = execute => ({
    execute,
    map: fn => Task(resolve => execute(x => resolve(fn(x))))
});
function get(url) {
    return Promise.resolve({ "code": 0, "userId": "1" });
}
const request = url => Task((resolve,reject) => get(url).then(resolve,reject));
request('data')
    .map(x => x.userId)
    .execute(user => console.log(user),error => console.error(error));

实现 flatMap

const Task = execute => ({
    map: fn => Task(resolve => execute(x => resolve(fn(x)))),
    flatMap: fn => Task(resolve => execute(x => fn(x).execute(resolve))),
    execute
});
function get(url) {
    if (url === 'data')
        return Promise.resolve({ "code": 0, "userId": "1" });
    else if (url === "1") {
        return Promise.resolve({ "id": 1, "name": "zhangsan", "age": 18 });
    }
}
const request = url => Task(resolve => get(url).then(resolve));
request('data')
    .map(x => x.userId)
    .flatMap(request)
    .map(x => x.name)
    .execute(user => console.log(user));

10.实际应用 #

10.1 react #

  • components
  • redux compose

10.2 vue #

  • function-api
  • vue2是将mounted,data,computed,watch之类的方法作为一个对象的属性进行导出。
  • vue3新增了一个名为setup的入口函数,value, computed, watch, onMounted等方法都需要从外部import
<template>
  <div>
    <span>count is {{ count }}</span>
    <span>plusOne is {{ plusOne }}</span>
    <button @click="increment">count++</button>
  </div>
</template>

<script>
import { value, computed, watch, onMounted } from 'vue'
export default {
  setup() {
    // reactive state
    const count = value(0)
    // computed state
    const plusOne = computed(() => count.value + 1)
    // method
    const increment = () => { count.value++ }
    // watch
    watch(() => count.value * 2, val => {
      console.log(`count * 2 is ${val}`)
    })
    // lifecycle
    onMounted(() => {
      console.log(`mounted`)
    })
    // expose bindings on render context
    return {
      count,
      plusOne,
      increment
    }
  }
}
</script>

访问验证

请输入访问令牌

Token不正确,请重新输入