Skip to content

函数式

一、函数式编程的出现

  • 发展历程:
    1. 命令(脚本)式编程 => 一行代码为单元,逐步运行
    2. 面向对象编程 => 以对象为操作单位
    3. 函数式编程 => 以函数为单位

1.问题的出现 - 从面试题开始

js
    // 地址解析 - 浏览器原理 - 参数parse
    // 1. url中参数是如何展示数组的
    // location.search => "?name[]=0&name[]=1&name[]=2"
    // 2. 参数提取拼接数组
    // [0,1,2]
    // 3. 转换为数组对象做存储
    // [{value:0},{value:1},{value:2}]

    // 解:
    // 1. 字符串拆分数组 遍历
    // 2. 字符串 => key val 遍历

    const _array = [ 1 , 2 , 3]
    const _objArr = [];

    const valParser = (arr) => {
        const newObjArr = []
        arr.forEach(v=>{
            
        })
        return newObjArr
    }

    // 需求分析: 数组 > 数组对象 > [ 字符串 > 对象 ]
    const formatName = R.merge(join,map,split)
    const objHelper = R.merge(assembleObj("name"),formatName)
    const nameParser = map(objHelper)

    nameParser(_array)

    // 面试题:正确的使用遍历 - for forEach map filter sort...
    // 本质作用 => 通用遍历 | 遍历逻辑处理 | 生成数组-处理 | 生成数组-检索 | 排序

二、函数式编程原理特点

1.什么是函数式的原理

  • 加法结合律|因式分解|完全平方式 => 原子组合的变化

2.理论思想

a. 一等公民 - 函数 => 1.逻辑功能的落脚点 - 函数 2.实现函数 + 拼接流程

b. 声明式编程 => 声明需求 - 更贴近语言习惯 react vue3

c. 惰性执行 - 无缝衔接,性能节约

js
    // 惰性函数
    const program = name => {
        if(name == "progressive"){
            return program = ()=>{
                console.log("this is progressive")
            }
        } else if(name == "objective"){
            return program = ()=>{
                console.log("this is objective")
            }
        } else {
            return program = ()=>{
                console.log("this is funcional")
            }
        }
    }
    program("progressive")();
    program();

3. 无状态和无副作用

  • a. 无状态 - 幂等; 数据不可变 - 不可操作改变源数据
  • b. 无副作用 - 函数内部不可直接对系统中任何参数变量直接做改变

三、实际开发

1. 纯函数的改造

js
    const _class = {
        name: "objective"
    }

    // 函数内部引用了外部变量 - 无状态
    const score = str => _class.name + ":" + str;

    // 对传入的变量进行了修改 - 可能导致全局变量被修改
    const changeClass = (obj,name) => obj.name = name;

    changeClass(_class,"functional");
    score("goode");

    // #############
    import _ from "lodash";
     const _class = {
        name: "objective"
    }

    const score = (obj , str) => _.deepClone(obj).name + ":" + str;
    const changeClass = (obj,name) => {...obj,name}

    changeClass(_class,"functional");
    score("goode");

2. 流水线的组装 - 加工 & 组装

a.加工 - 柯里化

提前对方法中某些参数做固定,后续可以直接使用,比如说

js
function check(reg){
    return function(str){
        return reg.test(str);
    }
}
var validFunc = check(/\d+/g);
console.log(validFunc('test1'))
console.log(validFunc('test2'))
console.log(validFunc('test3'))
js
    const sum = (x,y) =>x+y
    sum(1,1)

    const add = x=>{
        return y=> x + y
    }
    add(1)(1);

    // 要实现 体系 = 加工 + 组装,单个加工的输入输出(单值化) => 单元函数
    const fetch = ajax(method,url,params);

    const fetch = ajax.get(url);
    const request = fecth(params);
    const fetch = ajax(method)(url)(params);

    组合(request, fetch)
  • 面试题:手写构造可拆分传参的累加函数
  • add(1)(2)(3)(4)
js
    // 1.构造柯里化的结构
    // 2.输入 处理外层arguments => 类数组
    // 3.传入参数,无限拓展 => 内层递归 => 返回递归函数本身
    // 4.完成主功能区
    // 5.输出 从函数到产出 toString的替换

    function add(val){
        // input
        let args = Array.prototype.slice.call(arguments);

        // 内层函数
        let inner = function(){
            args.push(...arguments);

            return inner;
        }

        inner.toString = function(){
            return args.reduce((prev,cur)=>{
                return prev + cur;
            },0)
        }

        return inner
    }

    "" + add(1)(2)(3)(4) 

    // 如何调用原来的toString => .call

再来一题:

js
// function add(a, b, c, d) {
//     return a + b + c + d;
// }
// 实现一个curry
// 使得以下两个输出10,如果参数个数不足则返回一个函数,再次调用该函数如果参数个数满足则输出 
// curry(add)(1)(2)(3)(4)
// curry(add, 1, 2, 3, 4)
function add(a, b, c, d) {
    return a + b + c + d;
}
function curry() {
    const _fn = arguments[0]
    const args = Array.prototype.slice.call(arguments, 1);
    if (args.length === _fn.length) {
        return _fn.apply(null, args);
    }
    function _curry() {
        args.push(...arguments)
        if (args.length === _fn.length) {
            return _fn.apply(null, args);
        }
        return _curry;
    }
    return _curry;
}
console.log(curry(add)(1)(2)(3)(4));
console.log(curry(add, 1, 2, 3, 4));
b.流水线
js
    const compose = (f,g) => x => f(g(x))

    const sum1 = x => x+1;
    const sum2 = x => x+2;
    const sum12 = compose(sum2,sum1);
    sum12(6);
  • 实际实现使用
    js
      // 命令式
      trim(reverse(toUpperCase(map(arr))))
      // 面向对象
      arr.map().toUpperCase().reverse().trim()
    
      // 函数式
      const result = compose(trim,reverse,toUpperCase,map)
      
      pipe() // history | grep rm

四、函子

js
    // 一封情书
    class Mail{
        constructor(content){
            this.content = content;
        }

        map(fn){
            return new Mail(fun(this.content));
        }
    }

    
    // 1.写情书
    let mail1 = new Mail("love");
    
    // 2.读了信
    let mail2 = mail1.map(function(mail){
        return read(mail)
    })

    // 3.涂抹了
    let mail3 = mail2.map(function(mail){
        return cross(mail)
    })

    // 4.别人查看的时候
    mail3.map(function(mail){
        return read(mail)
    })

    // 链式
    new Mail("love").map(read).map(cross).map(read);

    // Functor(函子)遵守了一些特定规则的容器或者数据协议
    // 具有一个通用的map方法,返回新实例
    // 具有结合外部的运算能力 => 可在管道中处理不同层级又很纯净的单元操作

鄂ICP备2024055897号