Memoization is a technique used in computer programming to improve the performance of functions by reducing the amount of work they need to do. In essence, memoization allows a function to remember the results of its previous computations so that it can avoid repeating them when given the same inputs. Memoization is an extremely important example of a higher-order function.
The concept of memoization is fairly simple. When a function is called with a specific set of input parameters, it computes a result and returns it. If the same function is called again with the same input parameters, the function can skip the computation step and simply return the previously computed result. This can save a significant amount of processing time, especially for functions that are computationally expensive.
One of the most common use cases for memoization is in recursive functions. Recursive functions are functions that call themselves repeatedly with different input parameters. If the function is not properly optimized, it can quickly become very slow as the number of recursive calls increases. Memoization can help to reduce the number of recursive calls by allowing the function to remember the results of previous calls.
To implement memoization in JavaScript, we can use an object to store the results of previous computations. This object is often referred to as a "cache". Here's an example:
let cache = {};
function fibonacci(n) {
if (n in cache) {
return cache[n];
} else {
if (n <= 1) {
return n;
} else {
let result = fibonacci(n - 1) + fibonacci(n - 2);
cache[n] = result;
return result;
}
}
}
In this example, we define a function called fibonacci
that computes the nth Fibonacci number. The Fibonacci sequence is a series of numbers in which each number is the sum of the two preceding numbers. For example, the first ten numbers in the Fibonacci sequence are: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34.
The fibonacci
function checks whether the result for the current input parameter (n) is already stored in the cache. If it is, it returns the result from the cache. If it's not, it computes the result as usual and stores it in the cache for future use.
Using memoization in this way can significantly improve the performance of the fibonacci
function, especially for large values of n. Without memoization, the function would need to recompute many of the same values over and over again, leading to a significant amount of unnecessary work.
It's worth noting that memoization is not always appropriate or necessary. In some cases, the overhead of maintaining a cache can actually make a function slower. Additionally, memoization can be problematic if the function being memoized has side effects (i.e. it modifies some state outside of the function itself). In these cases, it's often better to simply optimize the function itself rather than relying on memoization.
It is also important to note that memoization only works correctly for pure functions. A pure function is defined as function that always returns the same output given the same inputs and doesn't have any side-effects.
For example, suppose you attempted to memoize the impure function Date.now which returns the current time in milliseconds since the unix epoch.
const getCurrentTimeMemoized = memoize(Date.now);
getCurrentTimeMemoized(); // 1683784131157
getCurrentTimeMemoized(); // 1683784131157
getCurrentTimeMemoized(); // 1683784131157
getCurrentTimeMemoized
correctly returns the current time the first time it is called. But each subsequent time, it incorrectly returns the same value.
Memoization Use-cases in Web Development
There are countless use-cases of memoization but we can discuss a few.
Caching Website Files
A large website often consists of many JavaScript files which are dynamically downloaded when a user visits different pages. A pattern is sometimes employed where the filename is based on a hash of the file's content. That way, when the web browser requests a filename that was already requested before, it can load the file locally from disk rather than have to download it again.
React Components
React is a highly popular library for building user interfaces, especially for single-page applications. One of its core principles is the idea of breaking down your application into separate components. Each of these components is responsible for rendering a distinct part of the app's HTML.
For example you might have a component like this:
const TitleComponent = (props) => {
return <h1>{props.title}</h1>;
};
The above function will get called every time the parent component renders, even if title was not changed. Performance can be improved by calling React.memo
on it, avoiding unnecessary renders.
In conclusion, memoization is a powerful technique for improving the performance of functions by avoiding redundant computation. It works by storing the results of previous computations in a cache and reusing those results when possible. While memoization can be very effective in certain situations, it's important to use it judiciously and be aware of its potential limitations.
Top comments (0)