diff --git a/.gitignore b/.gitignore index be1e0e9..0bec2f2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ npm-debug.log .DS_Store coverage/ +.vscode \ No newline at end of file diff --git a/benchmark/cache/index.js b/benchmark/cache/index.js index 62c1e27..b94f6c8 100644 --- a/benchmark/cache/index.js +++ b/benchmark/cache/index.js @@ -1,3 +1,4 @@ +'use stict' const ora = require('ora') const Table = require('cli-table2') const debug = require('logdown')() diff --git a/benchmark/index.js b/benchmark/index.js index 362f9be..dd50a86 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -1,3 +1,4 @@ +'use stict' const ora = require('ora') const Table = require('cli-table2') const debug = require('logdown')() diff --git a/benchmark/recursive-solo.js b/benchmark/recursive-solo.js index 70baf4e..4eb37b9 100644 --- a/benchmark/recursive-solo.js +++ b/benchmark/recursive-solo.js @@ -1,3 +1,4 @@ +'use stict' const ora = require('ora') const logger = require('logdown')() const Table = require('cli-table2') diff --git a/benchmark/strategy/index.js b/benchmark/strategy/index.js index d948b4f..125a562 100644 --- a/benchmark/strategy/index.js +++ b/benchmark/strategy/index.js @@ -1,3 +1,4 @@ +'use stict' const ora = require('ora') const Table = require('cli-table2') const debug = require('logdown')() diff --git a/benchmark/strategy/partial-application.js b/benchmark/strategy/partial-application.js index 15db328..62e7050 100644 --- a/benchmark/strategy/partial-application.js +++ b/benchmark/strategy/partial-application.js @@ -22,7 +22,10 @@ function strategy (fn, options) { return cache.get(cacheKey) } - function variadic (fn, cache, serializer, ...args) { + function variadic (fn, cache, serializer) { + for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { + args[_key - 3] = arguments[_key]; + } var cacheKey = serializer(args) if (!cache.has(cacheKey)) { @@ -34,9 +37,9 @@ function strategy (fn, options) { return cache.get(cacheKey) } - var memoized = fn.length === 1 - ? monadic - : variadic + var memoized = fn.length === 1 ? + monadic : + variadic memoized = memoized.bind(this, fn, options.cache.create(), options.serializer) memoized.label = 'strategy: Partial application, cache: ' + options.cache.label + ', serializer: ' + options.serializer.label diff --git a/src/delme.js b/src/delme.js new file mode 100644 index 0000000..a4f8a98 --- /dev/null +++ b/src/delme.js @@ -0,0 +1,16 @@ +var memoize = require('../src') + +var circular = { + a: 'foo' +} +circular.b = circular + +function circularFunction (a) { + return a.a +} + +var memoizedCircularFunction = memoize(circularFunction) + +// Assertions +var one = memoizedCircularFunction(circular) +var two = memoizedCircularFunction(circular) diff --git a/src/index.js b/src/index.js index b4c493c..f400bf4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,5 @@ +'use strict' +const util = require('util') // // Main // @@ -57,7 +59,11 @@ function strategyDefault (fn, options) { return cache.get(cacheKey) } - function variadic (fn, cache, serializer, ...args) { + function variadic (fn, cache, serializerrgs) { + for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { + args[_key - 3] = arguments[_key]; + } + var cacheKey = serializer(args) if (!cache.has(cacheKey)) { @@ -69,9 +75,9 @@ function strategyDefault (fn, options) { return cache.get(cacheKey) } - var memoized = fn.length === 1 - ? monadic - : variadic + var memoized = fn.length === 1 ? + monadic : + variadic memoized = memoized.bind( this, @@ -89,36 +95,91 @@ function strategyDefault (fn, options) { // Serializer // -function serializerDefault (...args) { - return JSON.stringify(args) +function customReplacer(args) { + var cache = []; + const replacer = function (key, value) { + if (typeof value === 'object' && value !== null) { + if (cache.indexOf(value) !== -1) { + // Circular reference found, discard key + return + } + // Store value in our collection + cache.push(value) + } + } + cache = null // Enable garbage collection + return replacer } // -// Cache +// Serializer // -class ObjectWithoutPrototypeCache { - constructor () { - this.cache = Object.create(null) +function serializerDefault () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key] } - has (key) { - return (key in this.cache) + try { + // try the fastest way first. + return JSON.stringify(args) + } catch (error) { + if (error instanceof TypeError && + error.message === 'Converting circular structure to JSON') { + // if node + if (util && util.inspect) { + return JSON.stringify(util.inspect(args)) + } else { + return JSON.stringify(args, customReplacer) + } + } else { + throw error // let others bubble up + } } +} - get (key) { - return this.cache[key] - } +// +// Cache +// - set (key, value) { - this.cache[key] = value - } +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var ObjectWithoutPrototypeCache = function () { + function ObjectWithoutPrototypeCache() { + _classCallCheck(this, ObjectWithoutPrototypeCache); - delete (key) { - delete this.cache[key] + this.cache = Object.create(null); } -} -const cacheDefault = { - create: () => new ObjectWithoutPrototypeCache() -} + _createClass(ObjectWithoutPrototypeCache, [{ + key: "has", + value: function has(key) { + return key in this.cache; + } + }, { + key: "get", + value: function get(key) { + return this.cache[key]; + } + }, { + key: "set", + value: function set(key, value) { + this.cache[key] = value; + } + }, { + key: "delete", + value: function _delete(key) { + delete this.cache[key]; + } + }]); + + return ObjectWithoutPrototypeCache; +}(); + +var cacheDefault = { + create: function create() { + return new ObjectWithoutPrototypeCache(); + } +}; \ No newline at end of file diff --git a/src/index.test.js b/src/index.test.js index b8cf728..6e26725 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -144,3 +144,21 @@ test('inject custom serializer', function () { expect(serializerMethodExecutionCount).toBe(2) }) + +test('memoize circular JSON', function () { + var circular = { + a: 'foo' + } + circular.b = circular + + function circularFunction(a) { + return a.a + } + + var memoizedCircularFunction = memoize(circularFunction) + + // Assertions + + expect(memoizedCircularFunction(circular)).toBe("foo") + expect(memoizedCircularFunction(circular)).toBe("foo") +})