През последната година се опитвах да подобря уменията си в JavaScript. Може би най-полезното нещо, което научих, е композицията. Да можеш да пишеш код, който се чете добре, е страхотно както за нас, така и за бедния човек, който може да поддържа кода веднъж в бъдеще.

Аз ❤️ ES6. Синтаксисът е много по-добър от стария стил на JavaScript. И дори лошата репутация, която JavaScript имаше навремето, е на път да изчезне. Методи като картиране, филтриране, намаляване са не само непроменливи методи, но и се четат добре. Но все още можете да сбъркате. И е доста лесно да се обърка с JavaScript. Така че изборът на стил на код и качването на функционалния JS хипстърски вагонможе да е добра идея!

Така че да продължим. Да кажем, че имаме тази част в стар стил JavaScript:

function completedTodos(todos) {
  var completedTodos = [];
  for (var i = 0; i < todos.length; i++) {
    if (todos[i].completed) {
      if (completedTodos.length < 10) {
        todos[i].image = './path/image.jpg';
        completedTodos.push(todos[i]);   
      }
    }
  }
  return completedTodos;
};

Освен стария синтаксис, функцията може да изглежда доста добре на пръв поглед. Не е толкова дълго и изглежда доста чисто. Но не е така. Първо мутира два пъти: Присвояването на свойства директно върху масива todos и методът push също се променят. Но този път няма да се концентрираме върху мутирането, запазваме го за по-късно. Ще се концентрираме върху разлагането.

И така, нека да видим какво всъщност прави функцията:Тя връща последните десет завършени задачи и добавя изображение към тях. Първо, името на функцията completedTasks не описва много какво всъщност прави и по-важното е доста трудно да се прочете от кода какво всъщност прави. А наличието на четим самообясняващ се код е доста ценно. Функционалното програмиране е много за това. Разказване на история, една функция в даден момент.

За да разложим тази функция, нека започнем с обмисляне на стъпките, от които се нуждаем, за да постигнем това, което искаме:
• Намерете всички завършени задачи
• Филтрирайте последните десет
• Добавете изображение към всяка задача

И така, ще напиша три нови функции със синтаксиса на ES6:

const completedTodos = todos => todos.filter(
  todo => todo.completed
);
const firstTenTodos = todos => todos.slice(0, 10);
const addImageToTodos = todos => todos.map(todo => {
  return {...todo, image: './path/image.jpg'}
});

Това е много по-хубаво. Имената на функциите са специфични за това, което функцията прави, и дори ако не сте запознати със синтаксиса, е доста ясно какво правят и методите. Първо филтрираме, за да получим завършените задачи, отрязваме последните 10 и след това картографираме всеки обект и добавяме изображение. Освен това няма мутиране, само красиви чисти функции, които връщат нови обекти. Страхотно! 🎉

Така че сега ще съставим нашите функции заедно. Можем да направим във vanilla JS така:

cont addImageToFirstTenCompletedTodos = 
addImageToTodos(firstTenTodos(completedTodos(todos)));

Но това не ми харесва. Трябва да прочетем функцията отвътре навън или отдясно наляво, за да я получим. Това не е много четивно! Вместо това ще използвам малък помощник, наречен Lodash Flow. Ще направи абсолютно същото, но отляво надясно и синтаксисът е много по-хубав.

import flow from 'lodash/flow';
const addImageToFirstTenCompletedTodos = flow(
  completedTodos,
  firstTenTodos,
  addImageToTodos,
)(todos);

Ще видиш? Това е само списъкът, който имахме в началото 👌

Бонус: Вече можем да пишем тестове!

Когато правим нещата по този начин, също е много по-лесно да пишем тестове. Единичните тестове може да са плашещи, ако сте нов в JavaScript, но всъщност са доста страхотни! Ще използвам „Jest“ за пример, но има и няколко други страхотни тестове.

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

import 'completedTodos' from '../functions/completedTodos';
var todos = [{
    name: 'One',
    completed: true,
  }, {
    name: 'Two',
    completed: false,
  }, {
    name: 'Three',
    completed: true,
  }
];
var result = [{
    name: 'One',
    completed: true,
  }, {
    name: 'Three',
    completed: true,
  }
];
describe('firstTenTodos', () => {
  it('Takes and array of todos and returns the completed', () => {
    expect(completedTodos(todos)).toEqual(result);
  });
});

Единственото нещо, което трябва да направим, е да опишем как изглеждат нашите данни и какъв искаме да бъде резултатът. След това поставяме нашата функция в expect и нашия очакван резултат в toEqual. След това трябва да дадем на нашата тестова колекция и име на теста и сме готови да започнем npm run test. Лесно грах 🙌

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

Така че аз самият съм далеч от функционален герой на JavaScript. Но наистина ми харесва да уча и виждам как кодът ми се подобрява. И докато светът на интерфейса се движи доста бързо в наши дни, усещането е доста приятно да научите и използвате тези „стари доказани принципи на компютърната наука“ на функционалното програмиране.