подробное руководство, когда и как использовать мемоизацию в JavaScript: лучшие практики и примеры.

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

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

Что такое мемоизация?

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

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

В этом посте мы подробно рассмотрим преимущества концепции мемоизации в JavaScript. Мы рассмотрим, как он работает, когда его использовать, и приведем примеры использования. К тому времени, когда вы закончите читать эту статью, у вас будет полное представление о том, как мемоизация может улучшить функциональность вашего кода.

Зачем использовать мемоизацию?

Допустим, у вас есть функция, которая вычисляет n-е число в последовательности Фибоначчи:

 function fib(n) {
        if (n <= 1) {
            return n
        }
        return fib(n - 1) + fib(n - 2)
    }

fib(9) // outputs: 34 default: 0.13916015625 ms

Эта функция отлично работает при малых значениях n и занимает 0,13 мс для выполнения, но по мере увеличения n ее выполнение может занять много времени. Например, вычисление fib(40) занимает несколько миллисекунд, а fib(50) — несколько секунд.

function fib(n) {
        if (n <= 1) {
            return n
        }
        return fib(n - 1) + fib(n - 2)
    }
console.time()
fib(40) // outputs: 102334155 default: 1380.770751953125 ms
console.timeEnd()

В приведенном выше примере вы можете видеть, что для выполнения fib(40) потребовалось 1380,77 мс. Если эта функция fib(40) запускается снова и снова с одним и тем же аргументом, ее выполнение займет несколько миллисекунд. Это может привести к проблемам с производительностью и требует оптимизации. Используя мемоизацию, мы можем сократить время вычислений и оптимизировать функцию fib() .

Чтобы оптимизировать эту функцию с помощью мемоизации, мы можем кэшировать результаты каждого вычисления с помощью объекта:

const memoize = (func) => {
 const cache = {};
 }

В приведенном выше примере я создал функцию с именем «memoize», которая принимает другую функцию в качестве аргумента и содержит в себе пустой объект с именем «кэш».

Продолжим с функцией memoize:

const memoize = (func) => {
        const cache = {};
        return (...args) => {
            const key = args[0];
            if (key in cache) {
                return cache[key];
            } else {
                const result = func(key);
                cache[key] = result;
                return result;
            }
        }
    }

Запоминаемая функция, возвращаемая memoize, имеет объект кеша, который используется для хранения результатов предыдущих вызовов func. Объект кеша изначально является пустым объектом.
В этой реализации memoize используется функция стрелки и синтаксис остальных параметров (...args) для захвата произвольного количества аргументов, передаваемых функции запоминания.

Затем он использует поиск свойств объекта, чтобы проверить, существует ли [ключ] в объекте кеша, и оператор in, чтобы определить, присутствует ли [ключ] в объекте. Если [ключ] существует в объекте кеша, возвращается кэшированный результат.

В противном случае функция ввода вызывается с аргументами, а результат сохраняется в объекте кеша (cache[key] = result;) с использованием первого аргумента в качестве [ключа].

Вот полный код -

 function fib(n) {
        if (n <= 1) {
            return n
        }
        return fib(n - 1) + fib(n - 2)
    }

    const memoize = (func) => {
        const cache = {};
        return (...args) => {
            const key = args[0];
            if (key in cache) {
                return cache[key];
            } else {
                const result = func(key);
                cache[key] = result;
                return result;
            }
        }
    }


    const result = memoize(fib)
    console.time()
    console.log(result(40)); //Output 832040 "default: 1390.040771484375 ms"
    console.timeEnd()  

    console.time()
    console.log(result(40)); //Output 832040 "default: 0.041015625 ms"
    console.timeEnd()

И теперь, когда вы запускаете memoize с аргументом fib(40) несколько раз, вы можете видеть, что мемоизированная версия функции работает значительно быстрее исходной функции: для вычисления fib(40) требуется всего 0,04 мс вместо 1390,04. мс.

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

Когда использовать мемоизацию?

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

Вот несколько распространенных сценариев, в которых мемоизация может быть полезна:

  1. Рекурсивные функции. Рекурсивные функции часто выигрывают от запоминания, поскольку они часто вызывают себя повторно с одними и теми же входными параметрами.
  2. Дорогие вычисления. Если ваша функция выполняет ресурсоемкую операцию, например сортировку большого массива или выполнение вызова API, можно использовать мемоизацию, чтобы избежать повторения операции для одних и тех же входных параметров.
  3. Неизменяемые данные. Если ваша функция работает с неизменяемыми данными, можно использовать запоминание, чтобы избежать повторного вычисления результата функции для одних и тех же входных параметров, поскольку результат всегда будет одним и тем же.
  4. Извлечение данных. Если ваша функция извлекает данные из базы данных или внешнего источника, можно использовать мемоизацию, чтобы избежать многократного выполнения одного и того же запроса для одних и тех же входных параметров.

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

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

Спасибо за прочтение ! Надеюсь, вам понравилось узнавать об этой фантастической технике оптимизации.
Если она вам понравилась, поделитесь ею с друзьями, я буду очень признателен🧡.

Изучите продвинутую концепцию javascript.

Спасибо 🙂