使用记忆器进行繁重的计算功能
如果要在处理器(客户端或服务器端)上构建可能很重的函数,则可能需要考虑一个 memoizer ,它是先前函数执行的缓存及其返回值。这允许你检查之前是否传递了函数的参数。请记住,纯函数是那些给出输入,返回相应的唯一输出并且不会在其范围之外引起副作用的函数,所以,不应该将 memoizers 添加到不可预测的函数或依赖于外部资源的函数(如 AJAX 调用或随机返回值)。
假设我有一个递归因子函数:
function fact(num) {
return (num === 0)? 1 : num * fact(num - 1);
}
例如,如果我将小值从 1 传递到 100,那么就没有问题,但是一旦我们开始深入,我们可能会炸毁调用堆栈或者让我们正在执行此操作的 Javascript 引擎有点痛苦,特别是如果引擎不计算尾部调用优化(尽管 Douglas Crockford 说本机 ES6 包含尾部调用优化)。
我们可以硬编码我们自己的字典从 1 到上帝知道什么数字与他们相应的阶乘,但是,我不确定我是否建议! 让我们创建一个 memoizer,好吗?
var fact = (function() {
var cache = {}; // Initialise a memory cache object
// Use and return this function to check if val is cached
function checkCache(val) {
if (val in cache) {
console.log('It was in the cache :D');
return cache[val]; // return cached
} else {
cache[val] = factorial(val); // we cache it
return cache[val]; // and then return it
}
/* Other alternatives for checking are:
|| cache.hasOwnProperty(val) or !!cache[val]
|| but wouldn't work if the results of those
|| executions were falsy values.
*/
}
// We create and name the actual function to be used
function factorial(num) {
return (num === 0)? 1 : num * factorial(num - 1);
} // End of factorial function
/* We return the function that checks, not the one
|| that computes because it happens to be recursive,
|| if it weren't you could avoid creating an extra
|| function in this self-invoking closure function.
*/
return checkCache;
}());
现在我们可以开始使用它了:
现在我开始思考我做了什么,如果我从 1 递增而不是从 num 递减,我可以递归地缓存缓存中从 1 到 num 的所有因子,但我会留给你。
这很好但是如果我们有多个参数怎么办?这是个问题?不完全是,我们可以做一些很好的技巧,比如在 arguments 数组上使用 JSON.stringify()
,或者甚至是函数依赖的值列表(对于面向对象的方法)。这样做是为了生成一个包含所有参数和依赖项的唯一键。
我们还可以创建一个 memoizes
其他函数的函数,使用与之前相同的作用域概念(返回使用原始函数且可以访问缓存对象的新函数):
警告:ES6 语法,如果你不喜欢它,替换…什么都没有,并使用 var args = Array.prototype.slice.call(null, arguments);
技巧; 用 var 替换 const 和 let,以及你已经知道的其他东西。
function memoize(func) {
let cache = {};
// You can opt for not naming the function
function memoized(...args) {
const argsKey = JSON.stringify(args);
// The same alternatives apply for this example
if (argsKey in cache) {
console.log(argsKey + ' was/were in cache :D');
return cache[argsKey];
} else {
cache[argsKey] = func.apply(null, args); // Cache it
return cache[argsKey]; // And then return it
}
}
return memoized; // Return the memoized function
}
现在请注意,这将适用于多个参数但在面向对象的方法中没有多大用处,我认为,你可能需要一个额外的依赖对象。此外,func.apply(null, args)
可以替换为 func(...args)
,因为数组解构将分别发送它们而不是作为数组形式。另外,仅供参考,将数组作为参数传递给 func 将不起作用,除非你像我一样使用 Function.prototype.apply
。
要使用上述方法,你只需:
const newFunction = memoize(oldFunction);
// Assuming new oldFunction just sums/concatenates:
newFunction('meaning of life', 42);
// -> "meaning of life42"
newFunction('meaning of life', 42); // again
// => ["meaning of life",42] was/were in cache :D
// -> "meaning of life42"