function.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // function utils
  2. import { lruQueue } from './lruQueue.js';
  3. /**
  4. * Memoize a given function by caching the computed result.
  5. * The cache of a memoized function can be cleared by deleting the `cache`
  6. * property of the function.
  7. *
  8. * @param {function} fn The function to be memoized.
  9. * Must be a pure function.
  10. * @param {Object} [options]
  11. * @param {function(args: Array): string} [options.hasher]
  12. * A custom hash builder. Is JSON.stringify by default.
  13. * @param {number | undefined} [options.limit]
  14. * Maximum number of values that may be cached. Undefined indicates
  15. * unlimited (default)
  16. * @return {function} Returns the memoized function
  17. */
  18. export function memoize(fn) {
  19. var {
  20. hasher,
  21. limit
  22. } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  23. limit = limit == null ? Number.POSITIVE_INFINITY : limit;
  24. hasher = hasher == null ? JSON.stringify : hasher;
  25. return function memoize() {
  26. if (typeof memoize.cache !== 'object') {
  27. memoize.cache = {
  28. values: new Map(),
  29. lru: lruQueue(limit || Number.POSITIVE_INFINITY)
  30. };
  31. }
  32. var args = [];
  33. for (var i = 0; i < arguments.length; i++) {
  34. args[i] = arguments[i];
  35. }
  36. var hash = hasher(args);
  37. if (memoize.cache.values.has(hash)) {
  38. memoize.cache.lru.hit(hash);
  39. return memoize.cache.values.get(hash);
  40. }
  41. var newVal = fn.apply(fn, args);
  42. memoize.cache.values.set(hash, newVal);
  43. memoize.cache.values.delete(memoize.cache.lru.hit(hash));
  44. return newVal;
  45. };
  46. }
  47. /**
  48. * Memoize a given function by caching all results and the arguments,
  49. * and comparing against the arguments of previous results before
  50. * executing again.
  51. * This is less performant than `memoize` which calculates a hash,
  52. * which is very fast to compare. Use `memoizeCompare` only when it is
  53. * not possible to create a unique serializable hash from the function
  54. * arguments.
  55. * The isEqual function must compare two sets of arguments
  56. * and return true when equal (can be a deep equality check for example).
  57. * @param {function} fn
  58. * @param {function(a: *, b: *) : boolean} isEqual
  59. * @returns {function}
  60. */
  61. export function memoizeCompare(fn, isEqual) {
  62. var memoize = function memoize() {
  63. var args = [];
  64. for (var i = 0; i < arguments.length; i++) {
  65. args[i] = arguments[i];
  66. }
  67. for (var c = 0; c < memoize.cache.length; c++) {
  68. var cached = memoize.cache[c];
  69. if (isEqual(args, cached.args)) {
  70. // TODO: move this cache entry to the top so recently used entries move up?
  71. return cached.res;
  72. }
  73. }
  74. var res = fn.apply(fn, args);
  75. memoize.cache.unshift({
  76. args,
  77. res
  78. });
  79. return res;
  80. };
  81. memoize.cache = [];
  82. return memoize;
  83. }
  84. /**
  85. * Find the maximum number of arguments expected by a typed function.
  86. * @param {function} fn A typed function
  87. * @return {number} Returns the maximum number of expected arguments.
  88. * Returns -1 when no signatures where found on the function.
  89. */
  90. export function maxArgumentCount(fn) {
  91. return Object.keys(fn.signatures || {}).reduce(function (args, signature) {
  92. var count = (signature.match(/,/g) || []).length + 1;
  93. return Math.max(args, count);
  94. }, -1);
  95. }