函数修饰器(decorator)

js中可以使用高阶函数实现对某个已有函数的功能修饰。比如,有一些计算时间较长的函数,为了优化运行,需要对这些函数添加cache功能,使用函数修饰器模式可以实现有效代码复用。

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

let worker = {
slow(min,max) {
return min+max
}
}

function cachingDecorator(func, hash) {
let cache = new Map()
return function() {
let key = hash(arguments)
if(cache.has(key)) {
return cache.get(key)
}
let result = func.apply(this,arguments)
cache.set(key,result)
return result
}
}

function hash(args){
return args[0]+','+args[1]
}

worker.slow = cachingDecorator(worker.slow,hash)

console.log(slow(3,5))
console.log(slow(3,5))

在上面的例子中,使用hash函数对函数参数列表arguments进行哈希,得到key,然后再用Map进行缓存。使用func.apply来让修饰后的函数不丢失this信息。

偏函数(partials)

使用bind可以实现偏函数功能,可以按照顺序从左往右绑定this以及任意个数的参数,bind的语法是这样的:

1
let bound = func.bind(context,arg1,arg2,...)

如果想不按照参数顺序,实现任意函数参数的绑定,可以使用js中的_.partial,下面是例子:

1
2
3
4
5
6
7
8
9
10
11
12
function greet(greeting, name) {
return greeting + ' ' + name
}

let sayHelloTo = _.partial(greet, 'hello')
sayHelloTo('Tom')
// 'hello Tom'

// 使用下划线占位符
let greetTom = _.partial(greet,_,'Tom')
greetTom('Tom')
// hi Tom

柯里化(currying)

柯里化的定义是,把函数f(a,b,c,d)转化成f(a)(b)(c)(d),以此实现对部分函数参数的预加载。

js中的_.curry实现的功能更加方便,下面是其实现:

1
2
3
4
5
6
7
8
9
10
11
function curry(func){
return function curried(...args) {
if(args.length >= func.length){
return func.apply(this,args)
}else{
return function(...args2) {
return curried.apply(this,args.concat(args2))
}
}
}
}

调用这样的柯里化后,可以实现的效果是:

1
2
3
4
5
6
7
8
9
10
function sum(a,b,c){
return a+b+c
}

let curriedSum = _.curry(sum)

curriedSum(1,2,3) // 6
curriedSum(1,2)(3) // 6
curriedSum(1)(2,3) // 6
curriedSum(1)(2)(3) // 6