Кратки и сладки техники, които да държите в задния си джоб

Тази седмица O’Reilly пусна третото издание на JavaScript Cookbook, сега напълно обновена с модерни функции и практики. Знам това, защото написах част от това съдържание, заедно с моя звезден съавтор „Адам Скот“.

За да отбележа това издание, реших да събера няколко от любимите си фрагменти на JavaScript. Не говоря за масивни функции, рамки или автоматично генериран код. Дори не говоря за нови езикови иновации или хакове за продуктивност. Говоря за умни малки кодлети, които правят живота по-добър. Такива, които съкращават 12 реда до 2 или улесняват писането на чист, ясен код. Сигурен съм, че вие ​​също имате свой собствен запас от трикове - и може би ще намерите нова идея, която да съберете в този списък.

1. Използване на Symbol за създаване на enum

Вероятно сте запознати с Symbol, необичайният JavaScript обект, който има една-единствена цел в живота: предоставяне на случаен идентификатор, който гарантирано е глобално уникален. Symbol има някои интересни (и силно специализирани) приложения за разширяемост и метапрограмиране. Но също така се удвоява като добър начин за създаване на enum – групиране на наименувани константи – което езикът JavaScript не поддържа първоначално.

Ето пример с конструкция, подобна на enum, наречена TrafficLight:

В този пример всяка стойност на enum (например TrafficLight.Green) получава уникална стойност. Но всъщност никога не виждате тази стойност, защото Symbol я поддържа напълно непрозрачна. Така че, ако трябва да сериализирате тези данни извън вашето приложение (например да ги съхранявате на диск или да ги изпратите по кабела), това вероятно не е подходът, който искате.

2. Безболезнено тествайте код в конзолата

Отнема само няколко секунди, за да стартирате тестова страница на JavaScript. Но понякога искам да изпробвам една отделна JavaScript функция. Още по-полезно е, ако мога да работя върху този тестов кодов фрагмент в браузъра, до статия, която чета.

Сега няма нищо магическо в това да извикате DevTools на вашия браузър (F12 в Windows и Cmd-Shift-J или Cmd-Option-J в macOS, в зависимост от браузъра). И няма нищо вълшебно в това да въведете някакъв код в JavaScript конзолата — просто не забравяйте да Shift+Enter за всеки нов ред и да натиснете Enter, за да изпълните готовия код. Но ако искате да повторите пример (да го въведете веднъж, да го редактирате, да го стартирате отново и т.н.), трябва да контролирате обхвата на изпълнението на вашия код.

Най-добрият начин да направите това е да увиете целия си кодов блок във фигурни скоби { }. По този начин можете да стартирате кода си (натиснете Enter), да го извикате отново (натиснете стрелката нагоре), да го редактирате и да го стартирате отново, всичко това без дразнещата грешка „Идентификаторът вече е деклариран“.

Така че вместо да пишете това:

let testValue = 40+12;
console.log(testValue);

Ти искаш това:

{
let testValue = 40+12;
console.log(testValue);
}

лесно!

3. Дълбоко копиране на масив в един ред

Вероятно знаете, че едно от големите подобрения на съвременния JavaScript е групата от методи за обработка на масиви във функционален стил, които ви позволяват да работите с вашите данни, без да ги итерирате. Един от най-мощните от тези методи е Array.map(), който изпълнява функция на всеки елемент и ви дава нов масив с резултатите.

Array.map() може да направи много трикове, но клонирането на масив е един от по-полезните. За да видите как работи, представете си, че създавате масив като този с два обекта:

const objectsOriginal = [{name: 'Sadie', age: 12},
                         {name: 'Patrick', age: 18}];

Сега кажете, че искате да копирате тези обекти. Този код не е това, което искате:

// All this gets you is two variables pointing to the same array
const objectsCopy = objectsOriginal;

Това е малко по-добре, но все още не прави това, което искате:

// Creates two array objects, but they share the same people objects
const objectsCopy = [...objectsOriginal];

(Можете да тествате това, като промените обект в един масив и проверите, че това е същият променен обект, дори ако имате достъп до него през другия масив.)

Ето решение с Array.map(), което взема всеки елемент, разширява обекта и след това създава дублиран обект със същите свойства:

const objectsCopy = objectsOriginal.map(element => ({...element}));

Ето пълна демонстрация на техниката:

Разбира се, има някои уговорки. Това е еднослойно дълбоко копие, така че ако вашите обекти съдържат препратки към други обекти, те не се дублират. В ситуация като тази е по-добре да „формализирате логиката си на клониране“, като създадете персонализиран клас и напишете персонализиран clone() метод. След това можете да използвате Array.map(), за да извикате този clone() метод:

const objectsCopy = objectsOriginal.map(element => element.clone());

4. Изпразване на масив в един ред

Докато сме на тема масиви, ето още един полезен трик. Понякога искате да изпразните обект от масив, без да го замествате с нов празен масив (може би защото е посочен от друг обект). Преди да започнете да итерирате и извиквате Array.remove(), ето пряк път, който работи, като зададете свойството length:

const numbers = [2, 42, 5, 304, 1, 13];
numbers.length = 0;
// The array is now empty

Ако сте обучени на традиционен ООП език, това може да изглежда странно, защото Array.length изглежда като свойство, което трябва да бъде само за четене, а задаването на свойство обикновено не трябва да задейства операция (като премахване на елементи). Но в JavaScript понякога просто правите това, което ви харесва.

5. Дайте на обекта си разумно низово представяне

Омръзна ли ви да виждате „[object Object]“ в конзолата на браузъра, когато го показвате с console.log()? Можете лесно да отмените това поведение, като дадете на вашия обект уважаван toString() метод. Ето един пример:

Още веднъж JavaScript премахва част от инфраструктурата, която бихте видели в класическия ООП език. (Например няма ключова дума override.) Но работи.

6. Поддържайте веригата на методите във вашите класове

Верижното свързване на методи всъщност не се квалифицира като трик, но е една от онези практики, които не винаги смятаме да поддържаме, и може да ви спести известно време. Също толкова важно е, че той се свързва с начина на живот на JavaScript, защото много вградени обекти го използват за добър ефект. Разгледайте този пример с обекта Array. Тук веригата на методите ви позволява да обедините две операции в един ред - конкатенация на масив и сортиране на масиви:

const evens = [2, 4, 6, 8];
const odds = [1, 3, 5, 7, 9];
const evensAndOdds = evens.concat(odds).sort();
console.log(evensAndOdds); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

Най-простият начин да поддържате верижно свързване на методи във вашите собствени персонализирани класове е просто да върнете препратка към текущия обект с ключовата дума this. Този Book клас използва тази техника в своите raisePrice() и releaseNewEdition() методи:

Функционалните програмисти ще разгледат това и ще кажат, че може би изобщо не искате обект със състояние, а неизменен обект, който продължава да връща копия, както правят Array.concat() и Array.sort(). Ако този подход има смислен за вашата кодова база, просто върнете нов обект с модифицираните детайли вместо текущия екземпляр в края на метода, като това:

raisePrice(percent) {
  const increase = this.price*percent;
  return new Book(this.title, this.author,
   this.price + Math.round(increase)/100, this.date);
}

7. Направете повторяем списък с произволни числа

Този е малко по-специализиран, но ми беше полезен в краен случай.

Има няколко различни начина за създаване на псевдослучайни числа в JavaScript. Стандартът Math.random() получава произволни стойности, които не са криптографски защитени, което е подходящо за повечето употреби. Ако не, има по-малко известния Crypto.getRandomValues(), който да ви помогне.

Но и двата подхода ви дават неповторими произволни числа. Това не е това, от което се нуждаете, ако искате да проведете повторяем тест или симулация, което е важно за много статистически и научни операции. Можете да стигнете много дълбоко и сложно в тази област, но аз винаги поддържам простия и невероятно бърз алгоритъм на Mulberry32, за да ми дават псевдослучайни числа, които са напълно детерминистични (което означава, че ако започнете с едно и също семе, винаги получавате същия списък на ценностите). И го обгръщам в генераторна функция, която е една от любимите ми специализирани функции на JavaScript. Ето кода:

Не е необходимо да разбирате частта за изместване на битове на тази реализация, която е заимствана от „класическата C реализация“. Обратът на JavaScript е, че това е функция генератор, както е обозначено със звездичката в ключовата дума function*. Функциите на генератора използват yield за връщане на стойности при поискване — в този случай произволни числа.

Ето как създавате и извиквате генератора:

Тази генераторна функция обгръща безкраен цикъл, който работи, докато продължавате да извиквате next(). Ако не се нуждаете от произволно число, изпълнението на генератора се поставя на пауза, като цялото му състояние се запазва непокътнато. Разбира се, не се нуждаете от функция за генериране, за да създадете списък с произволни числа, но това е елегантно решение.

Ако сте харесали тези примери, вижте Готварската книга на JavaScript за още идеи за миниатюрен код. Или се абонирайте за бюлетина на Young Coder за имейл веднъж месечно с най-добрите ни статии.