小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
前言
一个简单的纯函数:
// 比较两个数大小的纯函数
function compareSize(a,b) {
return a > b
}
console.log(compareSize(1,2)); // false
console.log(compareSize(2,2)); // false
console.log(compareSize(3,2)); // true
在上面的纯函数中,我们对两个数值进行了比较,参数b在三次函数的调用中, 都是一样的数值2。
既然这样,那我们改写一下吧。
function compareSize(a) {
let b =2
return a > b
}
console.log(compareSize(1)); // false
console.log(compareSize(2)); // false
console.log(compareSize(3)); // true
改写后的代码,参数变成了一个a, 虽然结果和上次一样,函数compareSize也还是一个纯函数。
但是内部出现了一个固定的参数b, 这样其实变成了 hardcode(硬编码) 的写法🤯。
是一种固化的代码,代码在内部写死了,修改起来会很麻烦。
让我们再换一种方式试一下:
function compareSize(b) {
return function (a) {
return a > b
}
}
const hasThanTarget = compareSize(2)
console.log(hasThanTarget(1)); // false
console.log(hasThanTarget(2)); // false
console.log(hasThanTarget(3)); // true
最后这种方式其实就是一种 函数柯里化 的处理方式,所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。
函数柯里化的定义
在计算机科学中,柯里化(英语: Currying ),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
这个技术由克里斯托弗·斯特雷奇以逻辑学家哈斯凯尔·加里命名的,尽管它是Moses Schönfinkel和戈特洛布·弗雷格发明的😂。
函数柯里化的作用
参数复用
通过柯里化方法,缓存参数到闭包内部参数,然后在函数内部将缓存的参数与传入的参数组合后给函数执行,来实现参数的复用,降低通用性,提高适用性.
🌰例子: 对正则校验的方法, 进行 Currying.
function curryingCheck(reg) {
return function (txt) {
return reg.test(txt);
};
}
// ES6
let curryingCheck = reg => (txt => reg.test(txt))
使用:
// 正则: 只能输入数字
let hasNumber = curryingCheck(/^\d+$/);
console.log(hasNumber("test")); // false
console.log(hasNumber(1)); // true
// 正则: 手机号校验
let hasPhone = curryingCheck(/^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/);
console.log(hasPhone("test")); // false
console.log(hasPhone(18577331234)); // true
延迟运行
如果调用柯里化函数传入参数是不调用的,会将参数添加到数组中存储,等到没有参数传入的时候进行调用.
🌰 例子: 求和运算, 进行 Currying.
function add() {
var args = Array.prototype.slice.call(arguments);
var _that = this;
return function() {
var newArgs = Array.prototype.slice.call(arguments);
var total = args.concat(newArgs);
if(!arguments.length) {
var result = 1;
for(var i = 0; i < total.length; i++) {
result *= total[i];
}
return result;
}
else {
return add.apply(_that, total);
}
}
}
使用:
add(1)(2)(3)(); // 6
add(1, 2, 3)(); // 6
函数柯里化原理及模拟实现
原理
利用闭包原理在执行可以形成一个不销毁的作用域,然后把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个最少参数函数。
模拟实现
上面的例子中的柯里化函数不够通用, 下面来模拟实现一个(来源)
function curry(fn, args) {
var length = fn.length;
args = args || [];
return function () {
var _args = args.slice(0), arg, i;
for (i = 0; i < arguments.length; i++) {
arg = arguments[i];
_args.push(arg);
}
if (_args.length < length) {
return curry.call(this, fn, _args);
} else {
return fn.apply(this, _args);
}
};
}
使用:
var fn = curry(function (a, b, c) {
console.log([a, b, c]);
});
fn("a", "b", "c"); // ["a", "b", "c"]
fn("a", "b")("c"); // ["a", "b", "c"]
fn("a")("b")("c"); // ["a", "b", "c"]
fn("a")("b", "c"); // ["a", "b", "c"]
lodash 中使用柯里化函数
🎯推荐工作中使用 _.curry(func)
- 功能: 创建一个函数, 该函数接受一个或多个func的参数, 如果func所需要的参数都被提供,则执行func并返回执行的结果, 否则继续返回该函数并等待接受剩余的参数.
- 参数: 需要柯里化的函数
- 返回值: 柯里化后的函数
使用:
var abc = function(a, b, c) {
return [a, b, c];
};
var curried = _.curry(abc);
curried(1)(2)(3);
// => [1, 2, 3]
curried(1, 2)(3);
// => [1, 2, 3]
curried(1, 2, 3);
// => [1, 2, 3]
柯里化总结
柯里化对于函数式 JavaScript 是一种极其有用的技术。
它允许你生成一个简洁、易配置、表现统一的库,而且使用起来上手快、具有可读性。
在你的编码实践中加入柯里化会激励你在全部代码中的部分函数上应用它,这样避免了很多潜在的重复工作,并可以帮助你养成关于函数命名和处理函数参数的好习惯。