Не каждый полезный прием в JavaScript тянет на отдельную статью, но именно такие мелочи часто экономят время в повседневной разработке. Хороший “трюк” здесь не про хитрость ради хитрости, а про короткий паттерн, который делает код понятнее или убирает лишний шум.

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

Как получить уникальные значения массива

Получить массив уникальных значений проще, чем кажется. Достаточно использовать оператор spread вместе с Set:

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

Массив и Boolean

Иногда нужно отфильтровать массив по новому значению, а не по исходному — для этого используем map:

const ids = [1, 2, 3];

ids.map((item) => {
  if (item === 2) {
    return null;
  }

  return item * 10;
});

Но map возвращает null или false там, где элемент не нужен — и в массиве остаются мусорные значения. Следующим шагом идет фильтрация:

const values = [1, 2, 3];

values
  .map((item) => {
    if (item === 2) {
      return null;
    }

    return item * 10;
  })
  .filter((item) => item);

filter оставляет только truthy-значения — null и undefined отсеиваются. Но можно сделать намерение явным:

const values = [1, 2, 3];

values
  .map((item) => {
    if (item === 2) {
      return undefined;
    }

    return item * 10;
  })
  // Избавиться от бесполезных элементов.
  .filter(Boolean);

.filter(Boolean) убирает null, undefined, 0, false и пустую строку — все значения, которые приводятся к false.

[1, undefined, null, true, 0, 5, "string", false].filter(Boolean); // [1, true, 5, "string"]

Как создать пустой объект

Объект, созданный через {}, кажется пустым, но у него есть __proto__, hasOwnProperty и другие унаследованные методы. Object.create(null) это убирает:

const dict = Object.create(null);

console.log(dict.__proto__ === undefined); // true
console.log(dict); // {} No properties

// Свойства объекта не существуют, пока мы не добавим их

На этом объекте нет абсолютно никаких ключей или методов, которые мы туда не поместим. Поскольку прототипа не существует, объектом нельзя манипулировать извне. Сравни это с Object.create({}):

const obj = Object.create({});

// obj.__proto__ === {}
// obj.hasOwnProperty === function

Object.prototype.someFunction = () => {};

// obj.someFunction === () => {};
// dict.someFunction === undefined

Передача Object.create пустого объекта позволяет добавлять свойства через Object.prototype.customPropName. А подобное нам не всегда подходит.

Объединение объектов

Оператор spread позволяет объединить несколько объектов в один:

const person = { name: "Alex", gender: "Male" };
const tools = { computer: "Mac", editor: "VSCode" };
const attributes = { handsomeness: "Extreme", hair: "Brown", eyes: "Green" };

const summary = { ...person, ...tools, ...attributes };

console.log(summary);
// { computer: "Mac", editor: "VSCode", eyes: "Green", gender: "Male", hair: "Brown", handsomeness: "Extreme", name: "Alex" }

Создается новый объект со всеми свойствами из переданных объектов. Количество объектов не ограничено. То же самое можно сделать через Object.assign, но spread короче.

Параметры функций

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

const isRequired = () => {
  throw new Error("param is required");
};

const hello = (name = isRequired()) => {
  console.log(`hello ${name}`);
};

// этот вызов выдаст ошибку, потому что имя не указано
hello();

// этот вызов также выдаст ошибку
hello(undefined);

// эти вызовы сработают
hello(null); // hello null
hello("Alexey"); // hello Alexey

Деструктуризация псевдонимов

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

const person = { fullName: "Alexey" };

// Берём person.fullName как { fullName }
const { fullName } = person;
console.log(fullName); // Alexey

// Берём person.fullName как { name }
const { fullName: name } = person;
console.log(name); // Alexey

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

Мы всегда можем получить полную строку запроса через свойство window.location.search:

console.log(window.location.search);
// "?post=1234&action=edit"

Но парсить строку вручную не нужно: есть URLSearchParams.

// Например: "?post=1234&action=edit"
const paramsString = '?post=1234&action=edit';
const urlParams = new URLSearchParams(paramsString);

console.log(urlParams.has('post')); // true
console.log(urlParams.get('action')); // "edit"
console.log(urlParams.getAll('action')); // ["edit"]
console.log(urlParams.toString()); // "?post=1234&action=edit"
console.log(urlParams.append('active', '1'));

// После вызова append к запросу добавилось active=1
console.log(urlParams.toString()); // "post=1234&action=edit&active=1"

URLSearchParams также предоставляет методы итерации, такие как keys(), values() и entries():

const paramsString = '?post=1234&action=edit';
const urlParams = new URLSearchParams(paramsString);

const keys = urlParams.keys();
for (const key of keys) {
  console.log(key);
}
// post
// action

const entries = urlParams.entries();
for (const pair of entries) {
  console.log(pair[0], pair[1]);
}
// post 1234
// action edit