一、纯函数
在程序设计中,若一个函数符合以下条件,则这个函数为纯函数:
1、确定的输入,一定会产出确定的输出
2、函数在执行过程中,不能产生副作用
副作用:执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响,比如修改了全局变量,修改参数或者改变外部的存储,副作用往往是产生bug的温床
let name = 'a';
function foo() {
console.log(name);
}
foo(); // a
name = 'b';
foo(); // b
// slice函数是纯函数
// splice是非纯函数:执行时有修改原来的数组本身
function baz(info) { // 非纯函数
info.age = 18;
}
const obj = {
name: '小红',
age: 10,
};
baz(obj);
console.log(obj); // {name: '小红', age: 18}
二、函数柯里化
只传递给函数一部分的参数来调用它,让它返回一个函数去处理剩余的参数,这个过程就称之为柯里化
// 原函数
function add(m, n, x, y) {
return m + n + x + y;
}
console.log(add(10, 20, 30, 40)); // 100
// 柯里化为
// 纯函数不存在闭包
function sum(m) {
return function (n) {
return function (x) {
return function (y) {
return m + n + x + y;
};
};
};
}
console.log(sum(10)(20)(30)(40)); // 100
// 简化柯里化的代码
const sum2 = m => n => x => y => m + n + x + y;
console.log(sum2(10)(20)(30)(40)); // 100
三、为什么有柯里化
1、单一职责原则:将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果;
// 柯里化单一职责原则
// 原函数
function add(x, y, z) {
x = x + 2;
y = y * 2;
z = z * z;
return x + y + z;
}
console.log(add(10, 20, 30)); // 952
// 柯里化为
function sum(x) {
x = x + 2;
return function (y) {
y = y * 2;
return function (z) {
z = z * z;
return function () {
return x + y + z;
};
};
};
}
console.log(sum(10)(20)(30)()); // 952
2、逻辑的复用
function makeAdder(count) {
return function (num) {
return count + num;
};
}
const result = makeAdder(5)(10); // 15
console.log(result);
const adder5 = makeAdder(5);
console.log(adder5(10)); // 15
console.log(adder5(20)); // 25
function log(date, type, message) {
console.log(`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]`);
}
log(new Date(), 'debug', '查找到bug'); // [8:48][debug]:[查找到bug]
// 柯里化的优化
const log = date => type => message => {
console.log(`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]`)
}
const nowLog = log(new Date())
nowLog('debug')('查找到bug'); // [8:52][debug]:[查找到bug]
四、柯里化函数的实现
// 普通函数 柯里化
function cutomCurrying(fn) {
function curried(...args) {
// 判断当前接收到的参数个数,是否与函数本身需要接收的参数一致
// 当已经传入的参数个数 >= 需要的参数个数时,直接执行函数
if (args.length >= fn.length) {
// curryAdd使用call时需要绑定作用域
return fn.apply(this, args);
}
// 当已经传入的参数个数 < 需要的参数个数时,返回新的函数继续接收参数
return function curried2(...args2) {
// 接收到参数后,需要递归调用curried来检查函数的个数
// curryAdd使用call时需要绑定作用域
return curried.apply(this, [...args, ...args2]);
};
};
return curried;
}
const curryAdd = cutomCurrying(add);
function add(x, y, z) {
return x + y + z;
}
console.log(curryAdd(10, 20, 30)); // 60
console.log(curryAdd(10)(20)(30)); // 60
console.log(curryAdd(10, 20)(30)); // 60
五、组合函数
function double(num) {
return num * 2;
}
function square(num) {
return num ** 2;
}
const count = 10;
console.log(square(double(count))); // 400
function composeFn(m, n) {
return function (count) {
return n(m(count));
};
}
const newFn = composeFn(double, square);
console.log(newFn(10)); // 400
function double(num) {
return num * 2;
}
function square(num) {
return num ** 2;
}
function cumstomCompose(...fns) {
const { length } = fns;
for (let i = 0; i < length; i++) {
if (typeof fns[i] !== 'function') {
throw new TypeError('要求都是函数类型');
}
}
return function (...args) {
let index = 0;
let result = length ? fns[index].apply(this, args) : args;
while (++index < length) {
result = fns[index].call(this, result);
}
return result;
};
}
const newFn = cumstomCompose(double, square);
console.log(newFn(10)); // 400
网友评论