函数缓存和函数柯里化

函数缓存

函数缓存是指的让一个函数的执行结果得到保存,在第二次执行时可以从缓存直接读取。例如有如下的问题场景:

1
2
3
var func = function (a, b, c) {}
func(1, 2, 3) // 执行花费10s钟
func(1, 2, 3) // 执行依然花费10s钟

如何让一个函数执行结果可以得到缓存,再次执行相同参数的函数,可以快速返回呢? 我们可以考虑对函数进行包装,利用闭包机制让同参数的执行结果被保存的一个哈希表里面。参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
var cache = function (fn) {
var cache = {}
// 返回一个闭包
return function () {
var k = Array.prototype.join.call(arguments, '-')
if (!cache[k]) {
cache[k] = fn.apply(this, arguments)
}
return cache[k]
}
}

函数柯里化

我认为的函数柯里化就是给函数绑定预置参数,从而让你调用函数的时候不再需要输入那么多参数。

然而细想一下,应用价值也不大啊。不就是我本来传3个参数的函数,你给我提前绑定好俩参数,我下次调用时传最后一个参数就行了,这有啥价值么,我完全可以不嫌麻烦自己传这3个参数哦。 然而,利用柯里化可以实现一种不限参数数量的无限调用手法。

最简单的一个curry函数(参考了百度efe的一个项目)

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @file 给函数绑定前置参数
* @author cuiyongjian(cuiyongjian@outlook.com)
*/

module.exports = function (fn, args) {
args = [].slice.call(arguments, 1);
return function () {
// 调用函数时,设置this为当调用环境的this
return fn.apply(this, args.concat([].slice.call(arguments)));
};
};

网上有些教程的curry实现中,并没有对fn.apply这个地方重新绑定this,这在一些场景下可能会导致this指向出问题。因为fn的调用已经放到了闭包函数内部单独调用,如果不绑定this,则fn函数中this永远是指向全局window的。

其实ES5当中bind就是一种函数柯里化。而且bind的polyfill实现也是基于函数柯里化

1
2
3
4
5
6
7
var bind = function (fn, thisArg, others) {
var args = [].slice.call(arguments, 2)
return function () {
return fn.apply(thisArg, args)
}
}

要polyfill的话,就这么检测玩

1
2
3
4
5
6
7
8
9
10
if (!function() {}.bind) {
Function.prototype.bind = function(context) {
var self = this
, args = Array.prototype.slice.call(arguments);

return function() {
return self.apply(context, args.slice(1));
}
};
}

至于bind和柯里化的区别

我感觉bind主要用来解决函数this指向问题,柯里化主要解决函数预置参数的问题。

Refer

仿lodash的curry
张鑫旭curry
js bind方法与函数柯里化
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
破狼-JavaScript函数柯里化