函数式
一、函数式编程的出现
- 发展历程:
- 命令(脚本)式编程 => 一行代码为单元,逐步运行
- 面向对象编程 => 以对象为操作单位
- 函数式编程 => 以函数为单位
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方法,返回新实例
// 具有结合外部的运算能力 => 可在管道中处理不同层级又很纯净的单元操作