Как безсмислената композиция ще ви направи по-добър функционален програмист

Открийте Функционалния JavaScript беше обявен за една от най-добрите нови книги за функционално програмиране от BookAuthority!

Снимка на Ханс-Питър Гостър на Unsplash
Без точков стил - има за цел да намали част от визуалното претрупване, като премахне ненужното параметриране на аргументите на параметри.
Кайл Симпсън във функционален-лек JavaScript

Помислете за течащия код:

нека newBooks = books.filter (point => isTechnology (point))

Сега погледнете същия код след елиминиране на точки (параметри / аргументи):

нека newBooks = books.filter (isTechnology)

Без точка в операции със списък

Да изброяваме операции в стил без точка.

Кажете, че трябва да намерим технологичните заглавия в списък с книги, да подготвим обекта на книгата с цялата информация за изгледа и да сортираме книгите по име на автора.

Ето как би изглеждал кодът:

функция getBooks () {
  върнете books.filter (isTechnology)
              .map (toBookView)
              .sort (ascByAuthor);
}
// Малки функции с точки
функция е Технология (книга) {
   връщане book.type === "T";
}
функция за BookView (книга) {
  върнете Object.freeze ({
    заглавие: book.title,
    автор: автори [book.authorID] .име
  });
}
  
функция ascByAuthor (book1, book2) {
  ако (book1.author  book2.author) върнете 1;
  връщане 0;
}

Обратните връщания еTechnology (), toBookView (), ascByAuthor () са малки функции с имена, разкриващи намерение. Те не са изградени в точков стил.

Кодът, сглобяващ всички тези функции в getBooks (), няма точка.

Разлагане и състав

Нашият естествен начин за справяне с даден проблем е да го разделим на по-малки парчета и след това да съберем всичко отново.

Разбиваме по-голямата задача на няколко функции, изпълнявайки по-малки задачи. След това отново комбинираме тези по-малки функции, за да решим първоначалния проблем.

Нека да прочетем отново изискванията:

Трябва да намерим заглавията на технологиите в списък с книги, да подготвим обекта на книгата с цялата информация за изгледа и да сортираме книгите по име на автора.

Създадохме:

  • isTechnology () предикат, за да провери дали е технологична книга
  • toViewBook ​​() за изграждане на обект с цялата информация за изгледа
  • ascByAuthorname (), за да сортирате две книги възходящо според името на автора
  • getBooks () за комбиниране на всички тези малки функции заедно в стил без точка
функция getBooks () {
  върнете books.filter (isTechnology)
              .map (toBookView)
              .sort (ascByAuthor);
}

Стъпки към композиция без точка

Няма допълнителен анонимен обратен разговор, когато правите точка без композиция. Без функционална ключова дума, без синтаксис на стрелка =>. Всичко, което виждаме, са имена на функции.

  • В повечето случаи извличайте обратните повиквания в посочените функции.
  • В прости случаи просто използвайте полезна функция от кутията с инструменти, за да създадете обратен разговор в движение. Вижте например функцията prop ().
  • Напишете функцията на координатора в стил без точка.

Малки функции

Следствието от писането на код по този начин е много малки функции с намерение разкриване на имена. Именуването на тези малки функции изисква време, но ако е добре, ще улесни четенето на кода.

Ще има два вида функции:

  • Функции, изпълняващи една задача: те са чисти или функции за затваряне. Обикновено те не са изградени в точков стил, но вместо това имат добри имена.
  • Функции, координиращи много задачи: присъединяването към тези малки задачи в стил без точка улеснява четенето.

Не всичко е безцелно

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

Частично приложение

Частичното приложение се отнася до процеса на фиксиране на редица аргументи на функция. - източник

Да вземем случай на функция isOfType (), която може да филтрира по всеки тип книга. Вижте кода по-долу:

нека newBooks = books.filter (book => isBookOfType ("T", книга));

За безсмислена композиция рефакторирам към посочена функция и след това композирам всичко обратно.

функция е Технология (книга) {
  връщането е BookOfType ("T", книга);
}
нека newBooks = books.filter (isTechnology);

В този случай можем да подобрим повече и да създадем новата функция на предикат в стил без точка, използвайки частично приложение.

нека еTechnology = isBookOfType.bind (null, "T");
нека newBooks = books.filter (isTechnology);

Функцията без точка по дефиниция не използва функционалната ключова дума или символа =>.

Функция предикат е функция, която приема един елемент като вход и връща true / false въз основа на това дали елементът отговаря на условие. - източник

Точка без обещания

Да вземем случай на извличане на набор от тодоси от сървъра, избор на най-важните и след това ги изобразяване на екрана.

По-долу е кодът:

fetchTodos (). тогава ((todos) => {
   нека topTodos = getTopPriority (todos);
   направи (topTodos);
 }). catch ((грешка) => {
   console.error ("Имаше грешка:" + error.status);
 });
функция getTopPriority (todos) {}
функция render (todos) {}

Сега нека го рефакторираме в стил без точка. Този път има нещо различно. Както можете да видите, резултатът от функцията getTopPriority () се използва като вход за функцията render (). Можем да използваме веригата за обещание, за да комбинираме заедно функциите getTopPriority () и render ().

Както направихме преди, ние рефакторираме обратния извикване на улов на именувана функция.

fetchTodos ()
  .След (getTopPriority)
  .След (направи)
  .catch (handleError);
функция handleError (грешка) {
  console.error ("Имаше грешка:" + error.status);
}

Всеки след това () може да върне стойност, която е използвала като входна стойност за следващия тогава () разговор.

Кутия за инструменти

В стила без точка, ние се нуждаем от кутия с инструменти, набор от функции, които да използваме отново и отново в общи сценарии.

тръба()

Ще приема случая с изпращане на част от данни чрез множество трансформации.

Обектът с лице с firstName и lastName първо ще има и двете имена с главни букви. Тогава пълното име ще бъде изчислено. След това в края пълното име ще бъде съкратено, за да отговаря на ограниченията на екрана.

По-долу е кода, показващ всички тези стъпки:

функция даFullNameForView (лице) {
  нека newPerson = capitalizeNames (лице);
  нека fullName = computeFullName (newPerson);
  връщане на по-кратко име (пълно име);
}
функция capitalizeNames (лице) {
  връщане {
   firstName: изписване с главни букви (person.firstName),
   lastName: изписване с главни букви (person.lastName)
  }
}
функция с главни букви (име) {
  върнете име.charAt (0) .toUpperCase () + name.slice (1);
}
функция computeFullName (лице) {
  връщане person.firstName + "" + person.lastName;
}
функция по-кратко име (име) {
  връщане name.substring (0, 8);
}

capitalizeNames (), computeFullName (), shorterName () са малките чисти функции. toFullNameForView () е координаторната функция, която ги съставя заедно.

Определянето на toFullNameForView () в стил без точка ще изисква нова полезна функция: pipe ().

функционална тръба (... функции) {
  функция за връщане (стойност) {
   функции за връщане.редуциране (composeLeftToRight, стойност);
  }
}
функция composeLeftToRight (computedValue, fn) {
  връщане fn (computedValue);
}

pipe () вериги функционират заедно, така че изходът на всяка функция се използва като вход за следващата.

Сега нека да определим координаторната функция в стил без точка.

нека toFullNameForView = тръба (
  capitalizeNames,
  computeFullName,
  shorterName,
);

проп ()

Да вземем един прост случай като следния:

books.map ((книга) => book.title);

Една от безпредметни опции е да рефакторирате връщането на повикване към посочена функция:

нека заглавия = books.map (toBookTitle);
функция за BookTitle (книга) {
  връщане book.title;
}

Друг вариант е да използвате функция с общо предназначение, която може да извлече свойство: prop ().

нека заглавия = books.map (prop ("заглавие"));
функция опора (propName) {
  функция за връщане getProp (obj) {
    връщане obj [propName];
  }
}

унарна ()

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

функция unary (fn) {
  функция за връщане unaryDecorator (първо) {
    върнете fn.call (това, първо);
  }
}

Нека използваме console.log () в стил без точка, за да регистрираме всички числа от масив.

нека числата са [1,2,3,4,5,6];
numbers.forEach (console.log);
// 1 0 (6) [1, 2, 3, 4, 5, 6]
// 2 1 (6) [1, 2, 3, 4, 5, 6]
// ...

Както виждате, има проблем. console.log () получава повече аргументи, от които се нуждае. Трябва да сме сигурни, че се извиква само с един аргумент.

Ето кода, използващ функцията unary ().

numbers.forEach (унарна (console.log));
// 1 2 3 4 5 6

По-долу е друг пример за използване на parseInt () в стил без точка:

нека числата са [1,2,3,4,5,6];
numbers.map (parseInt); // [1, NaN, NaN, NaN, NaN, NaN]
numbers.map (унарна (parseInt)); // [1, 2, 3, 4, 5, 6]

Точково без методи

Нека сега използваме методи в точков стил. Създавам обект с фабрична функция и след това използвам метода на обекта в стил без точка.

Ето примера:

функция Услуга () {
  нека url = "http: //";
 
  функция reload () {
    console.log (URL);
  }
  
  върнете Object.freeze ({
    презареди
  });
}
var service = нова услуга ();
setTimeout (service.reload); // HTTP:

Използването на метода в стил без точка работи чудесно.

След това ще направя същото, но този път изграждам обекта, използвайки клас.

клас услуга {
 конструктор () {
    this.url = "http:";
  }
  
  презареди () {
    console.log (this.url);
  }
}
var service = нова услуга ();
setTimeout (() => service.reload (), 0); // HTTP:
setTimeout (service.reload, 0); // неопределен

Както можете да видите, този изгубен контекст.

Проблемът може да бъде решен например с помощта на bind ().

setTimeout (service.reload.bind (услуга), 0);

Това е една от причините да предпочитам фабричните функции пред класовете. За по-задълбочено сравнение, разгледайте функцията Class срещу Factory: проучване на пътя напред.

заключение

Точковата композиция подобрява яснотата и четливостта на кода.

Освен това, тя предпочита практиката да се разлага всичко на по-малки парчета, които след това могат да бъдат съставени по много изразителен начин.

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

Открийте Функционалния JavaScript беше обявен за една от най-добрите нови книги за функционално програмиране от BookAuthority!

За повече информация относно прилагането на техники за функционално програмиране в React, разгледайте Functional React.

Прочетете повече за Vue и Vuex в Бързо въведение към Vue.js Components.

Научете как да прилагате принципите на моделите за дизайн.

Можете да ме намерите и в Twitter.