В 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. Так зачем это нужно? Два возможных сценария:
- Если данный аргумент больше 1440 (количество минут в день), нам нужно преобразовать его, чтобы мы могли использовать его должным образом. 25 часов (или 1500 минут) после полуночи не переносят нас в новое измерение времени. Сейчас всего час ночи.
›И это именно то, что нам покажет эта операция:1500 % 1440 === 60
, или 60 минут после полуночи, или 1:00. Таким образом, эта операция сохраняетdeltaMinutes
как есть, если он работает, и исправляет его, если он не работает. - Если данный аргумент отрицательный (который все еще сохранит свою отрицательность после
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
?).
Надеюсь, вы нашли это полезным; Я уверен. И даже если вам не потребовалась помощь в этом упражнении, я надеюсь, что вы возьмете эту идею с собой, чтобы опереться на сложные части. Означает ли это, что вам нужно писать в блоге слишком длинные сообщения о каждом упражнении, которое вызывает у вас проблемы? Нет, не совсем. Это просто означает, что вам нужно потратить время, чтобы по-настоящему понять.
Даже если эта одна программная деталь никогда не появится в будущем, необходимость настойчивости и мастерства всегда будет.