☕ 33 приема оптимизации JavaScript, которые вы должны знать в 2021 году
Предлагаем вашему вниманию подборку полезных техник и хитростей для оптимизации кода на JavaScript. Она будет интересна не только изучающим язык новичкам, но и опытным программистам.
Хороший разработчик всегда должен учиться чему-то новому и совершенствовать навыки, которые у него уже есть. Поэтому сейчас мы будем учиться писать более чистый и понятный код!
Ниже вы найдете большую подборку кратких вариантов широко распространенных конструкций. Во многих случаях они не только позволяют кодить быстрее, но и выглядят понятнее и чище, чем длинные аналоги.
В конце статьи приведен список современных возможностей языка, разделенный на версии.
Объявление переменных и присваивание значений
1. Объявление нескольких переменных
Переменные можно объявлять через запятую, при этом не требуется повторно использовать инструкцию let
:
Такой подход особенно удобен при объявлении нескольких переменных без моментального присваивания значений:
2. Арифметические операции
Присваивание в JavaScript легко совмещается с арифметическими операторами:
3. Присваивание значений нескольким переменным
Если требуется присвоить значения сразу нескольким переменным, мы обычно делаем так:
Но всю эту логику можно свести в одну строку, если использовать деструктуризацию:
Синтаксис деструктуризации работает не только с массивами, но и с обычными объектами:
4. Присваивание со значением по умолчанию
Иногда требуется положить в переменную какое-то значение, предварительно убедившись, что это значение существует и не является falsy (null
, undefined
, пустая строка, 0).
Для этого можно использовать обычную конструкцию if-else
:
Можно написать короче.
Взять, например, логический оператор ||
. Если в переменной находится truthy-значение, то будет возвращено оно, иначе – дефолтное значение с правой стороны оператора.
Еще более современный подход – nullish coalescing operator – ??
. Однако он проверяет не на все falsy-значения, а только на null
и undefined
. В остальном логика такая же, как у оператора ||
.
Условия
5. Автоматическая проверка на truthy и falsy значения
Иногда требуется проверить, есть ли в переменной какое-либо значение. При этом важно учитывать и null
, и undefined
, и другие falsy-значения (NaN
, пустая строка, 0).
К счастью, нет необходимости проводить все проверки по отдельности, тем более, что при этом легко что-нибудь упустить. Например, 0, который в ряде ситуаций тоже может расцениваться как "пустое" значение.
Можно просто положиться на JavaScript и его динамическую конверсию типов.
Оператор if
самостоятельно приведет переменную к логическому значению и осуществит проверку.
6. Логические операторы вместо if
В пункте Присваивание с дефолтным значением мы уже видели, как логический оператор (||
) может заменить конструкцию if
.
Оператор &&
также способен на многое:
Если test1
является falsy-значением, то до инструкции callMethod()
выполнение не дойдет.
7. Тернарный оператор вместо if
Зачастую простые конструкции if-else
можно заменить еще более простым тернарным оператором:
Таким образом можно преобразовать и более сложные условия, даже вложенные:
Важно: чем сложнее условие, тем запутаннее выглядит тернарный оператор и предпочтительнее – стандартная конструкция if-else
.
8. Проверка на значение из набора
Если требуется выполнить какие-то действия, когда x
равен одному из нескольких значений, первым порывом может быть использование обычных сравнений:
Но чем больше таких "подходящих" значений, тем сильнее разрастается условие и тем проще сделать ошибку. Проще поместить все эти значения в массив и использовать метод Array.prototype.includes
для проверки:
Важно: метод includes
использует строгое сравнение с учетом типа аргумента.
9. Коллекция вместо switch
Вместо того, чтобы рассматривать каждый случай внутри инструкции switch
, можно добавить их все в объект:
Циклы
10. Короткий синтаксис for
Всем знакомый старый-добрый цикл for
имеет довольно громоздкую структуру:
К счастью, в последних версиях языка появилось несколько более удобных альтернатив:
Кроме того, есть замечательный функциональный метод forEach
:
Такой код выглядит очень понятно, но у него есть некоторые ограничения. Например, перебор массива методом forEach
нельзя прервать с помощью инструкции break
.
Числа
11. Конверсия строки в число
Одиночный оператор сложения (+
) неявно приводит тип полученного аргумента к числу.
12. Экспоненциальная запись
Избавиться от большого количества нулей поможет экспоненциальная запись:
13. Возведение в степень
Если вы вдруг до сих пор не знали, то пришло время узнать – в JavaScript есть специальный оператор для возведения в степень:
14. Округление
Двойное побитовое отрицание для 32-битных целых чисел дает такой же эффект, как Math.floor (округляет вниз):
Строки
15. Получение символа из строки
Строка – это по сути массив символов, поэтому к каждому из них можно обратиться по индексу:
16. Повторение строки
Чтобы соединить несколько одинаковых строк в одну, существует несколько подходов. Самый очевидный – и самый громоздкий – использовать цикл:
Но есть и более логичный – встроенный метод String.prototype.repeat()
:
Бонус для любителей нестандартных решений:
17. Конкатенация
Составление одной строки из нескольких фрагментов – ужасная головная боль. Нас спасут шаблонные литералы:
Они не только позволяют избежать многочисленных конкатенаций, но и поддерживают многострочность.
Массивы
18. Наличие элемента
Обычно чтобы проверить, присутствует ли элемент в массиве, мы используем метод indexOf
и сравниваем найденный индекс с -1
.
Но можно использовать другой подход, без сравнения индексов – побитовый оператор ~
.
Оператор ~
возвращает 0
только для значения -1
. Для всех других значений будет возвращено число, отличное от нуля, то есть truthy-значение.
Важно: не жертвуйте читаемостью кода в угоду краткости. Используйте побитовые операторы только в том случае, если все члены вашей команды умеют с ними работать.
Не стоит забывать и про удобнейший метод Array.prototype.includes
:
19. Поиск элемента
Метод Array.prototype.find
– это удобная функциональная замена простому перебору элементов массива с помощью цикла for
:
20. Spread-синтаксис
Spread-синтаксис был введен в язык специально для облегчения множества операций со сложными структурами данных.
Например, для конкатенации массивов:
Или клонирования массива:
21. Минимальное и максимальное значение
Методы Math.max
и Math.min
могут принимать любое количество аргументов. Чтобы передать им массив, можно использовать метод Function.prototype.apply
:
А можно воспользоваться деструктуризацией:
Объекты
22. Присваивание значений
Если имя свойства совпадает с именем переменной, в которой хранится значение, дублировать его необязательно:
23. Перебор ключей и значений
В современном JavaScript есть сразу 3 метода для перебора объектов: Object.keys()
, Object.values()
и Object.entries()
. Каждый из них возвращает массив – ключей, значений или сразу и того, и другого.
Трансформировав структуру данных таким образом, мы можем легко проитерировать как обычный массив любым удобным способом.
Функции
24. Параметры по умолчанию
Современный стандарт JavaScript позволяет задать дефолтные значения параметров прямо в сигнатуре функции. Теперь не нужно проверять это отдельно:
Важно: дефолтное значение устанавливается только в том случае, если указанный параметр не передан или равен undefined
. На null
это не распространяется.
25. Операции в return
В операторе return
можно производить разнообразные вычисления, что позволит сэкономить пару строк кода.
26. Стрелочные функции
В ряде случаев стрелочные функции могут стать более короткой и удобной альтернативой обычным. Например, их очень удобно использовать для коллбэков:
Стрелочные функции позволяют возвращать значение неявно, без использования оператора return:
Важно: не злоупотребляйте стрелочными функциями из-за их краткости, используйте их по прямому назначению, например, для сохранения контекста выполнения.
Сокращения в программировании похожи на лекарства. В небольших дозах они полезны и делают вашу жизнь лучше, но при передозировке возможно отравление и другие неприятные побочные эффекты.
Поэтому соблюдайте меру и используйте сокращения только тогда, когда они действительно нужны и не нарушают читаемость кода.
Современные возможности JavaScript
ES2021/ES12
- String.prototype.replaceAll: заменяет в строке все совпадения с шаблоном.
- Promise.any: принимает коллекцию промисов и ожидает выполнения любого из них.
- WeakRef: содержит слабую ссылку на другой объект, которая не препятствует сборке мусора.
- FinalizationRegistry: вызывает коллбэк при уничтожении объекта сборщиком мусора.
- Приватные поля классов.
- Разделитель разрядов для чисел.
- Intl.ListFormat: форматирование списков.
- Intl.DateTimeFormat: форматирование даты и времени.
ES2020/ES11
- BigInt: способ представления больших чисел.
- import(): динамический импорт модулей.
- Nullish coalescing: удобная проверка на null и undefined.
- globalThis: глобальный объект.
- Promise.allSettled: принимает коллекцию промисов и ожидает их выполнения, возвращает массив с результатами.
- Опциональные последовательности: позволяет обращаться к свойству на любом уровне вложенности без проверки на наличие каждого свойства в цепочке.
- String.prototype.matchAll: поиск совпадений в строке.
- Именованный экспорт из модулей.
- import.meta: мета-данные модулей.
ES2019/ES10
- Array.flat: объединение массивов с указанием глубины.
- Array.flatmap: создание массива с коллбэком для каждого элемента.
- Object.fromEntries: преобразует список пар ключ-значение в объект.
- String.prototype.trimStart, String.prototype.trimEnd: удаление пробелов из строки.
- Function.prototype.toString: преобразование функций к строке.
- Symbol.prototype.description: возможность добавить описание для символов.
ES2018/ES9
- Асинхронные циклы.
- Promise.finally: вызывается при любом исходе промиса.
- Rest/Spread-синтаксис.
- Именованные группы захвата в регулярных выражениях.
- Флаг
s
в регулярных выражениях: режим dotall – точка может соответствовать символу переноса строки.
ES2017/ES8
- Object.entries: возвращает пары ключей и значений объекта.
- Object.values: возвращает массив значений объекта.
- String.prototype.padStart, String.prototype.padEnd: заполнение строки пробелами до нужной длины.
- Object.getOwnPropertyDescriptors: возвращает все собственные дескрипторы свойств объекта.
- Асинхронные функции.
ES2016/ES7
- Array.prototype.includes: проверка наличия значения в массиве.
- Возведение в степень.