前端基础打卡4-函数式编程

67 阅读2分钟

记录一下前端学习笔记,坚持打卡记录!!

开始第四篇

函数式编程的出现

首先看一个题目:需要将数组 ['progressive%coding', 'objective%coding','functional$%coding']转化为数组[{name: 'Progressive Coding'}, {name: 'Objective Coding'}, {name: 'Functional Coding'}],通常我们会按照以下的方式处理:

    const arr: string[] = ['progressive$%coding', 'objective$%coding', 'functional$%coding']

    const changeArr = (arr) => {
      let res: object[] = []
      arr.forEach(element => {
        const eleArr = element.split('$%')

        let str: string[] = []

        eleArr.forEach(name => {
          let nameItem = name[0].toUpperCase() + name.slice(1);

          str.push(nameItem);
        })

        res.push({
          name: str.join(' ')
        })
      });

      return res
    }

    console.log(changeArr(arr))

正常这样处理是没有太多问题的,也是我们写业务时常用的方式,但是也存在一些问题

  1. 过程逻辑复杂,需要看完整段才知道内容,不好维护,可读性较差
  2. 存在临时变量,并且整体段落首尾封闭,迭代扩展难度高

如果用下面的方法,则会解决上述问题

const arr2: string[] = ['progressive$%coding', 'objective$%coding', 'functional$%coding'];

// 1.将字符串的首字母转化为大写
const capitalizeFirstLetter = (str: string): string => {
  return str[0].toUpperCase() + str.slice(1)
}

// 2.字符串按照分割符分割并且组长
const splitAndCapitalizeAndJoin = (str: string): string => {
  const parts = str.split('$%')
  return parts.map(capitalizeFirstLetter).join(' ')
}

// 3.组装一个对象,其中包含一个指定的键和值  
const assembleObj = (key: string, value: any): { [key: string]: any } => {
  return { [key]: value };
};

// 转换整个数组  
const changeArr = (arr: string[]): object[] => {
  return arr.map(element => {
    const capitalizedString = splitAndCapitalizeAndJoin(element);
    return assembleObj('name', capitalizedString);
  });
};

console.log(changeArr(arr2));

现在,每个函数都专注于执行一个具体的任务,这有助于提高代码的可读性和可维护性。同时,这也使得每个函数都更容易进行单元测试,因为它们的功能更加明确。

函数式编程原理特点

理论思想

  1. 函数作为一等公民,逻辑功能实现最终落脚点都是函数,函数实现+拼接流程
  2. 惰性执行 - 衔接型,性能节约
    // 惰性函数
    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 functional');
            }
        }
    }
    program('progressive')();
  1. 无状态 - 幂等;输入相同,结果相同;不会因为外界的调用而改变能力功能
  2. 无副作用;函数的内部不应该直接对整个系统中任何参数变量进行改动

实际开发

一些纯函数以及不满足纯函数特性例子

    const _class = {
        name: 'objective'
    }
    // 函数内部引入了外部变量 —— 不符合无状态
    const score = str => _class.name + ':' + str;

    // 直接修改了输入参数 —— 有副作用
    const changeClass = (obj, name) => obj.name = name;

    // #####################
    const _class = {
        name: 'objective'
    }

    const score = (obj, str) => obj.name + ':' + str; // 不依赖外部变量
    const changeClass = (obj, name) => ({...obj, name}); // 未修改外部变量

    changeClass(_class, 'functional');
    score(_class, 'good');

手写构造可拆分传参的累加函数

实现add(1)(2)(3)(4)

// 定义一个柯里化累加函数 add 
const add = function () {
  // 将函数的参数转换为数组并存储在 args 中  
  let args = Array.from(arguments);

  // 定义一个内部函数 inner,用于累加参数  
  let inner = function () {
    args.push(...arguments); // arguments默认为函数的传入参数
    return inner;
  }

  // 给 inner 函数添加一个 toString 方法,用于计算和返回累加结果  
  inner.toString = function () {
    return args.reduce((prev, cur) => (prev + cur))
  }

  return inner;
}

console.log(add(1)(2)(3)(4).toString())

组装函数思想

const compose = (f, g) => x => f(g(x))
const sum1 = x => x + 1;
const sum2 = x => x + 2;
const sum12 = compose(sum1, sum2);
console.log(sum12(1));