В Ruby Track от Launch School довольно много курсов между RB175 и JS210. То есть есть много курсов, которые не ориентированы на Ruby или JavaScript - это нечто среднее между сетями, SQL, HTML и CSS.

В большинстве этих курсов мы отклоняемся от всестороннего акцента на овладение мастерством и вместо этого знакомимся с огромными темами, в которых для усвоения отобраны только части. Достаточно, чтобы напомнить нам, что не все, что связано с программированием, нужно осваивать (слава богу).

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

Страшная (но необходимая) часть мастерства - это склоняться к к ситуациям, в которых вы сильно заблудились, вместо того, чтобы избегать их. Присмотритесь к коду, который для вас не имеет смысла, вместо того, чтобы сказать: «Ммм, не понимаю, ну ладно, идем дальше».

Таким образом, это сообщение в блоге - способ сделать это.

Я ненавидел упражнения «После полуночи», когда делал их на Ruby. И почему-то они мне нравятся еще меньше на JS. (Почему я не могу все время просто работать со строками ?!) Вот почему я собираюсь написать это сообщение в блоге, объясняющее первое упражнение после полуночи.

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

После полуночи, часть 1 - Начало разрушения решения

Для всех инструкций и т. д. студентов LS вы можете получить доступ к объяснению проблемы здесь.

  • Задача: принять целое число, положительное или отрицательное, и превратить его во время, которое является (а) строкой, (б) правильно отформатировано, (в) военным временем (г) временем «прошедшее». " полночь

Прежде чем мы приступим к исправлению решения для использования объекта Date, давайте разберем данное решение.

  • На line 1 мы объявляем постоянную глобальную переменную MINUTES_PER_HOUR и присваиваем ее Number со значением 60. В этом есть смысл, потому что нам нужно знать, сколько минут в часе, и лучше не делать это магическим числом без ярлыка.
  • На line 2 мы объявляем постоянную глобальную переменную HOURS_PER_DAY и присваиваем ее Number со значением 24. Имеет смысл ... по той же причине, что и выше.
  • На line 3 мы объявляем постоянную глобальную переменную MINUTES_PER_DAY и назначаем ее результату умножения значений HOURS_PER_DAY и MINUTES_PER_HOUR. Таким образом, его значение равно 1440. Имеет смысл…
  • На lines 5-15 мы используем объявление функции для определения функции timeOfDay. Он принимает один параметр: deltaMinutes. Имя этого параметра, если оно сбивает с толку, можно мысленно заменить на «elapsedMinutes». Итак, если переданный аргумент равен 3, прошло 3 минуты. Если он равен -3, значит, прошло 3 минуты с отрицательным знаком, что означает, что мы возвращаемся во времени с начальной точки на 3 минуты.

Теперь проследим за вызовом нашей функции из line 27 через функции.

  • line 6 мы переназначаем deltaMinutes на результат deltaMinutes % MINUTES_PER_DAY. Этот оператор просто возвращает остаток от деления первого операнда на второй. Таким образом, deltaMinutes теперь результат 35 % 1440, который равен 35.
  • На lines 7-9 у нас есть условие if / else. Если новые deltaMinutes отрицательны, нам нужно переназначить их снова на результат некоторого добавления. В нашем случае блок переназначения даже не выполняется, потому что 35 больше 0.

Давайте сделаем здесь паузу. Кажется, lines 6–9 довольно бесполезны, не так ли? Ведь мы просто переназначили значение с 35 на… 35. Так зачем это нужно? Два возможных сценария:

  1. Если данный аргумент больше 1440 (количество минут в день), нам нужно преобразовать его, чтобы мы могли использовать его должным образом. 25 часов (или 1500 минут) после полуночи не переносят нас в новое измерение времени. Сейчас всего час ночи.
    ›И это именно то, что нам покажет эта операция: 1500 % 1440 === 60, или 60 минут после полуночи, или 1:00. Таким образом, эта операция сохраняет deltaMinutes как есть, если он работает, и исправляет его, если он не работает.
  2. Если данный аргумент отрицательный (который все еще сохранит свою отрицательность после line 6), то нам нужно преобразовать его, чтобы мы могли использовать его должным образом. -3 минуты просто означает 3 минуты до полуночи.
    ›Мы могли бы вычесть абсолютное значение отрицательных минут из количества минут в дне (1440–3) или добавить отрицательные минуты к количеству минут в дне (1440 + (-3)). Это дает тот же результат.
    ›Мы выбрали второй вариант кода на line 8. Если повернуть часы назад на 3 минуты от полуночи, получится тот же циферблат, что и прибавление [24 часа минус три минуты] к полуночи.

Таким образом, после lines 6-9, даже если наш аргумент был слишком большим или слишком маленьким, у нас есть работоспособное значение для deltaMinutes

  • На line 11 мы объявляем новую локальную переменную hours и назначаем ее возвращаемому значению некоторой математической конфигурации.
    ›Аргумент, переданный Math.floor, является возвращаемым значением (в нашем случае) 35 / 60. Это дает нам десятичную дробь.
    Math.floor принимает это десятичное число и округляет его до ближайшего целого числа, равного 0. Таким образом, часы равны 0.
    В этом есть смысл: мы идем вперед только 35 minutes, что меньше одного часа.
  • На line 12 мы объявляем новую локальную переменную minutes и назначаем ее возвращаемому значению deltaMinutes % MINUTES_PER_HOUR.
    ›Помните, % в JavaScript дает нам остаток от этого деления. 35 не входит в 60 чисто, даже один раз, поэтому все остальное.
    Что снова имеет смысл, потому что 35 минут меньше одного часа / 60 минут. Таким образом, минут 35.

Таким образом, у нас есть правильные часы и правильные минуты. Теперь нам нужно его правильно отформатировать.

  • На line 14 возвращаем правильное время. Мы используем отдельную функцию для добавления нулей для заполнения, когда это необходимо, но затем делаем : часть этого явно сами.

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

  • На lines 17-25 мы используем объявление функции для определения функции padWithZeroes, которая принимает два параметра: number и length.
    ›В нашем случае внутри вызова функции number равно 0, а length равно 2.
    Интересный факт: padWithZeroes доступен для использования до того, как он будет объявлен из-за подъема.
  • На line 18 мы объявляем новую локальную переменную numberString и назначаем ее возвращаемому значению String(number).
    ›Это возвращает преобразованную в строку версию number. Таким образом, в нашем случае numberString теперь присвоено '0'.
  • На lines 20-22 мы используем цикл while, чтобы добавить необходимые 0 к numberString. Помните, в настоящее время у нас есть '0', но мы хотим превратить его в '00'.
    ›Хотя длина numberString (которая в настоящее время равна 1) меньше целевой суммы (которая равна 2), мы добавляем ноль к переднему концу строки путем переназначения (поскольку вы не можете изменять строки в JS).
    ›Это происходит только один раз, и у нас есть то, что нам нужно: '00'.
    Мы могли бы встроить это в функцию padWithZeroes, чтобы иметь целевую длину 2, вместо того, чтобы делать ее отдельным параметром. ; тем не менее, это хорошая идея сделать это таким образом, если мы хотим повторно использовать функцию padWithZeroes по-другому, когда мы имеем в виду другую длину. Это также проясняет цель функции.
  • Возвращаясь к line 14, мы видим, что тогда мы вернемся: '00:35', что… именно то, что мы хотим.

Были сделаны! 🙌

Антракт

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

Это напоминание себе о том, что если я просто замедлю и возьму его по частям, скорее всего, это будет намного легче понять. Вперед!

После полуночи, часть 1 - Разрушение данного решения

Вместо того, чтобы проходить процесс создания решения, я собираюсь разбить данное решение. Это решение использует объект Date.

Первое замечание: функция padWithZeroes точно такая же, поэтому мы пропустим объяснение этой части. Мы сосредоточимся на различиях. Кроме того, в нашем примере мы по-прежнему будем передавать 35 в качестве аргумента для вызова функции.

  • У нас больше нет трех верхних констант для минут в час, часов в день и минут в день.
  • Вместо этого на line 1 объявлена ​​одна константа, которая не требует пояснений. MILLISECONDS_PER_MINUTE представляет именно это (1 секунда составляет 1000 миллисекунд)

Обратите внимание, что функция timeOfDay намного короче, и мы просто объявляем множество новых констант. Давайте присмотримся.

  • В определении функции timeOfDay на line 4 мы устанавливаем нашу «начальную позицию»: полночь. Часть 1 января 2000 года здесь неактуальна, потому что мы просто смотрим на время. Но чтобы создать новый объект Date, нам нужно предоставить эти детали.
  • На line 5 мы объявляем новую локальную переменную afterMidnight и устанавливаем для нее результат некоторой конфигурации с объектом Date. Давайте разберемся с этим.
    new Date: мы создаем новый объект Date
    midnight.getTime(): наша отправная точка - время от объекта Date, созданного на одну строку выше
    + deltaMinutes * MILLISECONDS_PER_MINUTE: мы добавляем до этой полуночной начальной точки deltaMinutes (в нашем случае 35), умноженное на 60000.
    ›Таким образом, наш afterMidnight закреплен за 2000–01–01T00:35:00.000Z.
  • На lines 6 & 7 мы используем Date методы объекта getHours() и getMinutes(), которые делают именно это, передаем их в padWithZeroes и вуаля. Они отформатированы.
  • На line 9 мы возвращаем часы и минуты в правильном формате с : между ними.

Почему миллисекунды?

Мы видим, как использование объекта Date значительно упростило это решение. Но зачем нам понадобились миллисекунды? Разве это не случайность?

Ну, так оно и есть. Согласно MDN:

Объекты JavaScript Date представляют отдельный момент времени в независимом от платформы формате. Объекты Date содержат Number, который представляет миллисекунды с 1 января 1970 года по всемирному координированному времени.

Это не имеет отношения к нашему текущему варианту использования, но интересный факт об объекте Date:

Следует отметить, что максимальное значение Date отличается от максимального безопасного целого числа (Number.MAX_SAFE_INTEGER равно 9,007,199,254,740,991). Вместо этого в ECMA-262 определено, что может быть представлено максимум ± 100000000 (сто миллионов) дней относительно 1 января 1970 г. по всемирному координированному времени (то есть 20 апреля 271821 г. до н.э. ~ 13 сентября 275760 г. н.э.) стандартным объектом Date (что эквивалентно ± 8 640 000 000 000 000 миллисекунд).

Из-за использования миллисекунд форматирование объекта Date (в полезный формат) требует некоторой преднамеренности. Например, если я хочу узнать текущую дату, я могу использовать Date.now(). Однако все, что мне дает, - это количество миллисекунд, прошедших с 1 января 1970 года (без учета дополнительных секунд).

Запуск Date.now() дает мне это: 1603044899619. Не очень читабельно. Вот почему такие Date методы, как getHours(), getFullYear(), getTime(), getMonth() и т. Д., Так удобны. Они делают за вас весь синтаксический анализ и преобразование. Но чтобы правильно прибавить или вычесть время, нам нужно делать это за миллисекунды.

Вот почему нам пришлось преобразовать минуты после (или до) полуночи в миллисекунды, умножив количество минут на количество миллисекунд в минуте (помните 35 * 60000?).

Надеюсь, вы нашли это полезным; Я уверен. И даже если вам не потребовалась помощь в этом упражнении, я надеюсь, что вы возьмете эту идею с собой, чтобы опереться на сложные части. Означает ли это, что вам нужно писать в блоге слишком длинные сообщения о каждом упражнении, которое вызывает у вас проблемы? Нет, не совсем. Это просто означает, что вам нужно потратить время, чтобы по-настоящему понять.

Даже если эта одна программная деталь никогда не появится в будущем, необходимость настойчивости и мастерства всегда будет.