memoization 来源于拉丁语 memorandum ("to be remembered"),不要与 memorization 混淆了。

首先来看一下维基百科的描述:

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.

简单来说,memoization 是一种优化技术,主要用于通过存储昂贵的函数调用的结果来加速计算机程序,并在再次发生相同的输入时返回缓存的结果。

本文首先介绍一个简单的使用 memoization 优化技术的例子,然后解读 underscore 和 reselect 库中使用 memoization 的源码,加深理解。

阶乘

不使用 memoization

不假思索,我们会立即写下如下的代码:

const factorial = n => {if (n === 1) {return 1} else {return factorial(n - 1) * n}
};
复制代码

使用 memoization

const cache = []
const factorial = n => {if (n === 1) {return 1} else if (cache[n - 1]) {return cache[n - 1]} else {let result = factorial(n - 1) * ncache[n - 1] = resultreturn result}
};
复制代码

使用 闭包 和 memoization

常见的方式是 闭包 和 memoization 一起搭配使用:

const factorialMemo = () => {const cache = []const factorial = n => {if (n === 1) {return 1} else if (cache[n - 1]) {console.log(`get factorial(${n}) from cache...`)return cache[n - 1]} else {let result = factorial(n - 1) * ncache[n - 1] = resultreturn result}}return factorial
};
const factorial = factorialMemo();
复制代码

继续变形,下面这种编写方式是最常见的形式。

const factorialMemo = func => {const cache = []return function(n) {if (cache[n - 1]) {console.log(`get factorial(${n}) from cache...`)return cache[n - 1]} else {const result = func.apply(null, arguments)cache[n - 1] = resultreturn result}}
}const factorial = factorialMemo(function(n) {return n === 1 ? 1 : factorial(n - 1) * n
});
复制代码

从阶乘的这个例子可以知道 memoization 是一个空间换时间的方式,存储执行结果,下次再次发生相同的输入会直接输出结果,提高了执行的速度。

underscore 源码中的 memoization

// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {var memoize = function(key) {var cache = memoize.cache;var address = '' + (hasher ? hasher.apply(this, arguments) : key);if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);return cache[address];};memoize.cache = {};return memoize;
};
复制代码

代码一目了然,使用 _.memoize 来实现阶乘如下:

const factorial = _.memoize(function(n) {return n === 1 ? 1 : factorial(n - 1) * n
});
复制代码

参照这个源码,上面的阶乘继续可以变形如下:

const factorialMemo = func => {const memoize = function(n) {const cache = memoize.cacheif (cache[n - 1]) {console.log(`get factorial(${n}) from cache...`)return cache[n - 1]} else {const result = func.apply(null, arguments)cache[n - 1] = resultreturn result}}memoize.cache = []return memoize
}const factorial = factorialMemo(function(n) {return n === 1 ? 1 : factorial(n - 1) * n
});
复制代码

reselect 源码中的 memoization

export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {let lastArgs = nulllet lastResult = null// we reference arguments instead of spreading them for performance reasonsreturn function () {if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {// apply arguments instead of spreading for performance.lastResult = func.apply(null, arguments)}lastArgs = argumentsreturn lastResult}
};
复制代码

从源码可以知道当 lastArgs 与 arguments 相同的时候,就不会再执行 func。

总结

memoization 是一种优化技术,避免一些不必要的重复计算,可以提高计算速度。

参考

  1. Memoization wiki
  2. Understanding JavaScript Memoization In 3 Minutes
  3. Underscore
  4. reselect
  5. Implementing Memoization in JavaScript

JavaScript 高级技巧 Memoization相关推荐

  1. JavaScript 高级技巧

    1 安全类型检测 javascript内置类型检测并不可靠 safari某些版本(<4)typeof正则表达式返回为function 建议使用Object.prototype.toString. ...

  2. 每个 JavaScript 工程师都应懂的33个概念

    简介 这个项目是为了帮助开发者掌握 JavaScript 概念而创立的.它不是必备,但在未来学习(JavaScript)中,可以作为一篇指南. 本篇文章是参照 @leonardomso 创立,英文版项 ...

  3. 每个JavaScript工程师都应懂的33个概念

    摘要: 基础很重要啊! 原文:33 concepts every JavaScript developer should know 译文:每个 JavaScript 工程师都应懂的33个概念 作者:s ...

  4. JavaScript 开发者应懂的 33 个概念

    JavaScript开发者应懂的33个概念 Fundebug 一行代码搞定BUG监控:www.fundebug.com ​关注她 356 人赞同了该文章 摘要: 基础很重要啊! 原文:33 conce ...

  5. 每个 JavaScript 工程师都应懂的33个概念 1

    简介 这个项目是为了帮助开发者掌握 JavaScript 概念而创立的.它不是必备,但在未来学习(JavaScript)中,可以作为一篇指南. 本篇文章是参照 @leonardomso 创立,英文版项 ...

  6. 理解与使用Javascript中的回调函数

    在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被"存储"在变量中,能作为函数参数被传递,能在函数中被创建, ...

  7. 【JavaScript】理解与使用Javascript中的回调函数

    在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被"存储"在变量中,能作为函数参数被传递,能在函数中被创建, ...

  8. JavaScript面向对象精要(二)

    四.构造函数和原型对象 1. 构造函数 构造函数就是用new创建对象时调用的函数.使用构造函数的好处在于所有用同一个构造函数创建的对象都具有同样的属性和方法. function Person(){} ...

  9. js 正则是否包含某些字符串_我从Vue源码中学到的一些JS编程技巧

    在我们面试的过程中,经常会遇到问源码的环节,因为优秀的框架通常都会包含很多设计理念跟编程实践.这段时间我一直在看Vue2的源码,发现了很多有意思的实现.虽然现在Vue3都已经发布了,也无法否认Vue2 ...

最新文章

  1. CVPR 2022 | 室外多模态3D目标检测(DeepFusion)
  2. 21天学通python第二版-电子工业出版社-网上书店
  3. Winform中实现ZedGraph曲线图的图像复制到剪切板、打印预览、获取图片并保存、另存为的功能
  4. superset各种数据库连接地址(持续更新中)
  5. 如何将nodejs项目程序部署到阿里云服务器上
  6. P5020-货币系统【背包】
  7. H5的Websocket基本使用
  8. 终端模拟器编译c语言,编写你自己的Terminal emulator
  9. svn复制出来的java_从svn下载的项目(或从别处拷贝来的)报错的可能情况以及解决经验...
  10. FreeBSD的起源和发展
  11. 【转】用Qt生成dll类库及调用方法
  12. RunTime之类与对象
  13. OSChina 周一乱弹 ——斯文眼镜男竟然对一只母喵做了这样的事情!
  14. Three.js - 渲染器(WebGLRenderer)(二)
  15. 一文看懂外汇风险准备金率调整为 20%的含义
  16. 那一年岳云鹏14岁,郭德纲26岁
  17. python 战舰_战舰python代码学院
  18. 《我的极品媳妇》方志强 王亚欣 小说读后感
  19. 分形之城--没过,吃宵夜去了,生蚝真好吃
  20. 金融与python-基于Python的金融分析与风险管理

热门文章

  1. 在2020年到来之前,你应该知道的十大科技趋势预测
  2. android的数据存储方式有哪几种方式,android存储数据的几种方式比较
  3. SAP RETAIL Site BP Customer 相同的code ?
  4. 人脸识别应用场景不断拓展 刷脸要方便更要安全
  5. 物联网生物识别技术在工作场所中发挥更大作用
  6. 为了读懂你,AI究竟有多努力?
  7. 复盘AI领域收购十年:635起收购案,苹果位居榜首
  8. 【谷歌推网页爬虫新标准,开源robots.txt解析器】
  9. AI神经网络如何辨别事物
  10. 电视剧《都挺好》给我的启示