+81
| export = memize; | ||
| /** | ||
| * Memize options object. | ||
| * | ||
| * @typedef MemizeOptions | ||
| * | ||
| * @property {number} [maxSize] Maximum size of the cache. | ||
| */ | ||
| /** | ||
| * Internal cache entry. | ||
| * | ||
| * @typedef MemizeCacheNode | ||
| * | ||
| * @property {?MemizeCacheNode|undefined} [prev] Previous node. | ||
| * @property {?MemizeCacheNode|undefined} [next] Next node. | ||
| * @property {Array<*>} args Function arguments for cache | ||
| * entry. | ||
| * @property {*} val Function result. | ||
| */ | ||
| /** | ||
| * Properties of the enhanced function for controlling cache. | ||
| * | ||
| * @typedef MemizeMemoizedFunction | ||
| * | ||
| * @property {()=>void} clear Clear the cache. | ||
| */ | ||
| /** | ||
| * Accepts a function to be memoized, and returns a new memoized function, with | ||
| * optional options. | ||
| * | ||
| * @template {Function} F | ||
| * | ||
| * @param {F} fn Function to memoize. | ||
| * @param {MemizeOptions} [options] Options object. | ||
| * | ||
| * @return {F & MemizeMemoizedFunction} Memoized function. | ||
| */ | ||
| declare function memize<F extends Function>(fn: F, options?: MemizeOptions | undefined): F & MemizeMemoizedFunction; | ||
| declare namespace memize { | ||
| export { MemizeOptions, MemizeCacheNode, MemizeMemoizedFunction }; | ||
| } | ||
| /** | ||
| * Memize options object. | ||
| */ | ||
| type MemizeOptions = { | ||
| /** | ||
| * Maximum size of the cache. | ||
| */ | ||
| maxSize?: number; | ||
| }; | ||
| /** | ||
| * Properties of the enhanced function for controlling cache. | ||
| */ | ||
| type MemizeMemoizedFunction = { | ||
| /** | ||
| * Clear the cache. | ||
| */ | ||
| clear: () => void; | ||
| }; | ||
| /** | ||
| * Internal cache entry. | ||
| */ | ||
| type MemizeCacheNode = { | ||
| /** | ||
| * Previous node. | ||
| */ | ||
| prev?: MemizeCacheNode | null | undefined; | ||
| /** | ||
| * Next node. | ||
| */ | ||
| next?: MemizeCacheNode | null | undefined; | ||
| /** | ||
| * Function arguments for cache | ||
| * entry. | ||
| */ | ||
| args: any[]; | ||
| /** | ||
| * Function result. | ||
| */ | ||
| val: any; | ||
| }; |
+4
-0
@@ -0,1 +1,5 @@ | ||
| #### v1.1.0 (2020-03-07) | ||
| - New Feature: Add TypeScript type definition. | ||
| #### v1.0.5 (2018-01-25) | ||
@@ -2,0 +6,0 @@ |
+135
-85
| var memize = (function () { | ||
| 'use strict'; | ||
| 'use strict'; | ||
| var index = function memize( fn, options ) { | ||
| var size = 0, | ||
| maxSize, head, tail; | ||
| /** | ||
| * Memize options object. | ||
| * | ||
| * @typedef MemizeOptions | ||
| * | ||
| * @property {number} [maxSize] Maximum size of the cache. | ||
| */ | ||
| if ( options && options.maxSize ) { | ||
| maxSize = options.maxSize; | ||
| } | ||
| /** | ||
| * Internal cache entry. | ||
| * | ||
| * @typedef MemizeCacheNode | ||
| * | ||
| * @property {?MemizeCacheNode|undefined} [prev] Previous node. | ||
| * @property {?MemizeCacheNode|undefined} [next] Next node. | ||
| * @property {Array<*>} args Function arguments for cache | ||
| * entry. | ||
| * @property {*} val Function result. | ||
| */ | ||
| function memoized( /* ...args */ ) { | ||
| var node = head, | ||
| len = arguments.length, | ||
| args, i; | ||
| /** | ||
| * Properties of the enhanced function for controlling cache. | ||
| * | ||
| * @typedef MemizeMemoizedFunction | ||
| * | ||
| * @property {()=>void} clear Clear the cache. | ||
| */ | ||
| searchCache: while ( node ) { | ||
| // Perform a shallow equality test to confirm that whether the node | ||
| // under test is a candidate for the arguments passed. Two arrays | ||
| // are shallowly equal if their length matches and each entry is | ||
| // strictly equal between the two sets. Avoid abstracting to a | ||
| // function which could incur an arguments leaking deoptimization. | ||
| /** | ||
| * Accepts a function to be memoized, and returns a new memoized function, with | ||
| * optional options. | ||
| * | ||
| * @template {Function} F | ||
| * | ||
| * @param {F} fn Function to memoize. | ||
| * @param {MemizeOptions} [options] Options object. | ||
| * | ||
| * @return {F & MemizeMemoizedFunction} Memoized function. | ||
| */ | ||
| function memize( fn, options ) { | ||
| var size = 0; | ||
| // Check whether node arguments match arguments length | ||
| if ( node.args.length !== arguments.length ) { | ||
| node = node.next; | ||
| continue; | ||
| } | ||
| /** @type {?MemizeCacheNode|undefined} */ | ||
| var head; | ||
| // Check whether node arguments match arguments values | ||
| for ( i = 0; i < len; i++ ) { | ||
| if ( node.args[ i ] !== arguments[ i ] ) { | ||
| /** @type {?MemizeCacheNode|undefined} */ | ||
| var tail; | ||
| options = options || {}; | ||
| function memoized( /* ...args */ ) { | ||
| var node = head, | ||
| len = arguments.length, | ||
| args, i; | ||
| searchCache: while ( node ) { | ||
| // Perform a shallow equality test to confirm that whether the node | ||
| // under test is a candidate for the arguments passed. Two arrays | ||
| // are shallowly equal if their length matches and each entry is | ||
| // strictly equal between the two sets. Avoid abstracting to a | ||
| // function which could incur an arguments leaking deoptimization. | ||
| // Check whether node arguments match arguments length | ||
| if ( node.args.length !== arguments.length ) { | ||
| node = node.next; | ||
| continue searchCache; | ||
| continue; | ||
| } | ||
| } | ||
| // At this point we can assume we've found a match | ||
| // Surface matched node to head if not already | ||
| if ( node !== head ) { | ||
| // As tail, shift to previous. Must only shift if not also | ||
| // head, since if both head and tail, there is no previous. | ||
| if ( node === tail ) { | ||
| tail = node.prev; | ||
| // Check whether node arguments match arguments values | ||
| for ( i = 0; i < len; i++ ) { | ||
| if ( node.args[ i ] !== arguments[ i ] ) { | ||
| node = node.next; | ||
| continue searchCache; | ||
| } | ||
| } | ||
| // Adjust siblings to point to each other. If node was tail, | ||
| // this also handles new tail's empty `next` assignment. | ||
| node.prev.next = node.next; | ||
| if ( node.next ) { | ||
| node.next.prev = node.prev; | ||
| // At this point we can assume we've found a match | ||
| // Surface matched node to head if not already | ||
| if ( node !== head ) { | ||
| // As tail, shift to previous. Must only shift if not also | ||
| // head, since if both head and tail, there is no previous. | ||
| if ( node === tail ) { | ||
| tail = node.prev; | ||
| } | ||
| // Adjust siblings to point to each other. If node was tail, | ||
| // this also handles new tail's empty `next` assignment. | ||
| /** @type {MemizeCacheNode} */ ( node.prev ).next = node.next; | ||
| if ( node.next ) { | ||
| node.next.prev = node.prev; | ||
| } | ||
| node.next = head; | ||
| node.prev = null; | ||
| /** @type {MemizeCacheNode} */ ( head ).prev = node; | ||
| head = node; | ||
| } | ||
| node.next = head; | ||
| node.prev = null; | ||
| head.prev = node; | ||
| head = node; | ||
| // Return immediately | ||
| return node.val; | ||
| } | ||
| // Return immediately | ||
| return node.val; | ||
| } | ||
| // No cached value found. Continue to insertion phase: | ||
| // No cached value found. Continue to insertion phase: | ||
| // Create a copy of arguments (avoid leaking deoptimization) | ||
| args = new Array( len ); | ||
| for ( i = 0; i < len; i++ ) { | ||
| args[ i ] = arguments[ i ]; | ||
| } | ||
| // Create a copy of arguments (avoid leaking deoptimization) | ||
| args = new Array( len ); | ||
| for ( i = 0; i < len; i++ ) { | ||
| args[ i ] = arguments[ i ]; | ||
| } | ||
| node = { | ||
| args: args, | ||
| node = { | ||
| args: args, | ||
| // Generate the result from original function | ||
| val: fn.apply( null, args ), | ||
| }; | ||
| // Generate the result from original function | ||
| val: fn.apply( null, args ) | ||
| }; | ||
| // Don't need to check whether node is already head, since it would | ||
| // have been returned above already if it was | ||
| // Don't need to check whether node is already head, since it would | ||
| // have been returned above already if it was | ||
| // Shift existing head down list | ||
| if ( head ) { | ||
| head.prev = node; | ||
| node.next = head; | ||
| } else { | ||
| // If no head, follows that there's no tail (at initial or reset) | ||
| tail = node; | ||
| } | ||
| // Shift existing head down list | ||
| if ( head ) { | ||
| head.prev = node; | ||
| node.next = head; | ||
| } else { | ||
| // If no head, follows that there's no tail (at initial or reset) | ||
| tail = node; | ||
| } | ||
| // Trim tail if we're reached max size and are pending cache insertion | ||
| if ( size === /** @type {MemizeOptions} */ ( options ).maxSize ) { | ||
| tail = /** @type {MemizeCacheNode} */ ( tail ).prev; | ||
| /** @type {MemizeCacheNode} */ ( tail ).next = null; | ||
| } else { | ||
| size++; | ||
| } | ||
| // Trim tail if we're reached max size and are pending cache insertion | ||
| if ( size === maxSize ) { | ||
| tail = tail.prev; | ||
| tail.next = null; | ||
| } else { | ||
| size++; | ||
| head = node; | ||
| return node.val; | ||
| } | ||
| head = node; | ||
| memoized.clear = function() { | ||
| head = null; | ||
| tail = null; | ||
| size = 0; | ||
| }; | ||
| return node.val; | ||
| // Ignore reason: There's not a clear solution to create an intersection of | ||
| // the function with additional properties, where the goal is to retain the | ||
| // function signature of the incoming argument and add control properties | ||
| // on the return value. | ||
| // @ts-ignore | ||
| return memoized; | ||
| } | ||
| memoized.clear = function() { | ||
| head = null; | ||
| tail = null; | ||
| size = 0; | ||
| }; | ||
| var memize_1 = memize; | ||
| return memoized; | ||
| }; | ||
| return memize_1; | ||
| return index; | ||
| }()); |
@@ -1,1 +0,1 @@ | ||
| var memize=function(){"use strict";return function(n,r){function u(){var r,u,i=t,o=arguments.length;n:for(;i;){if(i.n.length===arguments.length){for(u=0;u<o;u++)if(i.n[u]!==arguments[u]){i=i.r;continue n}return i!==t&&(i===e&&(e=i.u),i.u.r=i.r,i.r&&(i.r.u=i.u),i.r=t,i.u=null,t.u=i,t=i),i.l}i=i.r}for(r=new Array(o),u=0;u<o;u++)r[u]=arguments[u];return i={n:r,l:n.apply(null,r)},t?(t.u=i,i.r=t):e=i,f===l?(e=e.u).r=null:f++,t=i,i.l}var l,t,e,f=0;return r&&r.t&&(l=r.t),u.clear=function(){t=null,e=null,f=0},u}}(); | ||
| var memize=function(){"use strict";return function(e,f){var i,l,o=0;function n(){var n,r,u=i,t=arguments.length;n:for(;u;){if(u.n.length===arguments.length){for(r=0;r<t;r++)if(u.n[r]!==arguments[r]){u=u.r;continue n}return u!==i&&(u===l&&(l=u.u),u.u.r=u.r,u.r&&(u.r.u=u.u),u.r=i,u.u=null,i.u=u,i=u),u.t}u=u.r}for(n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return u={n:n,t:e.apply(null,n)},i?(i.u=u).r=i:l=u,o===f.e?(l=l.u).r=null:o++,(i=u).t}return f=f||{},n.clear=function(){l=i=null,o=0},n}}(); |
+63
-13
@@ -1,9 +0,51 @@ | ||
| module.exports = function memize( fn, options ) { | ||
| var size = 0, | ||
| maxSize, head, tail; | ||
| /** | ||
| * Memize options object. | ||
| * | ||
| * @typedef MemizeOptions | ||
| * | ||
| * @property {number} [maxSize] Maximum size of the cache. | ||
| */ | ||
| if ( options && options.maxSize ) { | ||
| maxSize = options.maxSize; | ||
| } | ||
| /** | ||
| * Internal cache entry. | ||
| * | ||
| * @typedef MemizeCacheNode | ||
| * | ||
| * @property {?MemizeCacheNode|undefined} [prev] Previous node. | ||
| * @property {?MemizeCacheNode|undefined} [next] Next node. | ||
| * @property {Array<*>} args Function arguments for cache | ||
| * entry. | ||
| * @property {*} val Function result. | ||
| */ | ||
| /** | ||
| * Properties of the enhanced function for controlling cache. | ||
| * | ||
| * @typedef MemizeMemoizedFunction | ||
| * | ||
| * @property {()=>void} clear Clear the cache. | ||
| */ | ||
| /** | ||
| * Accepts a function to be memoized, and returns a new memoized function, with | ||
| * optional options. | ||
| * | ||
| * @template {Function} F | ||
| * | ||
| * @param {F} fn Function to memoize. | ||
| * @param {MemizeOptions} [options] Options object. | ||
| * | ||
| * @return {F & MemizeMemoizedFunction} Memoized function. | ||
| */ | ||
| function memize( fn, options ) { | ||
| var size = 0; | ||
| /** @type {?MemizeCacheNode|undefined} */ | ||
| var head; | ||
| /** @type {?MemizeCacheNode|undefined} */ | ||
| var tail; | ||
| options = options || {}; | ||
| function memoized( /* ...args */ ) { | ||
@@ -47,3 +89,3 @@ var node = head, | ||
| // this also handles new tail's empty `next` assignment. | ||
| node.prev.next = node.next; | ||
| /** @type {MemizeCacheNode} */ ( node.prev ).next = node.next; | ||
| if ( node.next ) { | ||
@@ -55,3 +97,3 @@ node.next.prev = node.prev; | ||
| node.prev = null; | ||
| head.prev = node; | ||
| /** @type {MemizeCacheNode} */ ( head ).prev = node; | ||
| head = node; | ||
@@ -76,3 +118,3 @@ } | ||
| // Generate the result from original function | ||
| val: fn.apply( null, args ) | ||
| val: fn.apply( null, args ), | ||
| }; | ||
@@ -93,5 +135,5 @@ | ||
| // Trim tail if we're reached max size and are pending cache insertion | ||
| if ( size === maxSize ) { | ||
| tail = tail.prev; | ||
| tail.next = null; | ||
| if ( size === /** @type {MemizeOptions} */ ( options ).maxSize ) { | ||
| tail = /** @type {MemizeCacheNode} */ ( tail ).prev; | ||
| /** @type {MemizeCacheNode} */ ( tail ).next = null; | ||
| } else { | ||
@@ -120,3 +162,11 @@ size++; | ||
| // Ignore reason: There's not a clear solution to create an intersection of | ||
| // the function with additional properties, where the goal is to retain the | ||
| // function signature of the incoming argument and add control properties | ||
| // on the return value. | ||
| // @ts-ignore | ||
| return memoized; | ||
| }; | ||
| } | ||
| module.exports = memize; |
+32
-24
| { | ||
| "name": "memize", | ||
| "version": "1.0.5", | ||
| "version": "1.1.0", | ||
| "description": "Unabashedly-barebones memoization library with an aim toward speed", | ||
| "main": "index.js", | ||
| "scripts": { | ||
| "build": "NODE_ENV=production rollup -c rollup.config.js", | ||
| "postbuild": "npm run minify", | ||
| "build": "npm run build:bundle && npm run build:types", | ||
| "build:bundle": "NODE_ENV=production rollup -c rollup.config.js", | ||
| "postbuild:bundle": "npm run minify", | ||
| "build:types": "tsc -p tsconfig.decl.json", | ||
| "minify": "uglifyjs dist/memize.js -c -m --mangle-props domprops --mangle-props regex=\"/^next|prev|val|args|maxSize$/\" > dist/memize.min.js", | ||
| "mocha": "NODE_ENV=test mocha", | ||
| "lint": "eslint .", | ||
| "test": "npm run mocha && npm run lint", | ||
| "test:unit": "NODE_ENV=test mocha", | ||
| "test:lint": "eslint .", | ||
| "test:types": "tsc", | ||
| "test": "npm run test:unit && npm run test:lint && npm run test:types", | ||
| "benchmark": "node benchmark", | ||
@@ -18,3 +21,4 @@ "prepublishOnly": "npm test && npm run build" | ||
| "dist", | ||
| "index.js" | ||
| "index.js", | ||
| "index.d.ts" | ||
| ], | ||
@@ -41,23 +45,27 @@ "keywords": [ | ||
| "devDependencies": { | ||
| "@aduth/eslint-config": "^3.0.0", | ||
| "@types/node": "^13.9.0", | ||
| "benchmark": "^2.1.4", | ||
| "chai": "^4.1.1", | ||
| "chai": "^4.2.0", | ||
| "cli-table2": "^0.2.0", | ||
| "eslint": "^4.4.1", | ||
| "fast-memoize": "^2.2.8", | ||
| "lodash": "^4.17.4", | ||
| "lru-memoize": "^1.0.2", | ||
| "memoizee": "^0.4.5", | ||
| "eslint": "^6.8.0", | ||
| "eslint-plugin-jsdoc": "^22.0.0", | ||
| "fast-memoize": "^2.5.2", | ||
| "lodash": "^4.17.15", | ||
| "lru-memoize": "^1.1.0", | ||
| "memoizee": "^0.4.14", | ||
| "memoizejs": "^0.1.1", | ||
| "memoizerific": "^1.11.2", | ||
| "mocha": "^3.5.0", | ||
| "moize": "^3.4.0", | ||
| "ora": "^1.3.0", | ||
| "ramda": "^0.24.1", | ||
| "rollup": "^0.45.2", | ||
| "rollup-plugin-commonjs": "^8.1.0", | ||
| "rollup-plugin-replace": "^2.0.0", | ||
| "sinon": "^3.1.0", | ||
| "uglify-js": "^3.0.27", | ||
| "underscore": "^1.8.3" | ||
| "memoizerific": "^1.11.3", | ||
| "mocha": "^7.1.0", | ||
| "moize": "^5.4.5", | ||
| "ora": "^4.0.3", | ||
| "ramda": "^0.27.0", | ||
| "rollup": "^2.0.2", | ||
| "rollup-plugin-commonjs": "^10.1.0", | ||
| "rollup-plugin-replace": "^2.2.0", | ||
| "sinon": "^9.0.0", | ||
| "typescript": "^3.8.3", | ||
| "uglify-js": "^3.8.0", | ||
| "underscore": "^1.9.2" | ||
| } | ||
| } |
+38
-42
@@ -59,55 +59,51 @@ Memize | ||
| The following benchmarks are performed in Node 8.2.1 on a MacBook Pro (Late 2016), 2.9 GHz Intel Core i7. Lodash, Underscore, and Ramda are only included in the first benchmark because they do not support multiple argument memoization. | ||
| The following benchmarks are performed in Node 10.16.0 on a MacBook Pro (2019), 2.4 GHz 8-Core Intel Core i9, 32 GB 2400 MHz DDR4 RAM. | ||
| __Single argument__ | ||
|  | ||
| Name | Ops / sec | Relative margin of error | Sample size | ||
| -------------------|-------------|--------------------------|------------ | ||
| fast-memoize | 360,812,575 | ± 0.55% | 87 | ||
| memize | 128,909,282 | ± 1.06% | 87 | ||
| moize | 102,858,648 | ± 0.66% | 88 | ||
| lru-memoize | 71,589,564 | ± 0.90% | 88 | ||
| lodash | 49,575,743 | ± 1.00% | 88 | ||
| underscore | 35,805,268 | ± 0.86% | 88 | ||
| memoizee | 35,357,004 | ± 0.55% | 87 | ||
| moize (serialized) | 27,246,184 | ± 0.88% | 87 | ||
| memoizerific | 8,647,735 | ± 0.91% | 91 | ||
| ramda | 8,011,334 | ± 0.74% | 90 | ||
| memoizejs | 2,111,745 | ± 0.52% | 88 | ||
| | Name | Ops / sec | Relative margin of error | | ||
| | -------------------|------------|------------------------- | | ||
| | memize | 46,802,274 | ± 0.95% | | ||
| | moize | 36,659,057 | ± 1.09% | | ||
| | fast-memoize | 28,318,096 | ± 2.31% | | ||
| | moize (serialized) | 14,363,716 | ± 0.82% | | ||
| | underscore | 12,934,260 | ± 0.75% | | ||
| | lru-memoize | 11,648,537 | ± 1.13% | | ||
| | memoizee | 11,120,460 | ± 1.02% | | ||
| | lodash | 9,896,950 | ± 0.51% | | ||
| | memoizerific | 2,252,795 | ± 1.26% | | ||
| | memoizejs | 1,357,025 | ± 0.76% | | ||
| | ramda | 1,109,387 | ± 0.85% | | ||
| _**\* Note**: `fast-memoize` uses [`Function.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length) to optimize for singular argument functions, which [can yield unexpected behavior](https://github.com/caiogondim/fast-memoize.js#rest--default-parameters) if not account for._ | ||
| __Multiple arguments (primitive)__ | ||
|  | ||
| Name | Ops / sec | Relative margin of error | Sample size | ||
| -------------------|------------|--------------------------|------------ | ||
| memize | 81,460,517 | ± 0.61% | 88 | ||
| moize | 66,896,395 | ± 0.90% | 83 | ||
| lru-memoize | 26,315,198 | ± 1.26% | 85 | ||
| memoizee | 18,237,056 | ± 0.60% | 90 | ||
| moize (serialized) | 15,207,105 | ± 0.78% | 84 | ||
| memoizerific | 6,363,555 | ± 0.63% | 88 | ||
| memoizejs | 1,764,673 | ± 0.57% | 90 | ||
| fast-memoize | 1,560,421 | ± 0.72% | 87 | ||
| | Name | Ops / sec | Relative margin of error | | ||
| | -------------------|------------|------------------------- | | ||
| | memize | 35,171,560 | ± 0.62% | | ||
| | moize | 22,314,974 | ± 1.01% | | ||
| | moize (serialized) | 11,188,031 | ± 0.84% | | ||
| | lru-memoize | 8,625,528 | ± 1.83% | | ||
| | memoizee | 8,435,400 | ± 0.77% | | ||
| | memoizerific | 1,438,243 | ± 1.04% | | ||
| | memoizejs | 1,130,111 | ± 0.61% | | ||
| | fast-memoize | 754,958 | ± 0.64% | | ||
| __Multiple arguments (non-primitive)__ | ||
|  | ||
| Name | Ops / sec | Relative margin of error | Sample size | ||
| -------------------|------------|--------------------------|------------ | ||
| memize | 79,105,918 | ± 0.81% | 86 | ||
| moize | 62,374,610 | ± 0.55% | 87 | ||
| lru-memoize | 24,814,747 | ± 0.54% | 89 | ||
| memoizee | 12,119,005 | ± 0.47% | 89 | ||
| memoizerific | 6,748,675 | ± 0.66% | 88 | ||
| moize (serialized) | 2,027,250 | ± 1.07% | 87 | ||
| fast-memoize | 1,263,457 | ± 1.00% | 89 | ||
| memoizejs | 1,075,690 | ± 0.61% | 87 | ||
| | Name | Ops / sec | Relative margin of error | | ||
| | -------------------|------------|------------------------- | | ||
| | memize | 35,439,005 | ± 0.58% | | ||
| | moize | 22,624,991 | ± 1.11% | | ||
| | lru-memoize | 8,562,363 | ± 1.76% | | ||
| | memoizee | 8,424,725 | ± 1.11% | | ||
| | moize (serialized) | 1,575,815 | ± 0.87% | | ||
| | memoizerific | 1,466,993 | ± 0.87% | | ||
| | memoizejs | 832,957 | ± 0.94% | | ||
| | fast-memoize | 649,054 | ± 0.53% | | ||
| ## How it works | ||
| If you haven't already, feel free to [glance over the source code](./index.js). It's approximately 100 lines of code of heavily commented code, and should help provide substance to the implementation concepts. | ||
| If you haven't already, feel free to [glance over the source code](./index.js). The code is heavily commented and should help provide substance to the implementation concepts. | ||
@@ -118,8 +114,8 @@ Memize creates a [last-in first-out stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) implemented as a [doubly linked list](https://en.wikipedia.org/wiki/Doubly_linked_list). It biases recent access favoring real-world scenarios where the function is subsequently invoked multiple times with the same arguments. The choice to implement as a linked list is due to dramatically better performance characteristics compared to `Array#unshift` for surfacing an entry to the head of the list ([jsperf](https://jsperf.com/array-unshift-linked-list)). A downside of linked lists is inability to efficiently access arbitrary indices, but iterating from the beginning of the cache list is optimized by guaranteeing the list is sorted by recent access / insertion. | ||
| Finally, special care is made toward treatment of `arguments` due to engine-specific deoptimizations which can occur in V8 via [arguments leaking](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments). Order is important here; we only create a shallow clone when necessary, after the cache has been checked, to avoid creating a clone unnecessarily if a cache entry exists. Looking at the code, you'd not be blamed for thinking that dropping the shallow clone would improve performance, but in fact it would _slow_ execution by approximately 60%. This is due to how the lingering `arguments` reference would carry over by reference ("leaks") in the node's `args` property. | ||
| Finally, special care is made toward treatment of `arguments` due to engine-specific deoptimizations which can occur in V8 via [arguments leaking](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments). Order is important here; we only create a shallow clone when necessary, after the cache has been checked, to avoid creating a clone unnecessarily if a cache entry exists. Looking at the code, you'd not be blamed for thinking that dropping the shallow clone would improve performance, but in fact it would _slow_ execution by approximately 60%. This is due to how the lingering `arguments` reference would carry over by reference ("leaks") in the node's `args` property. _**Update:** As of November 2019, engine improvements are such that `arguments` leaking does not have as dramatic an effect. However, my testing shows that the shallow clone still performs equal or better than referencing `arguments` directly, and as such the implementation has not been revised in order to achieve optimal performance in the most versions of V8._ | ||
| ## License | ||
| Copyright 2017 Andrew Duthie | ||
| Copyright 2018-2020 Andrew Duthie | ||
| Released under the [MIT License](./LICENSE.md). |
| var memize = (function () { | ||
| 'use strict'; | ||
| var fifo = function memize( fn, options ) { | ||
| var cache = [], | ||
| maxSize; | ||
| if ( options && options.maxSize > 0 ) { | ||
| maxSize = options.maxSize; | ||
| } | ||
| function memoized( /* ...args */ ) { | ||
| searchCache: for ( var i = 0; i < cache.length; i++ ) { | ||
| // Check whether cached arguments match current invocation | ||
| for ( var j = 0; j < arguments.length; j++ ) { | ||
| if ( cache[ i ][ 0 ][ j ] !== arguments[ j ] ) { | ||
| continue searchCache; | ||
| } | ||
| // At this point, a assume a match is found and return | ||
| return cache[ i ][ 1 ]; | ||
| } | ||
| } | ||
| // No cached value found. Continue to insertion phase: | ||
| // Create a copy of arguments (avoid leaking deoptimization) | ||
| var args = []; | ||
| for ( i = 0; i < arguments.length; i++ ) { | ||
| args.push( arguments[ i ] ); | ||
| } | ||
| // Generate the result from original function | ||
| var result = fn.apply( null, args ); | ||
| cache.push( [ args, result ] ); | ||
| // Trim if we're reached max size and are pending cache insertion | ||
| if ( cache.length > maxSize ) { | ||
| cache = cache.slice( 1 ); | ||
| } | ||
| return result; | ||
| } | ||
| memoized.clear = function() { | ||
| cache = []; | ||
| }; | ||
| return memoized; | ||
| }; | ||
| return fifo; | ||
| }()); |
| var memize=function(){"use strict";return function(n,r){function e(){n:for(var r=0;r<u.length;r++)for(var e=0;e<arguments.length;e++){if(u[r][0][e]!==arguments[e])continue n;return u[r][1]}var i=[];for(r=0;r<arguments.length;r++)i.push(arguments[r]);var a=n.apply(null,i);return u.push([i,a]),u.length>t&&(u=u.slice(1)),a}var t,u=[];return r&&r.maxSize>0&&(t=r.maxSize),e.clear=function(){u=[]},e}}(); |
| var memoir=function(){"use strict";return function(n,r){function u(){var r,u,f=l,i=arguments.length;n:for(;f;){for(u=0;u<i;u++)if(f.n[u]!==arguments[u]){f=f.r;continue n}return f!==l&&(f.u.r=f.r,f.r=l),f.t}for(r=new Array(i),u=0;u<i;u++)r[u]=arguments[u];return f={n:r,t:n.apply(null,r)},l?(l.u=f,f.r=l):o=f,e===t?(o=o.u).r=null:e++,l=f,f.t}var t,l,o,e=0;return r&&r.l&&(t=r.l),u.clear=function(){l=null,o=null,e=0},u}}(); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 2 instances in 1 package
21966
23.45%356
52.14%2
-50%24
20%8
-20%120
-3.23%1
Infinity%