Галоўная / Артыкулы

Аудыт файла utils.js: Шас вбудованых функцый, які заменяюць ручная створаная дапаможнікаў

Пазіце, які вбудованыя API JavaScript можна выкарыстаць замест атак на глыбокую клонаванне, захоўнія копій, ручных таймаутаў, асоблівых прагнозаў і функцыі lodash groupBy, а таксама якія ўмовы трэба выважваць.

Выдавец: REACTAPP.TOP5 хв чытання1052 слоў

Крыніцы: 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 для тых средаванняў, якія вы насправды вядомі, адзін з гэтых інструментаў можа быць новейшы, а старэйшыя прыгледвачы можа не падтрымляць яго.
  • Зберагаюце поліфілы, пакуль ваша матрыца падтрымкі сапраўды не дазволіць іх адмахнуцца.
  • Пацелуйце месца вызоў паступова, адбываючы тэсты з кланаванням і групаваннем, калі мае значэньне формат выходных дадзеных.
  • Удаліце стары хелпер або імпорт бібліятэкі толькі тады, калі нічто яго не аб’являе.