DEV Community

Dhilip kumar
Dhilip kumar

Posted on

Understand how to Memoize a JavaScript function

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;
}

Enter fullscreen mode Exit fullscreen mode
  • The memoize function takes in a function fnToMemoize as a single Argument and returns a function 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 from memoizedCache or we actually call the function fnToMemoize and store the result in the memoizedCache.
//  `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];
  }
}


Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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));

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
logicmason profile image
Mark

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.

Collapse
 
dhilipkmr profile image
Dhilip kumar

Glad it helped :)

Collapse
 
calag4n profile image
calag4n

It's clever, I love this, nice work dude!

Collapse
 
dhilipkmr profile image
Dhilip kumar

😅✌️✌️

Collapse
 
nickfazzpdx profile image
Nicholas Fazzolari

What would be a potential context/use case for this optimization in the wild?

Collapse
 
dhilipkmr profile image
Dhilip kumar • Edited

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)