Initial (derived from es5-ext package)

This commit is contained in:
Mariusz Nowak
2012-08-06 12:40:20 +02:00
commit dc0d62cf7e
11 changed files with 922 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.DS_Store
/node_modules
/npm-debug.log

12
.travis.yml Normal file
View File

@@ -0,0 +1,12 @@
language: node_js
node_js:
- 0.6
- 0.8
- 0.9
before_script:
- ln -s ../ node_modules/memoize
notifications:
email:
- medikoo+memoize@medikoo.com

2
CHANGES Normal file
View File

@@ -0,0 +1,2 @@
v0.1.0 -- ?
* Initial (taken out from es5-ext package)

19
LICENCE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (C) 2012 Mariusz Nowak (www.medikoo.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

9
Makefile Normal file
View File

@@ -0,0 +1,9 @@
SHELL = bash
install:
npm install
test:
npm test
.PHONY: install test

143
README.md Normal file
View File

@@ -0,0 +1,143 @@
# memoize - Memoization for any type and length of function arguments
Complete memoize solution, originally derived from [es5-ext](https://github.com/medikoo/es5-ext) package. Works with any type and length of function arguments. It's one of the fastest solutions available.
```javascript
memoized = memoize(fn);
memoized('foo', 3);
memoized('foo', 3); // Cache hit
```
## Installation
### Node.js
In your project path:
$ npm install memoizee
<a name="installation-browser" />
### Browser
You can easily create browser bundle with help of [modules-webmake](https://github.com/medikoo/modules-webmake). Mind that it relies on some EcmaScript5 features, so for older browsers you need as well [es5-shim](https://github.com/kriskowal/es5-shim)
## Options
### Arguments length
By default fixed number of arguments that function takes is assumed (it's
assumed on function's `length` property) this behaviour can be overriden:
```javascript
memoized = memoize(fn, { length: 2 });
memoized('foo'); // Assumed ('foo', undefined)
memoized('foo', undefined); // Cache hit
memoized('foo', 3, {}); // Third argument is ignored (but passed to underlying function)
memoized('foo', 3, 13); // Cache hit
```
Dynamic _length_ behavior can be forced by setting `length` to `false`, that means memoize will work with any number of arguments.
```javascript
memoized = memoize(fn, { length: false });
memoized('foo');
memoized('foo'); // Cache hit
memoized('foo', undefined);
memoized('foo', undefined); // Cache hit
memoized('foo', 3, {});
memoized('foo', 3, 13);
memoized('foo', 3, 13); // Cache hit
```
### Resolvers
When expecting arguments of certain types it's good to coerce them before doing memoization. We can do that by passing additional resolving functions array:
```javascript
memoized = memoize(fn, { length: 2, resolvers: [String, Boolean] });
memoized(12, [1,2,3].length);
memoized("12", true); // Cache hit
memoized({ toString: function () { return "12"; } }, {}); // Cache hit
```
### Primitive mode
Dealing with input arguments as they are, may not be performant on large result sets. Optionally memoization can be run in _primitive_ mode, internally then obtained results are saved on hash (not array) and arguments are coerced to strings to generate unique hash id.
This mode will work properly only if your arguments can be coerced to unique strings. Mind also that perfmance gain when using this mode is only observed on large result sets (thousands of results) otherwise is not worth a hassle.
```javascript
memoized = memoize(fn, { primitive: true });
memoized('/path/one');
memoized('/path/one'); // Cache hit
```
### Memoizing a method
When we're defining a prototype, we may want to define method that will memoize it's results in relation to each instance. Normal way to obtain that would be:
```javascript
var Foo = function () {
memoize(this.bar.bind(this));
// ... constructor logic
};
Foo.prototype.bar = function () {
// ... method logic
};
```
With `method` option we can configure memoization directly on prototype and not in constructor. Following will have same effect:
var Foo = function () {
// ... constructor logic
};
Foo.prototype.bar = memoize(function () {
// ... method logic
}, { method: 'bar' });
Additionally we may provide descriptor which would be used for defining method on instance object:
var Foo = function () {
// ... constructor logic
};
Foo.prototype.bar = memoize(function () {
// ... method logic
}, { method: { name: 'bar', descriptor: { configurable: true } } });
#### Cache handling
Collected cache can be cleared. To clear all collected data:
memoizedFn.clearAll();
or to clear data for particall call.
memoizedFn.clear('foo', true);
Arguments passed to `clear` are treated with same rules as input arguments passed to function
## Profiling & Statistics
`lib/profile` module provides statistical data about memoized function calls. How many calls where initial and how many were result of cache hit. To collect data it needs to be imported before memoize initialization of functions that we want to track.
```javascript
var memProfile = require('memoizee/lib/profile')
, memoize = require('memoizee');
var memoized = memoize(fn);
memoized(1, 2);
memoized(1, 2);
memProfile.statistics // Holds statistics data in convenient hash form
memProfile.log(); // Outputs statictis data in readable form (using console.log)
```
## Tests [![Build Status](https://secure.travis-ci.org/medikoo/memoize.png?branch=master)](https://secure.travis-ci.org/medikoo/memoize)
$ npm test

292
lib/index.js Normal file
View File

@@ -0,0 +1,292 @@
'use strict';
var isArray = Array.isArray
, join = Array.prototype.join
, map = Array.prototype.map
, push = Array.prototype.push
, slice = Array.prototype.slice
, apply = Function.prototype.apply
, create = Object.create
, defineProperty = Object.defineProperty
, global = require('es5-ext/lib/global')
, toArray = require('es5-ext/lib/Array/from')
, indexOf = require('es5-ext/lib/Array/prototype/e-index-of')
, callable = require('es5-ext/lib/Object/valid-callable')
, isString = require('es5-ext/lib/String/is-string')
, resolve, memoize, id = 0;
resolve = function (args) {
return this.map(function (r, i) {
return r ? r(args[i]) : args[i];
}).concat(slice.call(args, this.length));
};
// Implementation details:
//
// Results are saved internally within array matrix:
// cache[0] -> Result of calling function with no arguments
// cache[1] -> Matrix that keeps results for one argument function calls
// cache[1][0] -> Array of different arguments with which
// function have been called
// cache[1][1] -> Array of results that matches cache[1][0] arguments array
// cache[2] -> Matrix that keeps results for two argument function calls
// cache[2][0] -> Array of different first (of two) arguments with which
// function have been called
// cache[2][1] -> Matrixes that keeps results for two arguments function calls
// Each matrix matches first argument found in cache[2][0]
// cache[2][1][x][0] -> Array of different second arguments with which
// function have been called.
// cache[2][1][x][1] -> Array of results that matches cache[2][1][x][0]
// arguments array
// ...and so on
memoize = module.exports = function () {
var fn, mfn, options, length, resolver, method, cache, find, save, clear
, value, primitive, getPrimitiveId, profile, gcMode, onclear;
fn = callable(this);
if (fn.memoized) {
// Prevent memoization of already memoized function
return fn;
}
options = Object(arguments[0]);
if (isNaN(options.length)) {
length = fn.length;
} else if (options.length === false) {
length = options.length;
} else {
length = Number(options.length);
}
if (options.resolvers) {
resolver = toArray(options.resolvers);
resolver.forEach(function (r) {
(r == null) || callable(r);
});
resolver = resolve.bind(resolver);
}
if (options.method) {
if (isString(options.method)) {
method = { name: String(options.method),
descriptor: { configurable: true, writable: true } }
} else {
method = options.method;
method.name = String(method.name);
method.descriptor = (method.descriptor == null) ?
{ configurable: true, writable: true } : Object(method.descriptor);
}
options = create(options);
options.method = undefined;
}
primitive = Boolean(options.primitive);
gcMode = Boolean(options.gcMode);
if (options.onclear != null) {
onclear = callable(options.onclear);
}
cache = primitive ? {} : [];
if (memoize._profile) {
profile = memoize._profile();
}
find = function (length, args) {
var index = 0, rset = cache, i;
if (length === 0) {
value = rset[length];
return rset.hasOwnProperty(length);
} else if ((rset = rset[length])) {
while (index < (length - 1)) {
i = indexOf.call(rset[0], args[index]);
if (i === -1) {
return false;
}
rset = rset[1][i];
++index;
}
i = indexOf.call(rset[0], args[index]);
if (i === -1) {
return false;
}
value = rset[1][i];
if (gcMode) {
++value.count;
value = value.value;
}
return true;
}
return false;
};
save = function (length, args, value) {
var index = 0, rset = cache, i;
if (gcMode) {
value = { value: value, count: 1 };
}
if (length === 0) {
rset[length] = value;
} else {
if (!rset[length]) {
rset[length] = [[], []];
}
rset = rset[length];
while (index < (length - 1)) {
i = indexOf.call(rset[0], args[index]);
if (i === -1) {
i = rset[0].push(args[index]) - 1;
rset[1].push([[], []]);
}
rset = rset[1][i];
++index;
}
i = indexOf.call(rset[0], args[index]);
if (i === -1) {
i = rset[0].push(args[index]) - 1;
}
rset[1][i] = value;
}
};
clear = function (length, args) {
var index = 0, rset = cache, i, path = [], value;
if (length === 0) {
if (gcMode) {
value = rset[length];
if (value) {
if (--value.count) {
return;
}
delete rset[length];
}
} else {
delete rset[length];
}
} else if ((rset = rset[length])) {
while (index < (length - 1)) {
i = indexOf.call(rset[0], args[index]);
if (i === -1) {
return;
}
path.push(rset, i);
rset = rset[1][i];
++index;
}
i = indexOf.call(rset[0], args[index]);
if (i === -1) {
return;
}
if (gcMode) {
value = rset[1][i];
if (--value.count) {
return;
}
}
rset[0].splice(i, 1);
rset[1].splice(i, 1);
while (!rset[0].length && path.length) {
i = path.pop();
rset = path.pop();
rset[0].splice(i, 1);
rset[1].splice(i, 1);
}
}
if (value && onclear) {
onclear(value.value, args);
}
};
getPrimitiveId = function (length, args) {
var argsLength;
if (length) {
argsLength = args.length;
if (length < argsLength) {
args = slice.call(args, 0, length);
} else if (length > argsLength) {
args = toArray(args);
push.apply(args, Array(length - argsLength));
}
return (length > 1) ? join.call(args, '\u0001') :
(args[0] + '\u0002');
} else {
return '';
}
};
mfn = function () {
var args, alength, id;
if (method && this && (this !== global)) {
method.descriptor.value = memoize.call(fn.bind(this), options);
defineProperty(this, method.name, method.descriptor);
return method.descriptor.value.apply(this, arguments);
}
args = resolver ? resolver(arguments) : arguments;
alength = (length === false) ? args.length : length;
if (primitive) {
id = getPrimitiveId(alength, args);
if (cache.hasOwnProperty(id)) {
profile && ++profile.cached;
if (gcMode) {
++cache[id].count;
return cache[id].value;
}
return cache[id];
} else {
profile && ++profile.initial;
mfn.args = arguments;
mfn.preventCache = false;
value = apply.call(fn, this, args);
if (!mfn.preventCache) {
cache[id] = gcMode ? { value: value, count: 1 } : value;
}
delete mfn.args;
return value;
}
}
if (find(alength, args)) {
profile && ++profile.cached;
return value;
} else {
profile && ++profile.initial;
mfn.args = arguments;
mfn.preventCache = false;
value = apply.call(fn, this, args);
if (!mfn.preventCache) {
save(alength, args, value);
}
delete mfn.args;
return value;
}
};
mfn.memoized = true;
mfn.clear = function () {
var args, alength, id, value;
args = resolver ? resolver(arguments) : arguments;
alength = (length === false) ? args.length : length;
if (primitive) {
id = getPrimitiveId(alength, args);
if (gcMode && ((value = cache[id])) && (--value.count)) {
return;
}
delete cache[id];
if (value && onclear) {
onclear(value.value, args);
}
} else {
clear(alength, args);
}
};
mfn.clearAll = function () {
cache = primitive ? {} : [];
};
return mfn;
};

57
lib/profile.js Normal file
View File

@@ -0,0 +1,57 @@
'use strict';
var max = Math.max
, partial = require('es5-ext/lib/Function/prototype/partial')
, forEach = require('es5-ext/lib/Object/for-each')
, pad = require('es5-ext/lib/String/prototype/pad')
, memoize = require('./')
, stats = exports.statistics = {};
memoize._profile = function () {
var id, stack = (new Error()).stack;
id = stack.split('\n')[3].replace(/\n/g, "\\n").trim();
return (stats[id] = { initial: 0, cached: 0 });
};
exports.log = function () {
var initial, cached, ordered, ipad, cpad, ppad, toPrc;
initial = cached = 0;
ordered = [];
toPrc = function (initial, cached) {
if (!initial && !cached) {
return '0.00%';
}
return ((cached / (initial + cached)) * 100).toFixed(2) + '%';
};
console.log("------------------------------------------------------------");
console.log("Memoize statistics:");
forEach(stats, function (data, name) {
initial += data.initial;
cached += data.cached;
ordered.push([name, data]);
}, null, function (a, b) {
return (this[b].initial + this[b].cached) -
(this[a].initial + this[a].cached);
});
console.log("");
ipad = partial.call(pad, " ",
max(String(initial).length, "Initial".length));
cpad = partial.call(pad, " ", max(String(cached).length, "From cache".length));
ppad = partial.call(pad, " ", 6);
console.log(ipad.call("Initial"), " ", cpad.call("From cache"),
" ", ppad.call("Gain"));
console.log(ipad.call(initial), " ", cpad.call(cached),
" ", ppad.call(toPrc(initial, cached)), " Total");
ordered.forEach(function (data) {
var name = data[0];
data = data[1];
console.log(ipad.call(data.initial), " ", cpad.call(data.cached),
" ", ppad.call(toPrc(data.initial, data.cached)), " " + name);
});
console.log("------------------------------------------------------------");
};

43
package.json Normal file
View File

@@ -0,0 +1,43 @@
{
"name": "memoizee",
"version": "0.1.0",
"description": "Memoization for any type and length of function arguments",
"main": "lib",
"scripts": {
"test": "node node_modules/tad/bin/tad lib"
},
"repository": {
"type": "git",
"url": "git://github.com/medikoo/memoize.git"
},
"keywords": [
"memoize",
"cache",
"memoization",
"memo",
"memcached",
"hashing.",
"storage",
"caching",
"memory",
"gc",
"weak",
"garbage",
"collector"
],
"bugs": {
"email": "medikoo+memoize@medikoo.com",
"url": "https://github.com/medikoo/memoize/issues"
},
"engines": {
"node": ">=0.4"
},
"dependencies": {
"es5-ext": "git://github.com/medikoo/es5-ext.git"
},
"devDependencies": {
"tad": "0.1.x"
},
"author": "Mariusz Nowak <medikoo+memoize@medikoo.com> (http://www.medikoo.com/)",
"license": "MIT"
}

333
test/index.js Normal file
View File

@@ -0,0 +1,333 @@
'use strict';
var toArray = require('es5-ext/lib/Array/from');
module.exports = function (t, a) {
return {
"0": function () {
var i = 0, fn = function () { ++i; return 3; };
fn = t.call(fn);
a(fn(), 3, "First");
a(fn(1), 3, "Second");
a(fn(5), 3, "Third");
a(i, 1, "Called once");
},
"1": function () {
var i = 0, fn = function (x) { ++i; return x; };
fn = t.call(fn);
return {
"No arg": function () {
i = 0;
a(fn(), undefined, "First");
a(fn(), undefined, "Second");
a(fn(), undefined, "Third");
a(i, 1, "Called once");
},
"Arg": function () {
var x = {};
i = 0;
a(fn(x, 8), x, "First");
a(fn(x, 4), x, "Second");
a(fn(x, 2), x, "Third");
a(i, 1, "Called once");
},
"Other Arg": function () {
var x = {};
i = 0;
a(fn(x, 2), x, "First");
a(fn(x, 9), x, "Second");
a(fn(x, 3), x, "Third");
a(i, 1, "Called once");
}
};
},
"3": function () {
var i = 0, fn = function (x, y, z) { ++i; return [x, y, z]; }, r;
fn = t.call(fn);
return {
"No args": function () {
i = 0;
a.deep(r = fn(), [undefined, undefined, undefined], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function () {
var x = {};
i = 0;
a.deep(r = fn(x, 8), [x, 8, undefined], "First");
a(fn(x, 8), r, "Second");
a(fn(x, 8), r, "Third");
a(i, 1, "Called once");
return {
"Other": function () {
a.deep(r = fn(x, 5), [x, 5, undefined], "Second");
a(fn(x, 5), r, "Third");
a(i, 2, "Called once");
}
};
},
"Full stuff": function () {
var x = {};
i = 0;
a.deep(r = fn(x, 8, 23, 98), [x, 8, 23], "First");
a(fn(x, 8, 23, 43), r, "Second");
a(fn(x, 8, 23, 9), r, "Third");
a(i, 1, "Called once");
return {
"Other": function () {
a.deep(r = fn(x, 23, 8, 13), [x, 23, 8], "Second");
a(fn(x, 23, 8, 22), r, "Third");
a(i, 2, "Called once");
}
};
}
};
},
"Dynamic": function () {
var i = 0, fn = function () { ++i; return arguments; }, r;
fn = t.call(fn, { length: false });
return {
"No args": function () {
i = 0;
a.deep(toArray(r = fn()), [], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function () {
var x = {};
i = 0;
a.deep(toArray(r = fn(x, 8)), [x, 8], "First");
a(fn(x, 8), r, "Second");
a(fn(x, 8), r, "Third");
a(i, 1, "Called once");
},
"Many args": function () {
var x = {};
i = 0;
a.deep(toArray(r = fn(x, 8, 23, 98)), [x, 8, 23, 98], "First");
a(fn(x, 8, 23, 98), r, "Second");
a(fn(x, 8, 23, 98), r, "Third");
a(i, 1, "Called once");
}
};
},
"Original arguments": function (a) {
var fn, mfn, x = {};
fn = function (x, y) { return toArray(mfn.args); };
mfn = t.call(fn);
a.deep(mfn(23, 'raz', x), [23, 'raz', x]);
},
"Resolvers": function () {
var i = 0, fn, fn2, r, j = 0, z;
fn = t.call(function () { ++i; return arguments; },
{ length: 3, resolvers: [Boolean, String] });
return {
"No args": function () {
i = 0;
a.deep(toArray(r = fn()), [false, 'undefined'], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function () {
var x = {};
i = 0;
a.deep(toArray(r = fn(0, 34, x, 45)), [false, '34', x, 45],
"First");
a(fn(0, 34, x, 22), r, "Second");
a(fn(0, 34, x, false), r, "Third");
a(i, 1, "Called once");
return {
"Other": function () {
a.deep(toArray(r = fn(1, 34, x, 34)),
[true, '34', x, 34], "Second");
a(fn(1, 34, x, 89), r, "Third");
a(i, 2, "Called once");
}
};
}
};
},
"Clear Cache": {
"Specific": function () {
var i = 0, fn, mfn, r, x = {};
fn = function (a, b, c) {
if (c === 3) {
++i;
}
return arguments;
}
mfn = t.call(fn);
mfn(1, x, 3);
mfn(1, x, 4);
mfn.clear(1, x, 4);
mfn(1, x, 3);
mfn(1, x, 3);
a(i, 1, "Pre clear");
mfn.clear(1, x, 3);
mfn(1, x, 3);
a(i, 2, "After clear");
i = 0;
mfn = t.call(fn, { length: false });
mfn(1, x, 3);
mfn(1, x, 3);
mfn();
mfn();
mfn.clear();
mfn(1, x, 3);
a(i, 1, "Proper no arguments clear");
},
"All": function () {
var i = 0, fn, r, x = {};
fn = function (a, b, c) {
++i;
return arguments;
}
fn = t.call(fn);
fn(1, x, 3);
fn(1, x, 4);
fn(1, x, 3);
fn(1, x, 4);
a(i, 2, "Pre clear");
fn.clearAll();
fn(1, x, 3);
fn(1, x, 4);
fn(1, x, 3);
fn(1, x, 4);
a(i, 4, "After clear");
}
},
"Method": {
"No descriptor": function (a) {
var mfn, x = {}, i = 0, fn = function () {
++i;
return this;
};
mfn = t.call(fn, { method: 'foo' });
a(mfn.call(x), x, "Context");
a(x.foo(), x, "Method");
a(i, 1, "Cached");
},
"Descriptor": function (a) {
var mfn, x = {}, i = 0, fn = function () {
++i;
return this;
};
mfn = t.call(fn,
{ method: { name: 'foo', descriptor: { configurable: true } } });
a(mfn.call(x), x, "Context");
a.deep(Object.getOwnPropertyDescriptor(x, 'foo'),
{ enumerable: false, configurable: true, writable: false,
value: x.foo });
a(x.foo(), x, "Method");
a(i, 1, "Cached");
}
},
"Primitive": {
"No args": function (a) {
var i = 0, fn = function () { ++i; return arguments[0]; }, mfn;
mfn = t.call(fn, { primitive: true });
a(mfn('ble'), 'ble', "#1");
a(mfn({}), 'ble', "#2");
a(i, 1, "Called once");
},
"One arg": function (a) {
var i = 0, fn = function (x) { ++i; return x; }, mfn
, y = { toString: function () { return 'foo' } };
mfn = t.call(fn, { primitive: true });
a(mfn(y), y, "#1");
a(mfn('foo'), y, "#2");
a(i, 1, "Called once");
},
"Many args": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, y = { toString: function () { return 'foo' } };
mfn = t.call(fn, { primitive: true });
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
},
"Clear cache": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, y = { toString: function () { return 'foo' } };
mfn = t.call(fn, { primitive: true });
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
mfn.clear('foo', { toString: function () { return 'bar'; } },
'zeta');
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#3");
a(i, 2, "Called twice");
}
},
"GC Mode": {
"Regular": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, invoked = false;
mfn = t.call(fn, { gcMode: true, onclear: function (val, args) {
a(val, 15, "onclear: Value");
a.deep(toArray(args), [3, 5, 7], "onclear: Arguments");
invoked = true;
} });
mfn.clear(3, 5, 7);
a(mfn(3, 5, 7), 15, "Initial");
a(mfn(3, 5, 7), 15, "Cache");
mfn.clear(3, 5, 7);
mfn(3, 5, 7);
mfn.clear(3, 5, 7);
mfn(3, 5, 7);
mfn.clear(3, 5, 7);
mfn(3, 5, 7);
a(i, 1, "Not cleared");
mfn.clear(3, 5, 7);
mfn.clear(3, 5, 7);
a(invoked, true, "Cleared");
mfn(3, 5, 7);
a(i, 2, "Restarted");
mfn(3, 5, 7);
a(i, 2, "Cached again");
},
"Primitive": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, invoked = false;
mfn = t.call(fn, { primitive: true, gcMode: true,
onclear: function (val, args) {
a(val, 15, "onclear: Value");
a.deep(toArray(args), [3, 5, 7], "onclear: Arguments");
invoked = true;
} });
mfn.clear(3, 5, 7);
a(mfn(3, 5, 7), 15, "Initial");
a(mfn(3, 5, 7), 15, "Cache");
mfn.clear(3, 5, 7);
mfn(3, 5, 7);
mfn.clear(3, 5, 7);
mfn(3, 5, 7);
mfn.clear(3, 5, 7);
mfn(3, 5, 7);
a(i, 1, "Not cleared");
mfn.clear(3, 5, 7);
mfn.clear(3, 5, 7);
a(invoked, true, "Cleared");
mfn(3, 5, 7);
a(i, 2, "Restarted");
mfn(3, 5, 7);
a(i, 2, "Cached again");
}
}
};
};

9
test/profile.js Normal file
View File

@@ -0,0 +1,9 @@
'use strict';
var memoize = require('../lib')
module.exports = function (t, a) {
a(typeof memoize._profile, 'function', "Set on memoize");
a(typeof t.statistics, 'object', "Access to statistics");
a(typeof t.log, 'function', "Access to log function");
};