目录
柯里化的定义
柯里化的结构
函数柯里化的作用
参数复用
提前返回
延迟执行
手写通用柯里化函数
柯里化的定义
在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术,每个函数都返回一个新的函数,直到所有参数都被传递完毕。
而对于 Javascript 语言来说,实际应用中的柯里化函数,可以传递一个或多个参数。
简单来说就是简化函数的参数接收,将多个参数尽可能的变少
柯里化的结构
举个栗子
function add(x, y, z) {
return x + y + z
}
var result = add(10, 20, 30)
console.log(result) // 60
// 柯里化
function sum1(x) {
return function(y) {
return function(z) {
return x + y + z
}
}
}
var result1 = sum1(10)(20)(30)
console.log(result1) // 60
简化柯里化代码
// 简化柯里化的代码
var sum2 = x => y => z => {
return x + y + z
}
console.log(sum2(10)(20)(30)) // 60
var sum3 = x => y => z => x + y + z
console.log(sum3(10)(20)(30)) // 60
大家可能会觉得这样做多此一举,直接传3个参数不更好吗,根据这个代码来说确实如此。
但有些场景使用函数柯里化就显得非常 Nice
函数柯里化的作用
参数复用
工作中会遇到的需求:
- 需要打印一些日志,日志包括
时间
、类型
、信息
- 通过正则校验电话号、邮箱、身份证是否合法等等
于是我们会封装一个校验函数如下
function checkByRegExp(regExp, str) {
return regExp.test(str)
}
但是我们要检验很多手机号、邮箱,所以我们要调用很多次
// 校验手机号
checkByRegExp(/^1\d{10}$/, '15152525634');
checkByRegExp(/^1\d{10}$/, '13456574566');
checkByRegExp(/^1\d{10}$/, '18123787385');
// 校验邮箱
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'fsds@163.com');
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'fdsf@qq.com');
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'fjks@qq.com');
尝试用柯里化来改进
function checkByRegExp(regExp) {
return function(str) {
return regExp.test(str)
}
}
// 校验手机
const checkPhone = curryingCheckByRegExp(/^1\d{10}$/)
// 校验邮箱
const checkEmail = curryingCheckByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/)
// 校验手机号
checkPhone('15152525634');
checkPhone('13456574566');
checkPhone('18123787385');
// 校验邮箱
checkEmail('fsds@163.com');
checkEmail('fdsf@qq.com');
checkEmail('fjks@qq.com');
提前返回
有的浏览器不支持 addeventlistener 需要判断一下是否使用 attachEventListener,但是有时候浏览器一旦确立下来,就不需要这样判断了,每次都判断效率很低。
柯里化处理
function curryingAddEvent() {
if (window.addEventListener) {
return function(element, type, fn, isCapture) {
element.addEventListener(type, fn, isCapture)
}
} else if (window.attachEvent) {
return function(element, type, fn) {
element.attachEvent("on" + type, fn)
}
}
}
const addEvent = curryingAddEvent()
// 也可以用立即执行函数将上述代码合并
const addEvent = (function curryingAddEvent() {
...
})()
延迟执行
事实上,上述正则校验和事件监听的例子中已经体现了延迟执行。
curryingCheckByRegExp 函数调用后返回了 checkPhone 和 checkEmail 函数
curryingAddEvent 函数调用后返回了 addEvent 函数
返回的函数都不会立即执行,而是等待调用
function fn() {
console.log("在执行内部函数之前做一些事情");
return function () {
// 代码逻辑
}
}
const newFn = fn()
newFn()
手写通用柯里化函数
const currying = function(fn, ...args) {
// fn需要的参数个数, 函数的length就是函数的形参个数
const len = fn.length
// 返回一个函数接收剩余参数
return function (...params) {
// 拼接已经接收和新接收的参数列表
let _args = [...args, ...params]
// 如果已经接收的参数个数还不够,继续返回一个新函数接收剩余参数
if (_args.length < len) {
return currying.call(this, fn, ..._args)
}
// 参数全部接收完调用原函数
return fn.apply(this, _args)
}
}
优势
- 柯里化突出一种重要思想:降低适用范围,提高适用性
- 柯里化的三个作用和特点:参数复用、提前返回、延迟执行
- 柯里化是闭包的一个典型应用,利用闭包形成了一个保存在内存中的作用域,把接收到的部分参数保存在这个作用域中,等待后续使用。并且返回一个新函数接收剩余参数