Если вы когда-либо готовили еду дома, вы можете понять, как писать код с отслеживанием состояния, используя методы объектно-ориентированного программирования на JavaScript.

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

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

Например, это глобальная переменная с именем state:

let state = "global";

Но как только ваша программа начнет включать в себя множество различных функций и / или объектов, вам нужно будет создать более строгий набор правил для вашего кода.

Здесь вступает в игру понятие состояние. Состояние описывает состояние всей программы или отдельного объекта. Это может быть текст, число, логическое значение или другой тип данных.

Это обычный инструмент для согласования кода. Например, после обновления состояния множество различных функций могут мгновенно отреагировать на это изменение.

Эта статья описывает состояние в контексте React, популярной библиотеки JavaScript.

Но знаете что? Даже состояние может вызвать у вас головную боль, когда ваш код станет сложным! Изменение состояния может вызвать непредвиденные последствия.

На этом остановимся. Состояние - популярный инструмент объектно-ориентированного программирования или ООП. Но многие программисты предпочитают функциональное программирование, которое препятствует изменению состояния. JavaScript поддерживает обе парадигмы.

Ладно, это сразу много терминологии. Я хотел найти способ показать, как ООП и функциональное программирование могут достигать одних и тех же целей, даже если функциональное программирование не использует состояние.

Из этого туториала Вы узнаете, как приготовить блюдо из спагетти и соуса с точки зрения ООП и функциональности.

Вот краткий обзор двух разных подходов:

Давайте начнем. Чтобы понять это руководство, вам просто нужно понять функции и объекты в JavaScript.

Объектно-ориентированный метод (с использованием состояния)

На рисунке выше мы показали два разных подхода к приготовлению этого макаронного обеда:

  1. Метод, ориентированный на состояние различных инструментов, таких как плита, горшок и макароны.
  2. Метод, ориентированный на развитие самой еды, без упоминания состояния отдельных инструментов (кастрюль, плит и т. Д.)

Объектно-ориентированный подход фокусируется на обновлении состояния, поэтому наш код будет иметь состояние на двух разных уровнях:

  1. Глобальный, или состояние всей трапезы.
  2. Локально для каждого объекта.

В этом руководстве мы собираемся использовать синтаксис ES6 для создания объектов. Вот пример глобального состояния и прототипа «Горшка».

let stoveTemp = 500;
function Pot(){
  this.boilStatus = '';
  this.startBoiling = function(){
    if( stoveTemp > 400)
      this.boilStatus = "boiling";
  }
}
let pastaPot = new Pot();
pastaPot.startBoiling();
console.log(pastaPot);
// Pot { boilStatus = 'boiling'; }

Примечание. Я упростил инструкцию console.log, чтобы сосредоточиться на обновлении состояния.

Вот визуальное представление этой логики:

До

После

Есть два состояния, и когда pastaPot создается с помощью прототипа Pot, он изначально имеет пустой boilStatus. Но затем происходит изменение состояния.

Мы запускаем pastaPot.startBoiling(), и теперь boilStatus (локальное состояние) «кипит», поскольку глобальное состояние stoveTemp превышает 400.

А теперь сделаем еще один шаг. Мы дадим пасте возможность закипеть из-за состояния pastaPot.

Вот код, который мы добавим во фрагмент выше:

function Pasta (){
  this.cookedStatus = false;
  this.addToPot = function (boilStatus){
    if(boilStatus == "boiling")
      this.cookedStatus = true;
  }
}
let myMeal = new Pasta();
myMeal.addToPot(pastaPot.boilStatus);
console.log(myMeal.cookedStatus);
// true

Вау! Это сразу много. Вот что произошло.

  1. Мы создали новый прототип «Пасты», где каждый объект будет иметь локальное состояние под названием cookedStatus.
  2. Мы создали новый экземпляр Pasta под названием myMeal.
  3. Мы использовали состояние из объекта pastaPot, который мы создали в последнем фрагменте, чтобы определить, следует ли обновить состояние с именем cookedStatus в myMeal на готовое.
  4. Поскольку состояние boilStatus в pastaPot было «кипящим», наша паста теперь приготовлена!

Вот этот процесс визуально:

До

После

Итак, теперь у нас есть локальное состояние одного объекта, которое зависит от локального состояния другого объекта. И это локальное состояние зависело от какого-то глобального состояния! Вы видите, насколько это может быть сложно. Но, по крайней мере, сейчас за этим легко следить, поскольку состояния обновляются явно.

Функциональный метод (без состояния)

Чтобы полностью понять состояние, вы должны быть в состоянии найти способ достичь того же результата, что и приведенный выше код, без фактического изменения состояния. Вот здесь и помогает функциональное программирование!

Функциональное программирование имеет две основные ценности, которые отличают его от ООП: неизменность и чистые функции.

Я не собираюсь углубляться в эти темы, но если вы хотите узнать больше, я рекомендую вам ознакомиться с этим руководством по функциональному программированию на JavaScript.

Оба эти принципа препятствуют использованию модификации состояния в вашем коде. Это означает, что мы не можем использовать локальное или глобальное состояние.

Функциональное программирование вместо этого поощряет нас передавать параметры отдельным функциям. Мы можем использовать внешние переменные, но не можем использовать их как состояние.

Вот пример функции, которая будет варить макароны:

const stoveTemp = 500;
const cookPasta = (temp) => {
  if(temp > 400)
    return 'cooked';
}
console.log(cookPasta(stoveTemp));
// 'cooked'

Этот код успешно вернет строку «приготовлено». Но обратите внимание - нет объекта, который мы обновляем. Функция просто возвращает значение, которое будет использовано на следующем шаге.

Вместо этого мы сосредоточены на входах и выходах одной функции: cookPasta.

Эта перспектива смотрит на преобразование самой пищи, а не на инструменты, которые используются для ее приготовления. Это немного сложнее визуализировать, но нам не обязательно, чтобы функция зависела от внешнего состояния.

Вот как это выглядит:

Думайте об этом как о «временной шкале» для прогресса приема пищи - эта конкретная функция охватывает только первую часть, переход от сухой пасты к приготовленной.

Теперь давайте накроем вторую часть, когда еда будет подана. Вот код, который будет подавать блюдо. Он появится после блока кода выше:

const serveMeal = (pasta) => {
 if (pasta == 'cooked')
   return 'Dinner is ready.'
}
console.log( serveMeal(cookPasta(stoveTemp)) );
// 'Dinner is ready.'

Теперь мы доставляем результаты функции cookPasta непосредственно в функцию serveMeal. Опять же, мы можем сделать это без изменения состояния или структуры данных.

Вот диаграмма, в которой используется «представление временной шкалы», чтобы показать, как эти две функции работают вместе:

Заинтересованы в дополнительных наглядных пособиях?

Если вам понравилось это руководство, дайте ему «аплодисменты»!

И, если вы хотите прочитать больше визуальных руководств по HTML, CSS и JavaScript, посетите основной сайт CodeAnalogies, где вы найдете более 50 руководств.