116848

12 JavaScript-трюков, которым не учат новичков

Небольшая подборка 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>
     </>
   )
 }
};

Если хотите вникнуть в тему глубже, загляните сюда:

Форматирование 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

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

eFusion
01 марта 2020

ТОП-15 книг по JavaScript: от новичка до профессионала

В этом посте мы собрали переведённые на русский язык книги по JavaScript – ...
admin
10 июня 2018

Лайфхак: в какой последовательности изучать JavaScript

Огромный инструментарий JS и тонны материалов по нему. С чего начать? Расск...