函数式
一、函数式编程的原理
1. 什么是函数式编程的原理
加法结合律 | 因式分解 | 完全平方公式
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')();
console.log('lazy');
program();
// progressive lazy progressive
// 提示 函数变量提升 program被覆盖
// 相当于最外层的program只执行了一次。 一般用于性能优化 如权限判断
3. 无状态与无副作用
无状态 - 幂等; 数据不可变(内部不对外部数据做操作)- 不可操作改变源数据
无副作用 - 函数内部不应该直接对整个系统中的任何参数变量做改动
二、实际开发
1. 纯函数的改造
// 非纯函数写法
const _class = {
name: 'zhangsan'
}
// 函数内部引入外部变量后 -- 违反无状态
const age = str => _class.name + ':' + str;
// 修改了传入参数 -- 违反无副作用
const changeClass = (obj, name) => obj.name = name;
chhangeClass(_class, 'lisi');
age('12')
// ----------------------------
// 纯函数写法
const _class = {
name: 'zhangsan'
}
const age = (obj, str) => obj.name + ':' + str; // 不依赖外部变量
const changeClass = (obj, name) => = ({...obj, name}); // 不修改外部变量
chhangeClass(_class, 'lisi');
age(_class, '12')
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)
面试题:手写构造可拆分传参的累加函数 add(1)(2)(3)
// 1. 构造科里化结构
// 2. 输入 处理外部arguments => 类数组形态处理
// 3. 传入参数无限拓展 => 递归 内层逻辑 => 返回函数
// 4. 主功能实现 => 累加
// 5. 输出
const add = function () {
// 输入
let args = Array.prototype.slice.call(arguments);
// 内存处理。 外面执行一次函数 就返回一个函数以供下一次的执行调用
let inner = function () {
args.push(...arguments);// 内外层参数合并 比如第二层的时候 把第一个参数1 跟第二个参数2合并
return inner
}
// 返回的是一个函数 但是实际上要的是字符串
inner.toString = function(){
return args.reduce((prev, cur) => {
return prev + cur
})
}
return inner;
}
console.log('' +add(1)(2)(3)(4)); // 10
递归小技巧。 因为递归的函数返回值是一样的, 如果结果的数据类型跟递归函数返回值不一样 大概率是复写方法。 如这道题中 返回的是要字符串或者是数字, 但是递归函数返回的是函数,所以复写了toString方法
b. 流水线 - 组装函数
const compose = (f, g) => {
return x => {
return 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);//执行从左往右
三、 BOX 与 函子
class Mail{
constructor(content){
this.content = content;
}
map(fn){
return new Mail(fn(this.content));
}
}
//拆开信
let mail1 = new Mail('love');
//读了信
let mail2 = mail1.map(function(mail){
return read(mail);
})
//烧了信
let mail3 = mail1.map(function(mail){
return burn(mail);
})
//老师查寝时
mail3.map(function(mail){
return check(mail);
})
// 链式
new mail('love')
.map(read)
.map(burn)
.map(check)