[JS] JavaScript-трюки

Andrew Samchuk
Nov 1 · 5 min read

Фильтрация уникальных значений

В стандарте ES6 появился новый тип объектов Set. Скомбинировав его со спред-оператором (...), можно легко получить из старого массива новый, в котором будут только уникальные значения.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 5]

Кэширование длины в цикле

Когда мы изучаем программирование на JavaScript, то во всех туториалах встречаем вот такую стандартную конструкцию цикла for:

for (let i = 0; i < array.length; i++){
console.log(i);
}

На каждой итерации цикла длина массива array будет высчитываться заново. Иногда это полезно, но в большинстве случаев эффективнее будет ее кэшировать после первого расчета. Для этого создадим переменную length. Это можно сделать в первой части условия, вместе с определением счетчика цикла:

for (let i = 0, length = array.length; i < length; i++){
console.log(i);
}

Укорачивание

Чтобы удалить несколько значений из конца массива, необязательно пользоваться методами slice(), splice() или pop(). Просто переопределите свойство length:

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
array.length = 4;
console.log(array); // [0, 1, 2, 3]

Это работает только с массивами, а вот с Set, например, трюк не пройдет.

Получение элементов с конца

В метод slice() можно передать отрицательный параметр, тогда отсчет элементов начнется с конца массива.

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

console.log(array.slice(-1)); // [9]
console.log(array.slice(-2)); // [8, 9]
console.log(array.slice(-3)); // [7, 8, 9]

Преобразование в строку

Быстро преобразовать число в строку можно с помощью оператора +, просто сконкатенировав его с пустой строкой.

const val = 1 + "";
console.log(val); // "1"
console.log(typeof val); // "string"

Преобразование в число

Для обратного преобразования снова пригодится вездесущий +.

let int = "15";
int = +int;
console.log(int); // 15
console.log(typeof int); // "number"

Булевы значения тоже можно превратить в числа:

console.log(+true); // 1
console.log(+false); // 0

В некоторых контекстах + работает как оператор конкатенации, поэтому нужна альтернатива.

Если вы работаете с целыми числами (без дробной части), обратите внимание на оператор ~ (тильда), известный также как "побитовое НЕ". Каждый бит операнда он заменяет на противоположный. Выражение ~n эквивалентно выражению -n-1, например, ~15 = -16.

Если этот оператор получает строку, то преобразует ее в число: ~"15" = -16.

Применение побитового отрицания к результату другой операции побитового отрицания успешно отменяет эффект, то есть возвращает исходное число. Действительно, -(-n-1)-1 = n. Другими словами, ~-16 = 15.

const int = ~~"15";
console.log(int); // 15
console.log(typeof int); // "number"

При желании можно использовать побитовое отрицание и с булевыми значениями, хотя вряд ли вы найдете для этого множество применений:

~true = -2
~false = -1

Преобразование в булев тип

Язык программирования JavaScript может рассматривать любое значение с логической точки зрения. Все, что преобразуется в false, называется "falsy" (ложное). Это число 0, пустая строка "", null, undefined, NaN и, конечно же, false. Все остальные значения – истинные ("truthy").

Оператор логического отрицания ! умеет работать со значениями любого типа. Он конвертирует любое falsy значение в true, а любое truthy – в false. Таким образом, на выходе всегда получается булево значение. Вот JavaScript примеры:

const true  = !0;
const false = !1;
const alsoFalse = !!0;
console.log(true); // true
console.log(typeof true); // "boolean"

Быстрое возведение в степень

Стандарт ES7 предложил нам новый оператор ** для возведения числа в степень.

2 ** 3; // 8

Это явно короче, чем Math.pow(2, 3).

Вероятно, вы ожидали увидеть здесь более привычный символ ^. Но в JavaScript он уже занят – это побитовое исключающее ИЛИ (XOR).

Быстрое округление

Если нужно сделать дробное число целым, вы используете Math.floor(), Math.ceil() или Math.round() – в зависимости от нужного эффекта. Но есть и более быстрый путь – побитовое ИЛИ.

console.log(23.9 | 0); // 23
console.log(-23.9 | 0); // -23

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

Такого же эффекта можно добиться с помощью уже знакомого нам двойного оператора побитового отрицания (~~).

Удаление разрядов

С помощью побитового ИЛИ можно удалять из числа разряды, не путаясь с дробной частью:

console.log(1553 / 10   | 0) // 155
console.log(1553 / 100 | 0) // 15
console.log(1553 / 1000 | 0) // 1

Короткие вычисления в одну цепочку

Тернарный оператор предоставляет простой и быстрый способ выполнения простых — а иногда и не очень простых — условных вычислений.

x > 100 ? 'Больше 100' : 'Меньше 100';
x > 100 ? (x > 200 ? 'Больше 200' : 'Между 100 и 200') : 'Меньше 100';

Но иногда даже тернарный оператор чересчур сложен. Если ваш программистский дух жаждет краткости, обратите внимание на логические операторы && и ||.

Оператор && вернет первое же "falsy" значение в цепочке. Если случится чудо, и каждый операнд окажется истинным, будет возвращено последнее выполненное выражение.

let one = 1, two = 2, three = 3;
console.log(one && two && three); // 3
console.log(0 && null); // 0

Оператор ||, напротив, возвращает первое же "truthy" значение. А если все звенья цепи ложны, вернется вычисленное значение последнего выражения.

let one = 1, two = 2, three = 3;
console.log(one || two || three); // 1
console.log(0 || null); // null

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

Можно использовать конструкцию if/else, чтобы проверить, определена ли переменная foo. Эта запись вполне приемлема, но длинновата. Вот более короткий вариант:

(foo || []).length;

Если переменная foo в логическом контексте ложна (например, это null или undefined), то вместо нее будет подставлен пустой массив как дефолтное значение.

Знакомы с проблемами доступа к свойствам глубокой вложенности? На любом уровне может не оказаться нужного объекта, и выпадет ошибка.

Допустим, есть объект John, а вы хотели бы узнать имя жены Джона (John.wife.name). Однако очень может быть, Джон не женат, необходимо уточнить этот момент.

if (John.wife && John.wife.name) {
return John.wife.name;
} else {
return "Джон, пора тебе жениться";
}

5 строк кода это немало. Давайте укоротим:

John.wife && John.wife.name || "Джон, пора тебе жениться";

Тут мы скомбинировали два логических оператора. У && приоритет больше, поэтому он всегда выполняется первым. Если у Джона нет жены – или у его жены вдруг почему-то нет имени – возвращаем дефолтное значение.

Новые возможности языка

Проблемы со структурой объектов встречаются очень часто, поэтому было внесено предложение добавить в язык JavaScript “опциональные цепочки” (optional chaining). Они позволяют запрашивать глубоко вложенные свойства, не опасаясь ошибок. Движение по цепочке будет продолжено только в том случае, если текущее свойство не равно null.

John.wife?.brother?.dog?.name;

Если у брата жены Джона есть собака, то это выражение вернет ее кличку.

Автоматический биндинг в классах

Если при создании методов вы используете стрелочные функции, такие методы неявно привязываются к экземплярам класса! Это позволяет сэкономить несколько строк кода и избавиться от надоевших конструкций вроде this.myMethod = this.myMethod.bind(this).

import React, { Component } from React;export default class App extends Component {
constructor(props) {
super(props);
this.state = {};
}
myMethod = () => {
// Этот метод неявно связан с this
}
render() {
return (
<>
<div>
{this.myMethod()}
</div>
</>
)
}
};

Форматирование JSON

Скорее всего, вы неоднократно использовали метод JSON.stringify(). А знаете ли вы, что он может самостоятельно форматировать ваш JSON-код?

stringify() может принимать два необязательных параметра (кроме первого – собственно объекта для сериализации):

  • replacer– функция для преобразования значений и свойств или массив тех свойств, которые должны войти в сериализованный объект.
  • space– число, определяющее количество пробелов перед каждым уровнем вложенности, или строка, которая будет вставлена перед каждым уровнем, например, "\t".
console.log(JSON.stringify({ alpha: 'A', beta: 'B' }, null, '\t')); // '{
// "alpha": A,
// "beta": B
// }'
Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade