Implementing a Custom memoizeLast() Function in JavaScript



The memoizeLast() method is a variation of memoization that specifically caches the result of the most recent function call. Unlike traditional memoization, which might cache results for multiple sets of arguments, memoizeLast() only remembers the last set of arguments and the corresponding result. This is useful for functions that are often called with the same arguments consecutively.

Let’s implement a custom memoizeLast() function in JavaScript.


What is memoizeLast()?

The memoizeLast() function is a performance optimization technique that caches the result of the last function call. When the function is called again with the same arguments, it returns the cached result instead of recomputing it. If the function is called with different arguments, the cache is updated with the new result.

Real Interview Insights

Interviewers might ask you to:

  • Implement a function that caches only the result of the most recent function call.
  • Handle comparisons of complex argument types.
  • Ensure the cache is updated appropriately when the function is called with new arguments.

Implementing customMemoizeLast Function

Here’s how you can implement a custom memoizeLast function:

function customMemoizeLast(func) {
  let lastArgs = null;
  let lastResult = null;
 
  return function(...args) {
    if (lastArgs && lastArgs.length === args.length && lastArgs.every((arg, index) => arg === args[index])) {
      return lastResult;
    }
 
    lastResult = func.apply(this, args);
    lastArgs = args;
    return lastResult;
  };
}
Explanation:
  • Last Arguments and Result: We store the last set of arguments in lastArgs and the corresponding result in lastResult.
  • Comparison Logic: When the memoized function is called, we compare the current arguments to the last cached arguments. If they match, we return the cached result. If they differ, we update the cache with the new arguments and result.
  • Handling Reference Types: The comparison here is shallow (i.e., it compares references for objects/arrays). For deep comparisons, a custom comparison function would be needed.

Practical Examples

Let's see the customMemoizeLast function in action:

// A simple function that adds two numbers
const add = customMemoizeLast((a, b) => {
  console.log('Calculating:', a, '+', b);
  return a + b;
});
 
console.log(add(2, 3)); // Output: Calculating: 2 + 3 \n 5
console.log(add(2, 3)); // Output: 5 (cached result)
console.log(add(3, 4)); // Output: Calculating: 3 + 4 \n 7
console.log(add(3, 4)); // Output: 7 (cached result)
 
// Handling functions with more complex arguments
const complexAdd = customMemoizeLast((obj1, obj2) => {
  console.log('Calculating:', obj1, '+', obj2);
  return obj1.value + obj2.value;
});
 
console.log(complexAdd({ value: 10 }, { value: 20 })); // Output: Calculating: {value: 10} + {value: 20} \n 30
console.log(complexAdd({ value: 10 }, { value: 20 })); // Output: 30 (cache miss because objects are not strictly equal)

Handling Edge Cases

  1. Argument Comparison: The default implementation uses shallow comparison (===) for arguments. If deep comparison is needed, this logic must be adjusted.
  2. Non-Primitive Arguments: Be mindful when using objects or arrays as arguments. Identical content but different references won't hit the cache.
  3. Empty Argument List: If the function is called without arguments, ensure it handles this case appropriately.

Use Cases for memoizeLast()

  1. Repeated Calls with Same Arguments: Optimize functions that are likely to be called repeatedly with the same arguments, like UI rendering functions.
  2. Computationally Expensive Functions: Cache the result of expensive computations when the input arguments are likely to remain the same.
  3. Event Handlers: Optimize event handlers that may be triggered multiple times with the same data.