Функции могут использовать объекты для запоминания результатов предыдущих операций, что позволяет избежать ненужной работы. Эта оптимизация называется запоминание. Объекты и массивы JavaScript очень удобны для этого.

Давайте разберемся в этом, написав программу для вычисления чисел Фибоначчи.

var fibonacci = function (n) {
    return n < 2 ? n : fibonacci(n − 1) + fibonacci(n − 2);
};

for (var i = 0; i <= 10; i += 1) {
    console.log('// ' + i + ': ' + fibonacci(i));
}

В приведенном выше сценарии вычисление числа Фибоначчи не оптимизировано, поскольку функция Фибоначчи выполняется больше требуемого количества раз.

Он вызывается нами 11 раз внутри цикла for и каждый раз сам по себе возвращает значения, которые, вероятно, уже были недавно вычислены.

Мы сохраним наши запомненные результаты в массиве memo, который мы можем скрыть в замыкании. Когда наша функция вызывается, она сначала проверяет, известен ли ей результат. Если да, то она может немедленно вернуть его:

var fibonacci = (function (  ) {
    var memo = [0, 1];
    var fib = function (n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = fib(n − 1) + fib(n − 2);
            memo[n] = result;
        }
        return result;
    };
    return fib;
}( ));

В этом случае функции будут вызываться сами по себе только 29 раз, а цикл for — 11 раз. Давайте рассмотрим еще один простой пример мемоизации:

// a simple function to add something
let addNum = (n) => (n + 1);
addNum(10);
// a simple memoized function to add something
let cachedAddNum = () => {
  let cache = {};
  return (n) => {
    if (n in cache) {
      console.log('Fetching from cache');
      return cache[n];
    }
    else {
      console.log('Calculating result');
      let result = n + 10;
      cache[n] = result;
      return result;
    }
  }
}
// returned function from memoizedAdd
const add = cachedAddNum();
console.log(add(10)); // calculated
console.log(add(10)); // cached

Примечание. Запоминать можно только чистую функцию.

Чистая функция – это та, которая

  • Возвращает один и тот же вывод для одного и того же ввода каждый раз.
  • Не зависит и не изменяет состояние переменных за пределами своей области.

Используя описанную выше концепцию, вы можете написать обобщенный мемоизатор, который принимает функцию, которую необходимо запомнить, и формулу, которая вычислит результат и вернет его вызывающему.