0%

函数柯里化

函数柯里化

把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回接受剩余的参数而且返回结果的新函数的技术。

简单的说,柯里化函数持续地返回一个新函数直到所有的参数用尽为止。这些参数全部保持“活着”的状态(通过闭包),然后当柯里化链中的最后一个函数被返回和执行时会全部被用来执行。

提高函数的适用性,同时降低函数的通用性;其实现方式就是固定一些可以预期的参数,然后返回一个特定的函数

简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 普通的add函数
function add(x, y) {
return x + y
}

// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}

add(1, 2) // 3
curryingAdd(1)(2) // 3

函数柯里化的好处

  • 参数复用:提前绑定好函数里面的某些参数,达到参数复用的效果,提高了适用性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 正常正则验证字符串 reg.test(txt)

// 函数封装后
function check(reg, txt) {
return reg.test(txt)
}

check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true

// Currying后
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}

var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
  • 提前返回
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //兼容现代浏览器以及IE浏览器的事件添加方法
    var addEvent = function(element, event, handler) {
    if (document.addEventListener) {
    if (element && event && handler) {
    element.addEventListener(event, handler, false);
    }
    } else {
    if (element && event && handler) {
    element.attachEvent('on' + event, handler);
    }
    }
    }

上面的方法有什么问题呢?很显然,我们每次使用addEvent为元素添加事件的时候,会走一遍if…else if …,其实只要一次判定就可以了,怎么做?–柯里化。它相对一第一种写法就是自执行然后返回一个新的函数,这样其实就是提前确定了浏览器支持的事件绑定,避免每次都进行判断。改为下面这样子的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var addEvent = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
} else {
return function(element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
};
}
})();
  • 延迟运行

call/apply 方法直接执行不同,bind 方法将第一个参数设置为函数执行的上下文,其他参数依次传递给调用方法(函数的主体本身不执行,可以看成是延迟执行),并动态创建返回一个新的函数, 这符合柯里化特点

1
2
3
4
5
6
Function.prototype.bind = function (target) {
const self = this;
return function () {
self.apply(target, arguments)
}
}

函数柯里化实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function currying(fn, args = []) {
return function (..._) {
args.push(..._); //使用args保存参数
if (args.length < fn.length) {
// 递归调用 返回一个新的函数 并保存传递的参数
return currying(fn, args)
} else {
// 执行被柯里化的函数 需要将参数进行结构
return fn(...args)
}
}
}

function add(a, b, c, d) {
return a + b + c + d;
}

const fn = currying(add)(1, 3, 4)
console.log(fn(2))

// 基于函数柯里化实现类型判断
const isType = (type, content)=>{
return Object.prototype.toString.call(content) === `[object ${type}]`;
}

const isString = currying(isType)('String');
console.log(isString('111'));


Function.prototype.uncurrying = function(){
return (...args)=>{
return this.call(...args)
}
}

const checkType = Object.prototype.toString.uncurrying();
console.log(checkType('111')) // '[object String]'

函数反柯里化实现

参考

https://segmentfault.com/a/1190000006096034

https://www.jianshu.com/p/2975c25e4d71

-------------本文结束感谢您的阅读-------------