Share This
Связаться со мной
Крути в низ
Categories
//33 приема оптимизации JavaScript, которые вы должны знать в 2021 году

33 приема оптимизации JavaScript, которые вы должны знать в 2021 году

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

33 priema optimizacii javascript kotorye vy dolzhny znat v 2021 godu b07d25b - 33 приема оптимизации JavaScript, которые вы должны знать в 2021 году

Хороший разработчик всегда должен учиться чему-то новому и совершенствовать навыки, которые у него уже есть. Поэтому сейчас мы будем учиться писать более чистый и понятный код!

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

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

Объявление переменных и присваивание значений

1. Объявление нескольких переменных

Переменные можно объявлять через запятую, при этом не требуется повторно использовать инструкцию let:

         // Длинно let test1 = 1; let test2 = 2;  // Коротко let test1 = 1, test2 = 2;     

Такой подход особенно удобен при объявлении нескольких переменных без моментального присваивания значений:

         let test1, test2, test3, test4, test5;     

2. Арифметические операции

Присваивание в JavaScript легко совмещается с арифметическими операторами:

         // Длинно test1 = test1 + 1; test2 = test2 - 1; test3 = test3 * 20;  // Коротко test1++; test2--; test3 *= 20;     

3. Присваивание значений нескольким переменным

Если требуется присвоить значения сразу нескольким переменным, мы обычно делаем так:

         let test1, test2, test3; test1 = 1; test2 = 2; test3 = 3;     

Но всю эту логику можно свести в одну строку, если использовать деструктуризацию:

         let [test1, test2, test3] = [1, 2, 3];     

Синтаксис деструктуризации работает не только с массивами, но и с обычными объектами:

         const data = {   test1: 1,   test2: 2,   test3: 3 };  // Длинно const test1 = data.test1; const test2 = data.test2; const test2 = data.test3;  //shorthand const { test1, test2, test3 } = data;     

4. Присваивание со значением по умолчанию

Иногда требуется положить в переменную какое-то значение, предварительно убедившись, что это значение существует и не является falsy (null, undefined, пустая строка, 0).

Для этого можно использовать обычную конструкцию if-else:

         let test2; if (test1) {   test2 = test1; } else {   test2 = 'default value'; }     

Можно написать короче.

Взять, например, логический оператор ||. Если в переменной находится truthy-значение, то будет возвращено оно, иначе – дефолтное значение с правой стороны оператора.

         let test 2 = test1 || 'default value';     

Еще более современный подход – nullish coalescing operator – ??. Однако он проверяет не на все falsy-значения, а только на null и undefined. В остальном логика такая же, как у оператора ||.

         const test = null ?? 'default value'; console.log(test); // 'default value'  const test1 = 0 ?? 2; console.log(test1); // 0     

Условия

5. Автоматическая проверка на truthy и falsy значения

Иногда требуется проверить, есть ли в переменной какое-либо значение. При этом важно учитывать и null, и undefined, и другие falsy-значения (NaN, пустая строка, 0).

         if (test1 !== null || test1 !== undefined || test1 !== '') {     // logic }     

К счастью, нет необходимости проводить все проверки по отдельности, тем более, что при этом легко что-нибудь упустить. Например, 0, который в ряде ситуаций тоже может расцениваться как «пустое» значение.

Можно просто положиться на JavaScript и его динамическую конверсию типов.

         if (test1) {   // logic }     

Оператор if самостоятельно приведет переменную к логическому значению и осуществит проверку.

6. Логические операторы вместо if

В пункте Присваивание с дефолтным значением мы уже видели, как логический оператор (||) может заменить конструкцию if.

Оператор && также способен на многое:

         // Длинно if (test1) {  callMethod();  }  // Коротко  test1 && callMethod();     

Если test1 является falsy-значением, то до инструкции callMethod() выполнение не дойдет.

7. Тернарный оператор вместо if

Зачастую простые конструкции if-else можно заменить еще более простым тернарным оператором:

         // Длинно let test: boolean; if (x > 100) {     test = true; } else {     test = false; }  // Коротко let test = (x > 10) ? true : false;  // Еще короче let test = x > 10;     

Таким образом можно преобразовать и более сложные условия, даже вложенные:

         let x = 300, test2 = (x > 100)          ? 'greater 100'          : (x < 50)             ? 'less 50'             : 'between 50 and 100';  console.log(test2); // "greater than 100"     

Важно: чем сложнее условие, тем запутаннее выглядит тернарный оператор и предпочтительнее – стандартная конструкция if-else.

8. Проверка на значение из набора

Если требуется выполнить какие-то действия, когда x равен одному из нескольких значений, первым порывом может быть использование обычных сравнений:

         if (x === 'abc' || x === 'def' || x === 'ghi' || x ==='jkl') {     //logic }     

Но чем больше таких «подходящих» значений, тем сильнее разрастается условие и тем проще сделать ошибку. Проще поместить все эти значения в массив и использовать метод Array.prototype.includes для проверки:

         if (['abc', 'def', 'ghi', 'jkl'].includes(x)) {    //logic }     

Важно: метод includes использует строгое сравнение с учетом типа аргумента.

9. Коллекция вместо switch

Вместо того, чтобы рассматривать каждый случай внутри инструкции switch, можно добавить их все в объект:

         // Длинно switch (data) {   case 1:     test1();   break;    case 2:     test2();   break;    case 3:     test();    break;   // ... }  // Коротко const collection = {   1: test1,   2: test2,   3: test };  collection[data] && collection[data]();     

Циклы

10. Короткий синтаксис for

Всем знакомый старый-добрый цикл for имеет довольно громоздкую структуру:

         for (var i = 0; i < testData.length; i++) {   // i - индекс элемента   // доступ к значению элемента i   var item = testData[i]; }     

К счастью, в последних версиях языка появилось несколько более удобных альтернатив:

         for (let i in testData) {   // i - индекс элемента   // доступ к элементу i   var item = testData[i]; }  for (let i of testData) {   // i - значение элемента }     

Кроме того, есть замечательный функциональный метод forEach:

         function testData(element, index, array) {   console.log('test[' + index + '] = ' + element); }  [11, 24, 32].forEach(testData); // logs: test[0] = 11, test[1] = 24, test[2] = 32     

Такой код выглядит очень понятно, но у него есть некоторые ограничения. Например, перебор массива методом forEach нельзя прервать с помощью инструкции break.

Числа

11. Конверсия строки в число

Одиночный оператор сложения (+) неявно приводит тип полученного аргумента к числу.

         // Длинно let test1 = parseInt('123');  let test2 = parseFloat('12.3');   // Коротко let test1 = +'123';  let test2 = +'12.3';     

12. Экспоненциальная запись

Избавиться от большого количества нулей поможет экспоненциальная запись:

         // Длинно for (var i = 0; i < 10000; i++) { ... }  // Коротко for (var i = 0; i < 1e4; i++) { ... }     

13. Возведение в степень

Если вы вдруг до сих пор не знали, то пришло время узнать – в JavaScript есть специальный оператор для возведения в степень:

         // Длинно Math.pow(2,3);   // Коротко 2**3      

14. Округление

Двойное побитовое отрицание для 32-битных целых чисел дает такой же эффект, как Math.floor (округляет вниз):

         // Длинно Math.floor(1.9) === 1 // true  // Коротко ~~1.9 === 1 // true     

Строки

15. Получение символа из строки

Строка – это по сути массив символов, поэтому к каждому из них можно обратиться по индексу:

         let str = 'abc';  // Длинно str.charAt(2); // c  // Коротко str[2]; // c     

16. Повторение строки

Чтобы соединить несколько одинаковых строк в одну, существует несколько подходов. Самый очевидный – и самый громоздкий – использовать цикл:

         let test = '';  for(let i = 0; i < 5; i ++) {    test += 'test ';  }      

Но есть и более логичный – встроенный метод String.prototype.repeat():

         let test = 'test '.repeat(5);      

Бонус для любителей нестандартных решений:

         let test = Array(6).join('test ');      

17. Конкатенация

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

         // Длинно const welcome = 'Hi ' + test1 + ' ' + test2 + '.'  // Коротко const welcome = `Hi ${test1} ${test2}`;     

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

         // Длинно const data = 'abc abc abc abc abc abcnt'     + 'test test,test test test testnt'  // Коротко const data = `abc abc abc abc abc abc          test test,test test test test`     

Массивы

18. Наличие элемента

Обычно чтобы проверить, присутствует ли элемент в массиве, мы используем метод indexOf и сравниваем найденный индекс с -1.

         if (arr.indexOf(item) > -1) {    // элемент в массиве есть }  if(arr.indexOf(item) === -1) {    // элемента в массиве нет }     

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

         if(~arr.indexOf(item)) {    // элемент в массиве есть }  if(!~arr.indexOf(item)) {    // элемента в массиве нет }     

Оператор ~ возвращает 0 только для значения -1. Для всех других значений будет возвращено число, отличное от нуля, то есть truthy-значение.

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

Не стоит забывать и про удобнейший метод Array.prototype.includes:

         if (arr.includes(item)) {    // элемент в массиве есть }      

19. Поиск элемента

Метод Array.prototype.find – это удобная функциональная замена простому перебору элементов массива с помощью цикла for:

         // Длинно function findElementByName(arr, name) {     for (let i = 0; i < arr.length; ++i) {         if (arr[i].name === name) {             return data[i];         }     } }  // Коротко function findElementByName(arr, name) {   return arr.find(el => el.name === name); }  const data = [   { name: 'John' },   { name: 'Jane' }, ] findElementByName('Jane'); // { name: 'Jane' }     

20. Spread-синтаксис

Spread-синтаксис был введен в язык специально для облегчения множества операций со сложными структурами данных.

Например, для конкатенации массивов:

         // Стандартный подход const data = [1, 2, 3]; const test = [4, 5, 6].concat(data);  // Spread-синтаксис const data = [1, 2, 3]; const test = [4, 5, 6, ...data];  console.log(test); // [ 4, 5, 6, 1, 2, 3]     

Или клонирования массива:

         // Стандартный подход const test1 = [1, 2, 3]; const test2 = test1.slice()  // Spread-синтаксис const test1 = [1, 2, 3]; const test2 = [...test1];     

21. Минимальное и максимальное значение

Методы Math.max и Math.min могут принимать любое количество аргументов. Чтобы передать им массив, можно использовать метод Function.prototype.apply:

         const arr = [1,2,3]; Math.max.apply(null, arr); // 3 Math.min.apply(null, arr); // 1     

А можно воспользоваться деструктуризацией:

         Math.max(…arr); // 3 Math.min(…arr); // 1     

Объекты

22. Присваивание значений

Если имя свойства совпадает с именем переменной, в которой хранится значение, дублировать его необязательно:

         let test1 = 'a';  let test2 = 'b';  // Длинно let obj = {test1: test1, test2: test2};   // Коротко let obj = {test1, test2};     

23. Перебор ключей и значений

В современном JavaScript есть сразу 3 метода для перебора объектов: Object.keys(), Object.values() и Object.entries(). Каждый из них возвращает массив – ключей, значений или сразу и того, и другого.

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

         const data = { test1: 'abc', test2: 'cde', test3: 'efg' };  Object.keys(data); // ['test1', 'test2', 'test3']; Object.values(data); // ['abc', 'cde', 'efg']; Object.entries(data); // [['test1','abc'],['test2','cde'],['test3','efg']]      

Функции

24. Параметры по умолчанию

Современный стандарт JavaScript позволяет задать дефолтные значения параметров прямо в сигнатуре функции. Теперь не нужно проверять это отдельно:

         // Длинно function add(test1, test2) {   if (test1 === undefined)     test1 = 1;   if (test2 === undefined)     test2 = 2;   return test1 + test2; }  // Коротко function add(test1 = 1, test2 = 2) {   return test1 + test2; }   add(5, 10); // 15 add(5); // 7 add(); // 3  add(5, null); // 5       

Важно: дефолтное значение устанавливается только в том случае, если указанный параметр не передан или равен undefined. На null это не распространяется.

25. Операции в return

В операторе return можно производить разнообразные вычисления, что позволит сэкономить пару строк кода.

         // Длинно function check(test) {   if (!test) {     return 'default value';   } else {     return test;   } }  // Коротко  function check(test) {   return test || 'default value'; }     

26. Стрелочные функции

В ряде случаев стрелочные функции могут стать более короткой и удобной альтернативой обычным. Например, их очень удобно использовать для коллбэков:

         // Длинно [1, 2, 3].map(function(i) {    return i * 2; });  // Коротко [1, 2, 3].map(i => i * 2);     

Стрелочные функции позволяют возвращать значение неявно, без использования оператора return:

         // Длинно function calculate(diameter) {   return Math.PI * diameter }  // Коротко calculate = diameter => Math.PI * diameter;     

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

***

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

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

***

Современные возможности 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: проверка наличия значения в массиве.
  • Возведение в степень.

  • 16 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close