What is Memoization anyway?
The ability to return the previously calculated value without recalculating them, on receiving same set of inputs again is basically what memoization is.
So whenever a Function receives the same set of input arguments it checks in its cache variable if there is a value already exists for it then returns that value or does a recalculation.
- It Helps in reducing the computation time.
- Faster render time
Outline:
- There is a summation function that adds two numbers.
- We create our own
memoization
function. - Use
memoization
function as Higher Order Function and create an output function. - Call the above Output function instead, when we need to call summation function.
Let's get Started.
Function summation
is our function that we are going to Memoize.
It is a simple function which adds two numbers and returns the result.
// Function that sums two numbers
const summation = function (a, b) {
return a + b;
}
- The
memoize
function takes in a functionfnToMemoize
as a single Argument and returns afunction
which can be called upon. -
memoizedCache
is an object where we cache our new results. -
constructPropertyFromArgs
is used to create a unique property name based on the argument and function we pass.We will see about that in details in next Section. -
manageInsertion
is used to delete the property from the cache object if the maximum size is reached.(default length : 10) - First we check if the property is present in the
memoizedCache
, if yes, we return result frommemoizedCache
or we actually call the functionfnToMemoize
and store the result in thememoizedCache
.
// `memoize` function decides if it has to return cached value or call the summation function
const memoize = function (fnToMemoize) {
const memoizedCache = {} // A closeure Object
return function(...args) {
const propToCheck = constructPropertyFromArgs(fnToMemoize, args);
if (!memoizedCache[propToCheck]) {
memoizedCache[propToCheck] = fnToMemoize(...args);
} else {
console.log('From Cache ');
}
return memoizedCache[propToCheck];
}
}
How do we construct a property name?
This is crucial, as improper naming may result in unexpected behaviour of the app.
The memoize
function can act as a generic function, through which we can memoize any of our other functions that are lying in the same scope.So, in order to avoid misbehaviour we need to have unique names to our functions.
Our Property name is a combination of function name and arguments separated by '|' which acts as a delimiter.
Why do we need Delimiter?
Let's say if we don't use a Delimiter and just join the string.
Here, the Property name for add (fn, 1, 2, 3)
will be fn123
.
And, the Property name for add (fn, 12, 3)
will also be fn123
.
So output of add(fn, 12,3)
will be 6 which is calculated from the previous execution.
// To create a Property name from the arguments passed to the function
const constructPropertyFromArgs = function (fnToMemoize, args) {
let propToCheck = [];
propToCheck = propToCheck.concat(fnToMemoize.name, args);
return propToCheck.join('|'); // A delimiter to join args
}
Finally we pass our summation
function to our memoize
function that returns a function which is stored in memSummation
.
Then we call memSummation
twice.
const memSummation = memoize(summation, 2); // `memoize` is a HOC
console.log(memSummation(10, 50));
console.log(memSummation(10, 50));
The output:
First console.log() returns output after execution whereas the second one is returned from the cache.
"From Summation function"
60
"From Cache "
60
Limitations in this approach:
- Anonymous functions are not handled
- Should be careful with choosing delimiter, as it fails for strings with the same delimiter as argument.
- Works only on pure functions
- No way to control Space Complexity.
A space complexity considered Example is in this blog
Find the CodePen Link here
Space Complexity considered codepen example
Checkout my other posts here
Don't forget to Follow me for Interesting posts :)
That's all Folks :)
Top comments (6)
I love this. When I was first really digging into JavaScript, I worked through re-implementing everything in Underscore.js and found it super valuable.
_.once
and_.memo
were pretty hard and really showed how scope and closures were different in JS than I'd realized.Glad it helped :)
It's clever, I love this, nice work dude!
π βοΈβοΈ
What would be a potential context/use case for this optimization in the wild?
The summation function here is so simple. But consider you are making an expensive operation instead of just sum.In that case if you already have a cached value from previous call it becomes handy.
Even react.memo should be doing something similar to this(But handling all limitations which my solution had)