函数式
一、函数式编程的出现
- 发展历程:命令(脚本)式 (面向过程) => 面向对象式(提高了模块的概念) => 函数式编程
1. url参数parse
-
数组在url中的展示形式
location.search => '?name[]=progressive%coding&name[]=functional$%coding'
-
参数提取拼接成数组
['progressive%coding', 'objective%coding', 'functional$%coding' ]
- 手写方法,转换成数组对象
[{name:'Progressive Coding'},{name:'Objective Coding'},{name:'Functional Coding'}]
面向过程
const _array = ['progressive$%coding', 'objective$%coding','functional$%coding' ]
const _objArr=[];
const nameParser = () =>{
array.forEach(item=>{
let names = item.split('$%');
let newName = [];
names.forEach(name=>{
let nameItem =name[0].toUpperCase() + name.slice(1);
newName.push(nameItem);
})
objArr.push({
name:newName.join(' ');
});
})
return objArr
}
console.log(nameParser(_array,_objArr));
//存在问题:
//1. 过程中存在逻辑包裹 - 需要看完整段代码才知道方法要干啥
//2.存在临时变量,并且首位封闭迭代拓展难度高
2. 解决方案
- 需求分析 => 从数组到数组对象的转换器 => 字符串转对象 nameParser => [objHelper :: string > object]
- 明确模块功能 => objHelper = formatName + assembleObj
- 功能拆分 => objHelper = [(split +capitalize + join)] + assembleObj
函数式
const _array = ['progressive$%coding', 'objective$%coding','functional$%coding' ]
const assembleObj = (key,x)=>{
let obj = {};
obj[key] = x;
return obj;
}
const capitalize = name =>{ name[0].toUpperCase() + name.slice(1); }
//组装描述
const formatName =组装合并(join(''),map(capitalize),split('$%'));
const objHelper = 组装合并(assembleObj('name'),formatName);
const nameParser = map(objHelper)
//使用
nameParser(_array);
二、函数式编程的原理特征
1. 什么是函数式编程的原理?
- 加法结合率|因式分解 | 完全平方公式 => a + b + c = (a + b) + c 原子组合的变化
2. 理论思想
a. 函数是一等公民:
1. 函数是逻辑功能实现的落脚点
2. 实现函数 + 函数拼接
b. 声明式编程
声明需求 => 语义化
c. 惰性执行 -无缝连接,性能节约
let program = name =>{
if(name === 'progressive'){
return program =() =>{
console.log('progressive')
}
}else if(name ==='objective'){
return program =() =>{
console.log('objective')
}
}else{
return program =() =>{
console.log('functional')
}
}
}
program('progressive')();//progressive
program();//progressive program第一次执行被提升,后续的操作都是第一次执行的结果
3. 无状态与副作用
- a. 无状态 - 幂等;数据不可变 - 不可操作改变源数据
- b. 无副作用 - 函数内部不应该直接对整个系统中任何参数变量做改动
三、实际开发
1. 纯函数改造
满足无状态和无副作用就是纯函数
const _class = {
name:'objective'
}
const score = str => _class.name + ':' + str;//函数内部引入外部变量后违反无状态的规则
const changeClass = (obj,name) => obj.name = name;//修改了传入参数,违反无副作用
changeClass(_class, 'functonal');
score('100');
//---------------------------------
//改造
const score = (obj, str) => obj.name + ':' +str;
const changeClass = (obj,name) => ({...obj, name})
2. 流水线组装 - 加工 + 组装
a. 加工 - 柯里化
f(x,y,z) => f(x)(y)(z)
const sum = (x,y)=>{
return x + y;
}
sum(1,2);
//柯里化
const add = x =>{
return y =>{
return x + y;
}
}
add(1)(2);
//实现体系 = 加工 + 组装,单个加工输入输出应当单值化
const fetch = ajax(method,url,params);
const request = ajax(method);
const fetch = request(url);
ajax(method)(url)(params)
可拆分传参的累加函数
- 构造柯里化结构
- 输入 处理外部的arguments => 类数组形态处理
- 传入参数无限拓展 => 递归 内层逻辑 => 返回函数
- 主功能实现 => 累加
- 输出
const add = function(){
//输入
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;
})
}
return inner;
}
''+add(1)(2)(3)(4) // '10'
parseInt((1)(2)(3)(4)) // 10
b. 流水线 - 组装函数
const compose = (f,g) => x => f(g(x))
const sum1 = x => x+1;
const sum2 = x => x+2;
const sum12 = compose(sum1,sum2);
sum12(1)
- 实际实现使用
//命令式
trim(reverse(toUpperCase(map(arr))))
//面向对象
arr.map().toUpperCase().reverse().trim();
//函数式
const result = compose(trim,reverse,toUpperCase,map);
pipe(map,toUpperCase,reverse,trim);