Аудыт файла utils.js: Шас вбудованых функцый, які заменяюць ручная створаная дапаможнікаў
Пазіце, які вбудованыя API JavaScript можна выкарыстаць замест атак на глыбокую клонаванне, захоўнія копій, ручных таймаутаў, асоблівых прагнозаў і функцыі lodash groupBy, а таксама якія ўмовы трэба выважваць.
Крыніцы: 6 Modern JavaScript Features You’re Probably Still Working Around
Большынства кодавых базаў дасягналі ў своім складзе функцыі-памочнікі і імпорты бібліятэкаў для проблем, якія платформа рашыла ўжо леты таму. Што гэтыя дадаткі былі адключаны тыха, прычынай чаго звыкі, сформаваныя ўжо да старых спосабаў рашэння, так і не былі пераглядзены. Ёсць шасць вбудованых функцый, якія варта пераглянуць у вашым utils.js, а таксама проблемы, якія кожная з іх усуне, і меры абережнасці, якія трэба ведаць пры ўдаленні чаго-небудзь.
structuredClone() заместа перадачы дадзеных у формате JSON
Найпаўзначыцэйнейшы спосаб размножэння об’екта — серіялізаванне і пасля таго парсаванне:
const copy = JSON.parse(JSON.stringify(original));
Ён не працуе з тым, што JSON не можа представіць: экземпляры Date вяртаюцца як строкі, атрыбуты, заданыя на undefined, пропадаюць, Map і Set ператвараюцца на порожнія об’екты без жадных паведамленняў, а ціркулярныя звязкі вызываюць падметы ў JSON.stringify.
structuredClone() — глобальная функцыя, якая доступна ў сучасных прыгледачах і Node.js, і яе не трэба імпортаваць. Возьмімо об’ект, які мае дату, множыню і карточку:
const original = {
date: new Date(),
items: new Set([1, 2, 3]),
meta: new Map([['key', 'value']]),
};
Клонаванне яго захоўвае кожны з гэтых типаў недыскретаваным, а цыклічныя структуры таксама копіююцца правільна:
const copy = structuredClone(original);
// Dates, Sets, Maps, and circular refs all survive intact
Ў яе викорыстоўваецца алгорытм структураванага клонавання, які працуе пад час postMessage і IndexedDB, таму яна падтрымлівае крайнія случаи, якіх не падтрымваюць ручна напісаныя клонаванні. У яе ёсць і меры: функцыі і элементы DOM не можна клонаваць, і тады выйдзе памылка; кроме таго, экземпляры вашых сабэйшых класаў вяртаюцца як звычныя об’екты без ўсіх ўластэйваў протатыпа.
toSorted() і toReversed() замест захоўнага клонавання
.sort() і .reverse() мутуюць об’ект на месцы, таму сортаванне массива, які аднаўляецца праз рэферэнс, зміняе яго ў всіх месцах. Начынаючы з гэтага массива:
const scores = [30, 10, 20];
toSorted() вяртае новы, сортаваны масэв і не паўтарае змест выходнага масэва:
const sorted = scores.toSorted((a, b) => a - b);console.log(scores); // [30, 10, 20] — untouched
console.log(sorted); // [10, 20, 30]
toReversed() і toSpliced() выконваюць той самы прынцып вяртання копіі. Якщо ваш код заполнены выразамі на кшталт [...arr].sort() для зберагчэння незменнасці стану, як часта бывае у редакторах React, тады распространэнне масэва больш не ўсё такі неабходнае. Больш дакладную інфармацыю пра гэтыя методы вяртання копіі можна знайсці у сучасных методах масэва JavaScript, якія заменяюць стандартны код.
Object.hasOwn() замест hasOwnProperty
Выкліканне гэтага метода через аб’ект маскве яго ахованыя проблемы:
obj.hasOwnProperty('key'); // breaks if obj = Object.create(null)
Об’ект, створаны ў спосабе Object.create(null), не мае протатыпу, таму ён ніколи не спадчыць атрыбут hasOwnProperty, і такое выкліканне вызывае памятку TypeError. То ж самае выходзіць, калі об’ект задае сабе власны атрыбут з такім іменем. Функцыя Object.hasOwn() ўсё ж статычная, таму яна не залежыць ад таго, што спадчывае об’ект:
Object.hasOwn(obj, 'key'); // works regardless of the object's prototype chain
Эта замена ўсьмохнутая, але яна запобегае проблемам, якія з’яўляюцца толькі пад час роботы продакшна, калі вызываючый апарат перадае об’ект у формате, які ніхто не планаваў.
AbortSignal.timeout() замест ручнага таймера
Ёжы fetch канечны тэрмін раней выклекаў патрабаванне да AbortController адзначаючы яго за дапамогою setTimeout:
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), 5000);
Потым патрабавалася памятка аб зняцці таймера пасля таго, як запит будзе завершаны:
fetch(url, { signal: controller.signal }).finally(() => clearTimeout(id));
Тепер сігнал можа маты свой сабэй канечны тэрмін:
fetch(url, { signal: AbortSignal.timeout(5000) });
Адзін рэчык, без чысткі, без застаўшагуся таймера. Калі ён выкананы, запрос адхоўляецца з TimeoutError DOMException, што дазволяе адразніць таймаут ад абароткі, вызванай корыстнікам. Чырпаць таймаут з іншым джэрам абароткі можна за дапамою AbortSignal.any() у чынный дакументацыі.
Promise.withResolvers() замест адкрытых функций resolve і reject
Інодзе праграму можа зьбірваць звычайна занэўнатка з ўсероўні свайго экзэкутара. Класычны спосаб — адгледзець зменнікі і прызначыць іх уяўнікай канстрактара:
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
Гэта працуе, але вынесенне функцый з закрытага ціклу лёгка спамятаць неправільна. Promise.withResolvers() вяртае праграму і обе функцыі зьбірвання разам:
const { promise, resolve, reject } = Promise.withResolvers();
Гэта падходзіць якраз тады, калі вы ставляеце асінхронную роботу у чергу чыстаўкаея API, які работае на адбудовы змаганняў, ў формат promise. Чырвоны прыклад можна пазначыць за лінкам коордынацыя вызоў інструментаў LLM за дапамогою Promise.withResolvers.
Object.groupBy() заместо lodash groupBy
Групаванне записаў па ключы ёсць аднай з найчастэйшых прычынаў імпортування бібліятэкі з адпаведнымі функцыямі. Дадамо спіс адносабов:
const people = [
{ name: 'Sam', age: 21 },
{ name: 'Lee', age: 34 },
{ name: 'Kai', age: 21 },
];
Object.groupBy() прымеў ітерабельны об’ект і функцыю-вызывач, і вяртае об’ект, у яком кожны ключ адпаведае арэйцы:
const byAge = Object.groupBy(people, (p) => p.age);
// { 21: [Sam, Kai], 34: [Lee] }
Зверніце увагу на назву. Ранейшая праця называла гэты метод Array.prototype.group(), але яго перайменавалі і пераказалі у статычны Object.groupBy() (а таксама Map.groupBy(), калі ключы не ў формате строк), перш чым была стандартизавана, і выдаваюцься толькі статычныя версіі. Рэзультат мае протатып null, і гэта найновейшая API ў гэтым списку, таму пераканайцеся, калькія версіі прыгледвача і Node.js ў вас падтрымліваюцца, перш чым адмахнуцца ад гэтай залежнасці.
Перш чым адмахнуцца ад дапаможных функцый
Жадны з гэтых крокаў не змінюе вашу архітектуру, але кожны з іх усунеяць модель, схильную да багоў, або непатрэбную залежнасць. Розумны порядак:
- Пераканайцеся ў падтрымкі на caniuse.com або MDN для тых средаванняў, якія вы насправды вядомі, адзін з гэтых інструментаў можа быць новейшы, а старэйшыя прыгледвачы можа не падтрымляць яго.
- Зберагаюце поліфілы, пакуль ваша матрыца падтрымкі сапраўды не дазволіць іх адмахнуцца.