Небольшая подборка JavaScript-трюков и полезностей для создания краткого и эффективного кода, которые вы не найдете в учебниках.
Большинство учебников и туториалов объясняют начинающим программистам базовые концепции и конструкции. Из них вы получили академические JavaScript основы языка, а вот практических хитростей придется набираться самостоятельно.
Многие из описанных в статье JavaScript-трюков стали возможны только в последних стандартах JS, поэтому в старых руководствах вы их не найдете. Другие приносят в жертву краткости читаемость, поэтому их не упоминают, чтобы не смущать разум новичков. Парочку приемов все-таки можно найти в учебниках, но вы вполне могли их пропустить.
4 JavaScript-трюка с массивами
Фильтрация уникальных значений
В стандарте ES6 появился новый тип объектов Set
. Скомбинировав его со спред-оператором (...
), можно легко получить из старого массива новый, в котором будут только уникальные значения.
const array = [1, 1, 2, 3, 5, 5, 1] const uniqueArray = [...new Set(array)]; console.log(uniqueArray); // [1, 2, 3, 5]
До ES6 для решения этой задачи пришлось бы написать гораздо больше кода! А еще Set ускоряет код.
Кэширование длины в цикле
Когда мы изучаем программирование на 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); }
Лаконичность кода почти не страдает, но при работе с большими массивами он будет работать немного эффективнее.
Укорачивание
Это один из самых известных JavaScript-трюков, но повторить его не помешает.
Чтобы удалить несколько значений из конца массива, необязательно пользоваться методами 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]
3 JavaScript-трюка с преобразованием типов
Преобразование в строку
Быстро преобразовать число в строку можно с помощью оператора +
, просто сконкатенировав его с пустой строкой.
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-трюков оправдано только в том случае, если вы хорошо понимаете, на чем основано их действие.
Преобразование в булев тип
Язык программирования JavaScript может рассматривать любое значение с логической точки зрения. Все, что преобразуется в false
, называется "falsy" (ложное). Это число 0, пустая строка ""
, null
, undefined
, NaN
и, конечно же, false
. Все остальные значения – истинные ("truthy"). На эту концепцию опирается множество JavaScript-трюков.
Оператор логического отрицания !
умеет работать со значениями любого типа. Он конвертирует любое falsy значение в true
, а любое truthy – в false
. Таким образом, на выходе всегда получается булево значение. Вот JavaScript примеры:
const true = !0; const false = !1; const alsoFalse = !!0; console.log(true); // true console.log(typeof true); // "boolean"
Такое преобразование может быть удобно в условных операторах.
Пара математических JavaScript-трюков
Загляните в нашу статью о работе с числами и математическими методами в JS.
Быстрое возведение в степень
Стандарт ES7 предложил нам новый оператор **
для возведения числа в степень.
2 ** 3; // 8
Это явно короче, чем Math.pow(2, 3)
.
Вероятно, вы ожидали увидеть здесь более привычный символ ^
. Но в JavaScript он уже занят – это побитовое исключающее ИЛИ (XOR).
До ES7 у нас был короткий способ возведения в степень только для числа 2 с помощью побитового оператора левого сдвига <<
:
// Эти выражения эквивалентны Math.pow(2, n); 2 << (n - 1); 2**n;
Например, 2 << 3 = 16
, и 2 ** 4 = 16
.
Быстрое округление
Если нужно сделать дробное число целым, вы используете Math.floor()
, Math.ceil()
или Math.round()
– в зависимости от нужного эффекта. Но есть и более быстрый путь – побитовое ИЛИ.
console.log(23.9 | 0); // 23 console.log(-23.9 | 0); // -23
Поведение оператора с положительными и отрицательными операндами отличается. Положительные округляются вниз, негативные – вверх. Все цифры после десятичной точки удаляются.
Такого же эффекта можно добиться с помощью других JavaScript-трюков, например, уже знакомого нам двойного оператора побитового отрицания (~~
).
Удаление разрядов
С помощью побитового ИЛИ можно удалять из числа разряды, не путаясь с дробной частью:
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
JavaScript пример 1
Требуется получить значение свойства length
некоторого массива. Однако вместо массива вы можете получить undefined
, и в этом случае будет ошибка.
Можно использовать конструкцию if/else
, чтобы проверить, определена ли переменная foo
. Эта запись вполне приемлема, но длинновата. Вот более короткий вариант:
(foo || []).length;
Если переменная foo
в логическом контексте ложна (например, это null
или undefined
), то вместо нее будет подставлен пустой массив как дефолтное значение.
JavaScript пример 2
Знакомы с проблемами доступа к свойствам глубокой вложенности? На любом уровне может не оказаться нужного объекта, и выпадет ошибка.
Допустим, есть объект 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;
Если у брата жены Джона есть собака, то это выражение вернет ее кличку.
Предложение сейчас находится в первой стадии рассмотрения (экспериментальная возможность). Вы можете почитать о нем или попробовать в действии с помощью Babel. Для этого добавьте в файл .babelrc плагин @babel/plugin-proposal-optional-chaining.
Автоматический биндинг в классах
Если при создании методов вы используете стрелочные функции, такие методы неявно привязываются к экземплярам класса! Это позволяет сэкономить несколько строк кода и избавиться от надоевших конструкций вроде 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> </> ) } };
Если хотите вникнуть в тему глубже, загляните сюда:
- Пора понять замыкания в JavaScript! Часть 1. Готовим фундамент
- Пора понять замыкания в JavaScript! Часть 2. Переходим к делу
Форматирование JSON
Скорее всего, вы неоднократно использовали метод JSON.stringify()
. А знаете ли вы, что он может самостоятельно форматировать ваш JSON-код?
stringify()
может принимать два необязательных параметра (кроме первого – собственно объекта для сериализации):
replacer
– функция для преобразования значений и свойств или массив тех свойств, которые должны войти в сериализованный объект.space
– число, определяющее количество пробелов перед каждым уровнем вложенности, или строка, которая будет вставлена перед каждым уровнем, например,"\t"
.
console.log(JSON.stringify({ alpha: 'A', beta: 'B' }, null, '\t')); // '{ // "alpha": A, // "beta": B // }'
Возможно, вы нашли среди этих советов парочку интересных.
Помните, что не все из этих JavaScript-трюков пригодны к использованию в продакшене. Некоторые предназначены скорее для изучения возможностей языка и код-гольфа.
Делитесь полезными трюками, которые сложно найти в учебниках.
Оригинал: 12 JavaScript Tricks You Won’t Find in Most Tutorials
Комментарии