HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>В этом уроке мы познакомимся с новой возможностью JavaScript, которая позволяет создавать динамические свойства. Мы узнаем, что такое Getter и мемоизация, и чем последнее отличается от кеширования.</p>
1 <p>В этом уроке мы познакомимся с новой возможностью JavaScript, которая позволяет создавать динамические свойства. Мы узнаем, что такое Getter и мемоизация, и чем последнее отличается от кеширования.</p>
2 <h2>Свойства</h2>
2 <h2>Свойства</h2>
3 <p>Начнем с примера, в котором мы видим уже привычные нам конструкции:</p>
3 <p>Начнем с примера, в котором мы видим уже привычные нам конструкции:</p>
4 <p>Предположим, нам нужно узнать количество элементов в результате, который мы получили.</p>
4 <p>Предположим, нам нужно узнать количество элементов в результате, который мы получили.</p>
5 <p>Часто вывод формируется в зависимости от размера выходного массива. Для этого в JavaScript обычно используется свойство length. Но здесь возникает вопрос: откуда оно возьмется там и как оно заполнится.</p>
5 <p>Часто вывод формируется в зависимости от размера выходного массива. Для этого в JavaScript обычно используется свойство length. Но здесь возникает вопрос: откуда оно возьмется там и как оно заполнится.</p>
6 <h2>Обновление во время вычисления</h2>
6 <h2>Обновление во время вычисления</h2>
7 <p>Например, мы можем делать обновление свойства length во время вычисления:</p>
7 <p>Например, мы можем делать обновление свойства length во время вычисления:</p>
8 <p>Как мы помним, у нас нет вычисленной коллекции. Есть только отложенные вычисления, так как мы используем Lazy Evaluation. Поэтому в toArray, который делает эти вычисления, можно установить length после того, как мы получаем результат.</p>
8 <p>Как мы помним, у нас нет вычисленной коллекции. Есть только отложенные вычисления, так как мы используем Lazy Evaluation. Поэтому в toArray, который делает эти вычисления, можно установить length после того, как мы получаем результат.</p>
9 <p>Но этот способ не работает. В таком случае это будет значить, что length можно использовать только после вызова toArray. То есть до этого момента мы его не можем использовать. А поскольку мы уже вызвали toArray, то length внутри уже не нужен. В этой ситуации длину можно посмотреть у самого массива. При этом до вызова toArray мы не можем узнать длину. Придется произвести вычисления.</p>
9 <p>Но этот способ не работает. В таком случае это будет значить, что length можно использовать только после вызова toArray. То есть до этого момента мы его не можем использовать. А поскольку мы уже вызвали toArray, то length внутри уже не нужен. В этой ситуации длину можно посмотреть у самого массива. При этом до вызова toArray мы не можем узнать длину. Придется произвести вычисления.</p>
10 <p>В итоге нужен другой способ, который позволяет реализовывать свойства. В JavaScript он называется Getter.</p>
10 <p>В итоге нужен другой способ, который позволяет реализовывать свойства. В JavaScript он называется Getter.</p>
11 <h2>Getter</h2>
11 <h2>Getter</h2>
12 <p>Чтобы использовать Getter, нужно в классе описать функцию length, при этом слева приписать ключевое слово get с пробелом:</p>
12 <p>Чтобы использовать Getter, нужно в классе описать функцию length, при этом слева приписать ключевое слово get с пробелом:</p>
13 <p>В этом случае эта конструкция превращается в динамическое свойство. То есть снаружи оно выглядит как свойство - вызываем coll.length, но в реальности вызывается функция get. При этом она не имеет права принимать параметры. То есть это просто свойство, а не вызов функции.</p>
13 <p>В этом случае эта конструкция превращается в динамическое свойство. То есть снаружи оно выглядит как свойство - вызываем coll.length, но в реальности вызывается функция get. При этом она не имеет права принимать параметры. То есть это просто свойство, а не вызов функции.</p>
14 <p>Далее это свойство работает следующим образом:</p>
14 <p>Далее это свойство работает следующим образом:</p>
15 <p>Здесь мы делаем выборку и result.length. После этого будет вызвана функция, внутри которой мы и должны будем произвести необходимые нам вычисления. Например, мы можем вызвать toArray. Это зависит от конкретной реализации. Но нам в любом случае придется выполнить эти вычисления, когда вызывается это свойство.</p>
15 <p>Здесь мы делаем выборку и result.length. После этого будет вызвана функция, внутри которой мы и должны будем произвести необходимые нам вычисления. Например, мы можем вызвать toArray. Это зависит от конкретной реализации. Но нам в любом случае придется выполнить эти вычисления, когда вызывается это свойство.</p>
16 <p>Снаружи это будет прозрачно. То есть нам не надо знать, когда выполняются эти вычисления, а также порядок вызовов.</p>
16 <p>Снаружи это будет прозрачно. То есть нам не надо знать, когда выполняются эти вычисления, а также порядок вызовов.</p>
17 <p>Теперь мы можем вызывать все свойства в любом порядке и функцию toArray. При этом всё будет работать, как мы ожидаем. А вычисление будет происходить в тот момент, когда это нужно. Например, при подсчете количества элементов.</p>
17 <p>Теперь мы можем вызывать все свойства в любом порядке и функцию toArray. При этом всё будет работать, как мы ожидаем. А вычисление будет происходить в тот момент, когда это нужно. Например, при подсчете количества элементов.</p>
18 <h2>Повторные вычисления</h2>
18 <h2>Повторные вычисления</h2>
19 <p>При этом есть небольшая проблема, которая может стать заметной при большом количестве вычислений.</p>
19 <p>При этом есть небольшая проблема, которая может стать заметной при большом количестве вычислений.</p>
20 <p>Когда мы вызываем length или toArray, вычисления производятся каждый раз заново. Это будет замедлять программу. Причем будет видно, что вычислять не нужно, потому что коллекция уже конкретная, и мы знаем, какие операции производятся. Новых операций там не появится, потому что у нас неизменяемая коллекция - каждый раз мы получаем новую.</p>
20 <p>Когда мы вызываем length или toArray, вычисления производятся каждый раз заново. Это будет замедлять программу. Причем будет видно, что вычислять не нужно, потому что коллекция уже конкретная, и мы знаем, какие операции производятся. Новых операций там не появится, потому что у нас неизменяемая коллекция - каждый раз мы получаем новую.</p>
21 <p>В этом случае нужно сделать так, чтобы не производить повторных вычислений. Для этого существует специальная техника - мемоизация.</p>
21 <p>В этом случае нужно сделать так, чтобы не производить повторных вычислений. Для этого существует специальная техника - мемоизация.</p>
22 <h2>Мемоизация</h2>
22 <h2>Мемоизация</h2>
23 <p><strong>Мемоизация</strong>- это сохранение результата выполнения функций для предотвращения повторных вычислений. Этот паттерн описывается таким шаблоном:</p>
23 <p><strong>Мемоизация</strong>- это сохранение результата выполнения функций для предотвращения повторных вычислений. Этот паттерн описывается таким шаблоном:</p>
24 <p>В этом шаблоне мы проверяем внутри нашей функции или динамического свойства, не установлено ли какое-то свойство. Если нет, то мы его устанавливаем и записываем туда наше вычисление. После этого мы возвращаем значение этого свойства. В нашем случае мы назвали свойство memo</p>
24 <p>В этом шаблоне мы проверяем внутри нашей функции или динамического свойства, не установлено ли какое-то свойство. Если нет, то мы его устанавливаем и записываем туда наше вычисление. После этого мы возвращаем значение этого свойства. В нашем случае мы назвали свойство memo</p>
25 <p>В первый раз, когда мы делаем вызов, у нас производится вычисление и заполнение this.memo. Когда мы делаем вызов второй раз, то нам напрямую возвращается memo.</p>
25 <p>В первый раз, когда мы делаем вызов, у нас производится вычисление и заполнение this.memo. Когда мы делаем вызов второй раз, то нам напрямую возвращается memo.</p>
26 <p>Когда мы используем нашу библиотеку, это работает идеально. Дело в том, что immutable-коллекции и memo не могут устареть. А когда вызываем новые методы или свойства на нашем объекте, они будут порождать новый объект, который уже содержит копии данных. Поэтому там будет свой memo.</p>
26 <p>Когда мы используем нашу библиотеку, это работает идеально. Дело в том, что immutable-коллекции и memo не могут устареть. А когда вызываем новые методы или свойства на нашем объекте, они будут порождать новый объект, который уже содержит копии данных. Поэтому там будет свой memo.</p>
27 <p>Здесь снова проявляется преимущество immutable-подхода, который при отсутствии изменяемости не заставляет нас синхронизировать состояние.</p>
27 <p>Здесь снова проявляется преимущество immutable-подхода, который при отсутствии изменяемости не заставляет нас синхронизировать состояние.</p>
28 <p>Мемоизация всегда выполняется по одному шаблону. Создается переменная, которая заполняется, если она была пустая, а затем используется для отдачи значения без выполнения вычислений. Ее можно сделать с помощью одних функций - на замыканиях. Это значит, что придется генерировать функцию.</p>
28 <p>Мемоизация всегда выполняется по одному шаблону. Создается переменная, которая заполняется, если она была пустая, а затем используется для отдачи значения без выполнения вычислений. Ее можно сделать с помощью одних функций - на замыканиях. Это значит, что придется генерировать функцию.</p>
29 <p>Ниже пример с мемоизацией факториала:</p>
29 <p>Ниже пример с мемоизацией факториала:</p>
30 <p>Мемоизация отличается от кеширования отсутствием устаревания. Данные внутри memo устареть не могут по определению. Если наша функция чистая, то для одного входа она всегда дает один и тот же выход. При этом она мемоизируют только чистые функции, иначе всегда есть риск сохранить результат, который впоследствии может поменяться.</p>
30 <p>Мемоизация отличается от кеширования отсутствием устаревания. Данные внутри memo устареть не могут по определению. Если наша функция чистая, то для одного входа она всегда дает один и тот же выход. При этом она мемоизируют только чистые функции, иначе всегда есть риск сохранить результат, который впоследствии может поменяться.</p>
31 <h2>Выводы</h2>
31 <h2>Выводы</h2>
32 <p>В этом уроке мы познакомились с новой возможностью JavaScript, которая позволяет создавать динамические свойства. Еще мы узнали, что такое Getter. Он позволяет реализовывать свойства. А также узнали, что мемоизация - это сохранение результата выполнения функций для предотвращения повторных вычислений. При этом она отличается от кеширования тем, что отсутствует устаревание.</p>
32 <p>В этом уроке мы познакомились с новой возможностью JavaScript, которая позволяет создавать динамические свойства. Еще мы узнали, что такое Getter. Он позволяет реализовывать свойства. А также узнали, что мемоизация - это сохранение результата выполнения функций для предотвращения повторных вычислений. При этом она отличается от кеширования тем, что отсутствует устаревание.</p>