⚡ Современные и малоизвестные возможности JavaScript, о которых стоит знать

⚡ Современные и малоизвестные возможности JavaScript, о которых стоит знать

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


🔁 Итераторы и их вспомогательные методы

Если ты когда-либо делал цепочку преобразований массива вроде:

arr.slice(10, 20).filter(el => el < 10).map(el => el + 5);

…то ты, возможно, не задумывался, насколько это неэффективно. Каждое преобразование создаёт новый массив. Для больших массивов (>500K элементов) это может стать серьёзной проблемой.

С недавних пор в JavaScript появились итераторные помощники — методы, которые работают как map, filter и т.д., но не создают временные массивы, а возвращают новые итераторы.

Вот основные:

drop(n) — пропускает n элементов (аналог slice(n)).

take(n) — берёт максимум n элементов с начала (аналог slice(0, n)).

some(fn) / every(fn) — аналоги одноимённых методов массива.

filter(fn) / find(fn) / map(fn) / flatMap(fn) — всё как в массивах.

reduce(fn, initial) — как reduce массива, но для итераторов.

forEach(fn) — вызывает функцию для каждого элемента.

toArray() — преобразует итератор в массив.

Их можно использовать с помощью Iterator.from() или, например, arr.values():

arr.values()
  .drop(10)
  .take(10)
  .filter(el => el < 10)
  .map(el => el + 5)
  .toArray();

⚠️ Поддержка появилась недавно. Например, Safari начал поддерживать только с 31 марта 2025 года, так что пока лучше использовать с осторожностью.


📍 Метод at()

Метод Array.prototype.at() — это удобный способ получить элемент по индексу. Самое классное — он поддерживает отрицательные индексы:

[10, 20, 30].at(-1); // 30
[10, 20, 30].at(-2); // 20

Прощай, arr[arr.length - 1].


🧩 Promise.withResolvers()

Раньше, чтобы получить resolve и reject вне Promise, писали так:

let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

Теперь можно проще:

const { promise, resolve, reject } = Promise.withResolvers();

📝 Замена строк с колбэком

Методы replace и replaceAll могут принимать функцию, а не строку:

let counter = 0;
console.log("NUMBER, NUMBER, NUMBER".replaceAll("NUMBER", (match) => `${match}=${++counter}`));
// → NUMBER=1, NUMBER=2, NUMBER=3

Позволяет эффективно делать множественные замены за один проход.


🔄 Обмен значениями

Не надо больше писать:

const temp = a;
a = b;
b = temp;

Теперь можно элегантнее:

[a, b] = [b, a];

Красиво и просто.


📦 structuredClone()

Альтернатива JSON.parse(JSON.stringify(obj)), но безопаснее и быстрее. Она:

  • Поддерживает NaN, undefined, bigint, циклические ссылки.
  • Работает быстрее и не ломается на рекурсивных объектах.
const obj = {};
obj.self = obj;

const clone = structuredClone(obj);
console.log(clone.self === clone); // true

🔖 Tagged templates

Многие знают про шаблонные строки, но не про тегированные шаблоны, которые позволяют обрабатывать строки функцией.

Пример — экранирование HTML:

function escapeHtml(strings, ...values) {
  const div = document.createElement("div");
  return strings.reduce((acc, str, i) => {
    div.textContent = values[i] ?? "";
    return acc + str + div.innerHTML;
  }, "");
}

console.log(escapeHtml`<br> ${'<script>'}`); // <br> &lt;script&gt;

Позволяет кастомизировать шаблонные строки.


🧠 WeakMap / WeakSet

Как Map и Set, но:

  • ключи только объекты (не строки/числа)
  • элементы могут быть автоматически удалены при потере ссылок (garbage collection)

Полезно, когда нужно связать данные с объектом без риска утечек памяти.

const map = new WeakMap();
{
    const obj = {};
    map.set(obj, "value");
} // obj больше недоступен → будет удалён вместе с entry из WeakMap

🔣 Операции над множествами

В JavaScript теперь есть булевые операции для Set:

const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);

a.union(b); // union() — объединение. Set(6) {1, 2, 3, 4, 5, 6}
a.intersection(b); // intersection() — пересечение. Set(2) {3, 4}
a.difference(b); // difference() — разность. Set(2) {1, 2}
a.symmetricDifference(b); // symmetricDifference() — симметричная разность. Set(4) {1, 2, 5, 6}
a.isDisjointFrom(b); // isDisjointFrom() — проверка, что множества не пересекаются. false
a.isSubsetOf(b); // isSubsetOf() — подмножество. false
a.isSupersetOf(b); // isSupersetOf() — надмножество. false

Оригинал статьи

Подписывайтесь на FrontEndDev чтобы получать больше новостей о web разработке