Skip to content

Instantly share code, notes, and snippets.

@ThomasBurleson
Last active August 27, 2017 21:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ThomasBurleson/c8fce5fcedd92dcc9102 to your computer and use it in GitHub Desktop.
Save ThomasBurleson/c8fce5fcedd92dcc9102 to your computer and use it in GitHub Desktop.
Memoization functions with timeouts and call-once options...
;(function(root) {
'use strict';
// A 32-bit checksum of a string.
if(!String.prototype.checksum) {
String.prototype.checksum = checksum;
}
// Publish the functions to the `root` package
root.memoize = memoize;
root.timed = timed;
root.once = once;
root.timedOnce = timedOnce;
// ******************************************************
// Memoized Functions
// ******************************************************
/**
*
*/
function memoize(f) {
validateFunction( f );
/*
The cache object for the memoized function. Each function
will hold its own cache object. The object will be indexed
by 32-bit checksums of argument-lists.
*/
var cache = {};
return function MemoizedFunction() {
// Create a checksum of all the arguments, if any argument is available.
var checksum = calculateChecksum(arguments);
// if the checksum is unknown in the cache, call the function and cache it.
if ( !hasCheckSum(cache,checksum) }
cache[checksum] = f.apply(f, arguments);
}
// return the cached value.
return cache[checksum];
};
};
/**
*
*/
function timed(f, timeout) {
/*
The cache object for the memoized function. Each function
will hold its own cache object. The object will be indexed
by 32-bit checksums of argument-lists.
The times object maintains a list of 'last updated time'.
These times are used to determine the age of the cache
item.
*/
var cache = {};
var times = {};
validateFunction( f );
validateTimeout( timeout );
return function TimedMemoizedFunction() {
// What's the current time?
var now = +new Date();
var checksum = calculateChecksum(arguments);
/*
if the checksum is unknown in the cache, call the function and cache it.
if the last update time is unknown in the times cache, call the function and cache it.
if the current time minus the last update time is equal to or greater than the timeout,
call the function and cache it.
*/
if ( !hasCheckSum(cache,checksum) ||
!hasCheckSum(times,checksum) ||
(now - times[checksum]) >= timeout ) {
cache[checksum] = f.apply(f, arguments);
times[checksum] = now;
}
// return the cached value.
return cache[checksum];
};
};
/**
* Once is a function which allows a function to be called only once,
* regardless of the input.
*/
function once(f) {
var cached;
validateFunction( f );
return function OnceFunction() {
if(typeof(cached) === 'undefined') {
cached = f.apply(f, arguments);
}
return cached;
};
};
/**
*
*/
function timedOnce(f, timeout) {
var cached;
var lut = 0;
validateFunction( f );
validateTimeout( timeout );
return function TimedOnceFunction() {
var now = +new Date();
if(typeof(cached) === 'undefined' || (now - lut) >= timeout) {
cached = f.apply(f, arguments);
lut = now;
}
return cached;
};
};
// ******************************************************
// Internal Helper Functions
// ******************************************************
/**
*
*/
function hasCheckSum(cache, checksum) {
return (typeof(cache[checksum]) !== 'undefined');
}
/**
* Build checksum value representing the String's value
*/
function checkSum() {
var checksum = 0, i, chr, len;
if (this.length == 0) {
return checksum;
}
for (i = 0, len = this.length; i < len; i++) {
checksum = ((((checksum << 5) - checksum) + this.charCodeAt(i)) | 0);
}
return checksum;
};
/**
* Confirm that the argument is a Function reference
*/
function validateFunction(f) {
if(typeof(f) !== "function" || !(f instanceof Function)) {
throw new TypeError("Provided argument is not a function.");
}
}
/**
* Confirm that the variable is a number
*/
function validateTimeout(timeout) {
if(typeof(timeout) !== "number") {
throw new TypeError("Provided argument is not a number.");
}
}
/**
* For the specified Function arguments, calculate the checksum to lookup
* previous invocations of the Function.
*/
function calculateChecksum(args) {
// Create a checksum of all the arguments, if any argument is available.
var checksum = 0;
// Convert to array for .call() methods...
args = Array.prototype.slice.call(args);
if(args.length > 0) {
// if you don't expect complex argument structure, this will suffice
// checksum = Array.prototype.join.call(args, ",").checksum();
// serialize the arguments object to a JSON string and create a 32-bits checksum.
checksum = JSON.stringify(arguments).checksum();
}
return checksum;
};
}('mem' in window ? window.mem : window.mem = {}));
@ThomasBurleson
Copy link
Author

@ThomasBurleson
Copy link
Author

I also love the trick in the Module Wrapper (aka IIFE):

}('mem' in window ? window.mem : window.mem = {}));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment