Работа мечты в один клик 💼

💭Мечтаешь работать в Сбере, но не хочешь проходить десять кругов HR-собеседований? Теперь это проще, чем когда-либо!
💡AI-интервью за 15 минут – и ты уже на шаг ближе к своей новой работе.
Как получить оффер? 📌 Зарегистрируйся 📌 Пройди AI-интервью 📌 Получи обратную связь сразу же!
HR больше не тянут время – рекрутеры свяжутся с тобой в течение двух дней! 🚀
Реклама. ПАО СБЕРБАНК, ИНН 7707083893. Erid 2VtzquscAwp
Хороший разработчик всегда должен учиться чему-то новому и совершенствовать навыки, которые у него уже есть. Поэтому сейчас мы будем учиться писать более чистый и понятный код!
Ниже вы найдете большую подборку кратких вариантов широко распространенных конструкций. Во многих случаях они не только позволяют кодить быстрее, но и выглядят понятнее и чище, чем длинные аналоги.
В конце статьи приведен список современных возможностей языка, разделенный на версии.
Объявление переменных и присваивание значений
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 abc\n\t'
+ 'test test,test test test test\n\t'
// Коротко
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 arr[i];
}
}
}
// Коротко
function findElementByName(arr, name) {
return arr.find(el => el.name === name);
}
const data = [
{ name: 'John' },
{ name: 'Jane' },
]
findElementByName(data, '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: проверка наличия значения в массиве.
- Возведение в степень.
Комментарии