<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Алексей Бунин</title><generator>teletype.in</generator><description><![CDATA[Иду во фронтенд. Прохожу курс Three.js Journey, попутно изучая 3D и JavaScript]]></description><image><url>https://img3.teletype.in/files/ed/3b/ed3b7aaa-ae2c-4334-b72a-df9f63726038.png</url><title>Алексей Бунин</title><link>https://blog.buninman.ru/</link></image><link>https://blog.buninman.ru/?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/buninman?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/buninman?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Wed, 06 May 2026 11:04:52 GMT</pubDate><lastBuildDate>Wed, 06 May 2026 11:04:52 GMT</lastBuildDate><item><guid isPermaLink="true">https://blog.buninman.ru/learn2302</guid><link>https://blog.buninman.ru/learn2302?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman</link><comments>https://blog.buninman.ru/learn2302?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman#comments</comments><dc:creator>buninman</dc:creator><title>Прогресс обучения_Февраль 2023</title><pubDate>Fri, 10 Feb 2023 14:33:11 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/52/06/52065412-0c8b-4241-bd21-3799c2fa7e8c.png"></media:content><category>Обучение</category><tt:hashtag>threejs</tt:hashtag><tt:hashtag>blender</tt:hashtag><tt:hashtag>kaino</tt:hashtag><tt:hashtag>substance</tt:hashtag><tt:hashtag>курс</tt:hashtag><tt:hashtag>обучение</tt:hashtag><description><![CDATA[<img src="https://img4.teletype.in/files/b0/44/b0448132-33cf-45bb-aa68-80ae754d97b6.png"></img>Давно ничего не писал, поэтому решил сделать статью с промежуточными итогами своего обучения за 2.5 месяца.]]></description><content:encoded><![CDATA[
  <p id="mMj4">Давно ничего не писал, поэтому решил сделать статью с промежуточными итогами своего обучения за 2.5 месяца.</p>
  <p id="2dTJ">Интересно было самому посмотреть на тот путь, который я прошел. В статью многое не попало, но все равно сделать можно было куда больше за это время.</p>
  <hr />
  <h2 id="SrX8"></h2>
  <h2 id="4tqh">Курс Three.js Journey</h2>
  <p id="5JTk">Это курс по созданию 3д в браузерах. За время своего отсутствия я успел пройти где-то треть курса. Курс этот очень объемный, около 70 часов видеолекций, так что треть это довольно дофига.</p>
  <p id="s3lk">Каких-то конкретных результатов или готовых работ пока нет. Изучаем разные аспекты, делаем простенькие примеры. Но скоро будем делать 3д-сцену с порталом, а к концу курса сделаем игру с полноценным физическим движком.</p>
  <p id="sV2g">А пока, вот например работа с текстурами. Текстуры в Three.js такие же как и везде. Поддерживаются и карты нормалей и прозрачность и отражения и тени. Все что нужно.</p>
  <figure id="4TJz" class="m_column">
    <img src="https://img4.teletype.in/files/b0/44/b0448132-33cf-45bb-aa68-80ae754d97b6.png" width="1600" />
  </figure>
  <hr />
  <p id="PYqV"></p>
  <p id="pT4c">А вот работа с 3д-текстом, попытался сделать логотип. Идея была сделать шарики из разных материалов, а посередине логотип дизайнера интерьеров. Думаю, если идею развить, можно сделать довольно необычный сайт-портфолио. </p>
  <p id="boMb">Но для создания такого сайта у меня тогда не хватало навыков. Как минимум логотип проще вставить 3д моделью, а не пытаться писать его 3д-текстом.</p>
  <figure id="yjYS" class="m_column">
    <img src="https://img2.teletype.in/files/99/35/99358020-9352-4195-9da1-0c05c3144058.png" width="1600" />
  </figure>
  <hr />
  <p id="W725"></p>
  <p id="4nlf">Тут мы создавали целую сцену с анимацией движения света (типа призраки). Никаких 3д моделей не подгружали, все сделано силами JavaScript.</p>
  <figure id="s7Bz" class="m_column">
    <img src="https://img1.teletype.in/files/0f/26/0f263ddd-7bec-4b3f-b45c-ed35a026998c.png" width="1600" />
  </figure>
  <hr />
  <p id="99n4"></p>
  <p id="uE34">Дальше идет работа с частицами, партиклами. Это просто облако разноцветных колечек. В качестве скриншота выглядит конечно не очень, но когда вертишь камерой внутри такого облака, это прикольно, видно объем.</p>
  <figure id="cNe1" class="m_column">
    <img src="https://img2.teletype.in/files/1c/bd/1cbda898-e8d2-4f48-a8f8-ba47b26ad3c9.png" width="1600" />
  </figure>
  <hr />
  <p id="Chgv"></p>
  <p id="umQJ">Галактика. Тоже частицы, но уже более сложное их применение. Причем можно в реальном времени менять всякие параметры, вроде количества лучей и радиус их загиба.</p>
  <figure id="kIFA" class="m_column">
    <img src="https://img2.teletype.in/files/dd/2d/dd2d517d-f99e-43ce-b383-483ec7dd6f88.png" width="1600" />
  </figure>
  <hr />
  <p id="tQcH"></p>
  <p id="SZev">А вот это уже импортированная 3д модель с полным набором текстур, динамическим освещением, HDRI-картой и рендер движком.</p>
  <figure id="SVEx" class="m_column">
    <img src="https://img4.teletype.in/files/73/ad/73ad7d47-0ecd-415e-a49e-c6d1ed98ed60.png" width="1600" />
  </figure>
  <hr />
  <p id="9P9O"></p>
  <h2 id="b19o">Курсы по Blender</h2>
  <p id="i6nZ">После урока со шлемом, я углубился в изучение 3д. Я начал, один за другим, проходить курсы по 3д от школы Кайно. На данный момент я прохожу уже 3й курс, и каждый следующий сложнее предыдущего.</p>
  <p id="8QMh">Первым я прошел бесплатный курс с каретой, его легко можно найти на Ютубе. Если оборачиваться назад, то курс довольно простой и поверхностный, но на тот момент я ничего не знал про работу в Блендере и курс оказался кстати!</p>
  <figure id="ePkR" class="m_column" data-caption-align="center">
    <img src="https://img2.teletype.in/files/9f/dc/9fdc0e1a-3a0d-4d91-985d-539b3f2114ed.png" width="1920" />
    <figcaption>Я доделал его 10 января,а потом практически полностью пересобрал 27-го, чтоб добавить деталей и оптимизировать модель для выгрузки в веб</figcaption>
  </figure>
  <hr />
  <p id="qfp2"></p>
  <p id="X566">Затем реклама мне предложила пройти марафон и я согласился. После кареты, это уже было лишнее, марафон совсем для новичков.</p>
  <p id="kAui">Тем не менее я его прошел, а чтобы было интереснее, я делал полностью свою 3д модель, оставив только основную идею - домик в лесу.</p>
  <figure id="V57D" class="m_column" data-caption-align="center">
    <img src="https://img3.teletype.in/files/2b/e6/2be6f6bd-81ce-4ccf-a95e-306e8f7a5219.png" width="1920" />
    <figcaption>Я доделал его 15 января</figcaption>
  </figure>
  <hr />
  <p id="9f1N"></p>
  <p id="PVQe">Следующим был курс с названием &quot;2.0&quot;, прошел его залпом за 2.5 дня. На нем делается высокополигональная модель самолета и есть простая работа с текстурами.</p>
  <p id="1cAZ">Прошел его просто потому, что в более старшем курсе есть отсылки на этот курс. Авторы рассчитывают что ты будешь проходить их один за другим. В противном случае я бы его пропустил. Толку в нем не много.</p>
  <figure id="FAAy" class="m_column">
    <img src="https://img2.teletype.in/files/11/66/1166ec14-1490-4550-9a97-72ee251d03d7.png" width="1920" />
  </figure>
  <figure id="03nM" class="m_column" data-caption-align="center">
    <img src="https://img2.teletype.in/files/1d/4f/1d4f13d7-483d-4a1a-ab6e-bbb2dc443bff.png" width="1920" />
    <figcaption>Я доделал его 20 января</figcaption>
  </figure>
  <hr />
  <p id="akRZ"></p>
  <p id="M2Ck">Сейчас прохожу курс &quot;Геймдев&quot;, большой и комплексный курс. Сначала создается низкополигональная модель (лоуполи), потом высокополигональная модель (хайполи), потом запекание, текстуры и тп.</p>
  <p id="M2Y6">Робота делать было довольно долго и скучно, но это позволяет машинально закрепить работу с горячими клавишами и хорошо запомнить последовательность действий.</p>
  <p id="W7vy">Сейчас у меня уже полностью готова 3д-модель и я на этапе изучения программы Substance Painter, в которой будет проходить текстурирование робота и запекание текстур.</p>
  <figure id="qd3F" class="m_column">
    <img src="https://img1.teletype.in/files/8f/c1/8fc1d65f-96be-474a-aa14-65467ccf332f.png" width="1920" />
    <figcaption>Лоуполи модель я доделал 30 января, а хайполи 2 февряля</figcaption>
  </figure>
  <hr />
  <p id="yQUF"></p>
  <h2 id="36ki">Дальше</h2>
  <p id="XeAh">После завершения курса с роботом я вернусь к изучению курса Three.js Journey, и буду параллельно делать сайт, в центре которого будет карета из первого курса.</p>
  <p id="EMgu">Я уже доработал 3д модель этой кареты для импорта в веб, даже выложил &quot;набросок&quot; будущего сайта в сеть, но мне не хватает знаний о текстурировании и запекании текстур.</p>
  <figure id="LiNb" class="m_column">
    <img src="https://sun9-68.vkuserphoto.ru/impg/8VzVcNsT9Y1lbOSlgLwfi8rEEBX1Z8eVgwLWPg/dZCvAv9bv8A.jpg?size=1240x994&quality=96&sign=42b70234063212b1e472845b33664228&c_uniq_tag=1inrhloo1UdC62gwioxyMauRgqbBO8fwVBV1oiAZe3Q&type=album" width="1240" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="oSaU" data-align="center"><a href="https://kaino-wagon.vercel.app/" target="_blank">Вот тут уже можно посмотреть сайт, хотя смотреть особо нечего</a></p>
  </section>
  <hr />
  <p id="kmul"></p>
  <p id="ufsD">В целом, это все, вроде получилась интересная история, с картинками. Возможно, буду продолжать подбивать итоги недели/месяца в подобные статьи.</p>
  <p id="Z4Yn">А короткие посты я делаю в группе <a href="https://vk.ru/threejsjourney" target="_blank">ВК</a> или <a href="https://t.me/threejsjourney" target="_blank">Телеграме</a>.</p>
  <p id="QgSd"></p>
  <tt-tags id="RsUh">
    <tt-tag name="threejs">#threejs</tt-tag>
    <tt-tag name="blender">#blender</tt-tag>
    <tt-tag name="kaino">#kaino</tt-tag>
    <tt-tag name="substance">#substance</tt-tag>
    <tt-tag name="курс">#курс</tt-tag>
    <tt-tag name="обучение">#обучение</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.buninman.ru/gameonjs-01</guid><link>https://blog.buninman.ru/gameonjs-01?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman</link><comments>https://blog.buninman.ru/gameonjs-01?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman#comments</comments><dc:creator>buninman</dc:creator><title>Игры на JavaScript</title><pubDate>Sat, 03 Dec 2022 05:59:55 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/e1/54/e154b02a-f09b-4375-bbd7-b5a7c45fbf62.png"></media:content><category>Разное</category><description><![CDATA[<img src="https://img4.teletype.in/files/fa/d4/fad4360d-487e-4dc5-ba15-f052c7c12ca1.png"></img>Подборка создана просто как источник вдохновения и пример того, что можно сделать используя JavaScript.]]></description><content:encoded><![CDATA[
  <blockquote id="QhLJ">Подборка создана просто как источник вдохновения и пример того, что можно сделать используя JavaScript.</blockquote>
  <p id="Be0j"></p>
  <p id="t9au">Периодически, чтоб зарядиться желанием создавать что-то, я играю в разные небольшие игрушки, смотрю что делают другие программисты.</p>
  <p id="qjMe">Это не обязательно должны быть игры на JS, просто не сильно нагруженные механиками игры, которые вполне возможно повторить.</p>
  <hr />
  <p id="qcIv"></p>
  <h2 id="v6yJ">Keep Out! </h2>
  <p id="msFq">Начну с игры, на которую я наткнулся первой. Я тогда только начинал знакомится с программированием и библиотекой three.js, и игрушка произвела на меня сильное впечатление. </p>
  <p id="v6cU">Если разобраться, сама по себе она довольно простая, ходишь по небольшому лабиринту и бьешь разных мобов. Но сделана самобытно и в нее интересно поиграть. Проблема тут только в управлении - мне было сложно привыкнуть к резким поворотам на 90°😁</p>
  <figure id="FAQn" class="m_column">
    <img src="https://img4.teletype.in/files/fa/d4/fad4360d-487e-4dc5-ba15-f052c7c12ca1.png" width="1200" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="kZvs" data-align="center"><a href="https://www.playkeepout.com/" target="_blank">Играть</a></p>
  </section>
  <hr />
  <p id="ckcz"></p>
  <h2 id="R4R7">Gorescript</h2>
  <p id="ZAZT">Шутер от первого лица для любителей Doom или Duke Nukem 3D, я больше люблю Дюка.</p>
  <p id="zD4z">Играется очень приятно, есть всякие секретные двери с плюшками. Например, на первом уровне попробуйте зайти в центральный зал, там есть пулемет. Но сначала найдите способ опустить цветные колонны, иначе не доберетесь до вкусняшки.</p>
  <p id="eKJb">Я поиграл только в первую версию игры. которая называется Gorescript Classic, но есть еще новая версия, которая продается в Steam за недорого.</p>
  <figure id="vJ3u" class="m_column">
    <img src="https://img3.teletype.in/files/ee/21/ee21a178-b7fe-481b-ab7b-76da5aafe254.png" width="1200" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="1JJN" data-align="center"><a href="https://gorescript.github.io/classic/play/" target="_blank">Играть в первую часть</a></p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="dtWe" data-align="center"><a href="https://store.steampowered.com/app/618690/Gorescript/" target="_blank">Страничка второй части в Steam</a></p>
  </section>
  <hr />
  <p id="FwdM"></p>
  <h2 id="lVas">Heraclos</h2>
  <p id="uXnI">У парня выдался неудачный день - он стал избранным. Такое вот начало у этой игрушки, дальше сюжет рассказывать не буду, сами зацените😄</p>
  <p id="mW6D">Игра представляет собой небольшое одноразовое приключение. Кое-где сделана с огрехами, но сыграть все равно интересно и сделана она с юмором.</p>
  <figure id="HLw7" class="m_column">
    <img src="https://img1.teletype.in/files/cf/e6/cfe6c61f-f969-4234-9fe3-16e13b9b6961.gif" width="1200" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="crA0" data-align="center"><a href="https://heraclosgame.com/" target="_blank">Играть</a></p>
  </section>
  <hr />
  <p id="DRYL"></p>
  <h2 id="rcus">InterLand</h2>
  <p id="PWoH">Игрушка от гугла. Тут 4 разных аркады которые рассказывают о том как пользоваться интернетом и сервисами гугла, конечно.</p>
  <p id="qzo2">Играть в нее не интересно, но сделана хорошо и есть чему поучиться. За это она и попала в эту подборку.</p>
  <figure id="2JOl" class="m_column">
    <img src="https://img4.teletype.in/files/35/f0/35f012c0-3b8e-4a58-9fc0-6fc762bd169e.gif" width="1200" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="z9m6" data-align="center"><a href="https://beinternetawesome.withgoogle.com/interland" target="_blank">Играть</a></p>
  </section>
  <hr />
  <p id="B4CO"></p>
  <h2 id="NAKl">Screeps</h2>
  <p id="7PZw">Очень неоднозначное творение. Представляет из себя стратегию где все управление осуществляется написанием кода. Чтоб хоть что-то в ней сделать нужно хоть немного знать JavaScript.</p>
  <p id="TX94">Я пока еще не разобрался в ней, надо потратить время на чтение документации и просмотр гайдов, но я обязательно попробую в нее поиграть и напишу отдельную статью о своих успехах. По-моему, задумка крайне интересная.</p>
  <p id="Cyq5">Поиграть в тренировочный демо-режим можно бесплатно на сайте.</p>
  <figure id="yTXY" class="m_column">
    <iframe src="https://www.youtube.com/embed/ZboTgOajnGg?autoplay=0&loop=0&mute=0"></iframe>
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="9RIi" data-align="center"><a href="https://screeps.com/" target="_blank">Сайт игры</a></p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="WI5v" data-align="center"><a href="https://store.steampowered.com/app/464350/Screeps_World/" target="_blank">Страничка игры в Steam</a></p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="i366" data-align="center"><a href="https://github.com/TooAngel/screeps" target="_blank">Исходный код на GitHub</a></p>
  </section>
  <hr />
  <p id="BiL8"></p>
  <h2 id="3An5">Epic SkidMarks</h2>
  <p id="WBfX">Кольцевые гоночки. Навевают воспоминаниями о близардских Rock &amp; Roll Racing, которые я очень любил. Тут нет оружия, но зато можно легко подключиться с друзьями к любому серверу и погонять немножко.</p>
  <p id="kor0">Игра сырая и создать свой сервер у меня не получилось, но поиграть вдвоем все равно прикольно.</p>
  <figure id="38AG" class="m_column">
    <img src="https://img1.teletype.in/files/4c/67/4c672f56-a8a3-44c2-a56d-2d0fa3b3ffe4.gif" width="1200" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="uYvQ" data-align="center">Играть</p>
  </section>
  <hr />
  <p id="ExQH"></p>
  <h2 id="xDre">HexGL</h2>
  <p id="lIup">Тут нам дают что-то вроде космического корабля и мы летим по трассе несколько кругов. Основная задача - ехать бысто и не врезаться в ограждения, а то взорвемся.</p>
  <p id="6JxZ">Управление простое и понятное, но не врезаться все равно сложно. При всем моем опыте игры в Need For Speed, не взорваться я не смог😂</p>
  <figure id="co3X" class="m_column">
    <img src="https://img3.teletype.in/files/a0/61/a0614481-420e-4a5e-a666-7176f1f169cf.png" width="1200" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="g2pD" data-align="center">Играть</p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="kEUK" data-align="center">Исходный код на GitHub</p>
  </section>
  <hr />
  <p id="uZXv"></p>
  <h2 id="FKka">Diablo</h2>
  <p id="nBQR">Я прошел всю третью часть Дьябло и очень люблю эту игру, поэтому не прошел мимо этого маленького недоделанного клона первой дьяблы😅</p>
  <p id="r8jf">Тут есть только одна комната и несколько мобов, которых можно зарубить, но если есть желание, можешь продолжить разработку, быть может у тебя получится достойный убийца Дьябло!</p>
  <figure id="0exR" class="m_column">
    <img src="https://img1.teletype.in/files/ca/0a/ca0a7f44-1f34-4cf3-9c11-826a244eccd1.png" width="915" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="8JGo" data-align="center"><a href="https://mitallast.github.io/diablo-js/" target="_blank">Играть</a></p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="XUkN" data-align="center"><a href="https://github.com/mitallast/diablo-js" target="_blank">Исходный код на GitHub</a></p>
  </section>
  <hr />
  <p id="uruQ"></p>
  <h2 id="kf6d">DuckHunt</h2>
  <p id="gfnP">В детстве у меня была Dendy и вроде на ней была эта игра, но к сожалению, поиграть я мог только у знакомых, т.к. у моей Dendy не было ружья, который подключался к приставке.</p>
  <p id="F7lS">Ружья на приставках тех времен были далеки от совершенства и подстрелить утку было ой как не просто.. а бесячая собака еще и смеялась над тобой постоянно😁</p>
  <p id="ElYp">Мышкой попасть по уткам стало совсем просто, да и играть уже не так интересно, но тем не менее приятно вспомнить былые деньки.</p>
  <figure id="4tq5" class="m_column">
    <img src="https://img4.teletype.in/files/75/d8/75d87c7a-7076-4e78-b11d-c1c3e66268dc.png" width="1200" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="i7Gd" data-align="center"><a href="https://duckhuntjs.com/" target="_blank">Играть</a></p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="WAHQ" data-align="center"><a href="https://github.com/MattSurabian/DuckHunt-JS" target="_blank">Исходный код на Github</a></p>
  </section>
  <hr />
  <p id="fBiM"></p>
  <h2 id="iDlU">Circus</h2>
  <p id="2GOy">Еще одна приставочная игра из детства. Основная задача - прыгать через кольца и препятствия, и при этом не поджарить льва. Это намного сложнее чем кажется и мелким я ее не сильно любил. Вообще не любил😀</p>
  <p id="RnCz">Я вставил игры с приставки в подборку потому, что они лучше большинства игр которые сейчас есть в сервисах типа Google.Play и Яндекс.Игры. И если сделать клон какой-то из игр, но со своим &quot;сюжетом&quot; и графикой, уже будет классно!</p>
  <figure id="qzLL" class="m_column">
    <img src="https://img1.teletype.in/files/84/ba/84baca27-4ac1-45e9-9639-cce67806717e.gif" width="915" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="jjgs" data-align="center">Играть</p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="GkFW" data-align="center">Исходный код на GitHub</p>
  </section>
  <hr />
  <p id="JuEq"></p>
  <h2 id="spasibo_esli_dochitali___">Спасибо, если дочитали ( ´･･)ﾉ(._.&#x60;)</h2>
  <p id="WJCL">Если ты знаешь классные игры на JavaScript, в которые можно поиграть, пиши про них в комментариях. Соберем еще одну подборку!</p>
  <p id="tFv0">Или может ты помнишь какую-то игру из детства, которую можно воссоздать?</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.buninman.ru/pong</guid><link>https://blog.buninman.ru/pong?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman</link><comments>https://blog.buninman.ru/pong?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman#comments</comments><dc:creator>buninman</dc:creator><title>Игра Понг на JavaScript</title><pubDate>Mon, 14 Nov 2022 02:16:04 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/ec/5a/ec5aa8f6-0b4c-4c04-a1bd-5b8f71c0f5f4.png"></media:content><category>Обучение</category><description><![CDATA[<img src="https://img1.teletype.in/files/c0/ba/c0ba3d85-a7fd-4aee-be24-5e86a8aa2345.gif"></img>Весь код моего Понга с комментариями к каждой строчке]]></description><content:encoded><![CDATA[
  <blockquote id="8yiv">Весь код моего Понга с комментариями к каждой строчке</blockquote>
  <p id="640N"></p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Y4AM">⚠ Эту статью также можно <a href="https://habr.com/ru/post/697870/" target="_blank">прочитать на Хабре</a></p>
  </section>
  <blockquote id="rOz6">Это моя первая статья вышедшая на Хабре. Менее чем за неделю она собрала почти 4000 просмотров и 47 добавлений в избранное. Я очень рад такому результату! А еще в читатели на Хабре указали мне на то, что комментарии к функциям должны быть написаны сверху, поэтому тут выходит уже исправленный вариант.</blockquote>
  <p id="4VWx"></p>
  <p id="e2RM">Я был очень вдохновлен созданием <a href="https://blog.buninman.ru/snake" target="_blank">Змейки</a>. Даже не смотря на то, что она является просто повторением чужого гайда, из-за того я проанализировал в ней каждую строчку кода, мне удалось почерпнуть немало полезных знаний и навыков. Я также смог сам внести интересные изменения в ее код.</p>
  <p id="mlZY"></p>
  <h3 id="wpXy">Повторил</h3>
  <p id="cYhy">К повторению Понга я приступил сразу же и это не заняло много времени. Но на этапе анализа стало ясно, что код очень сложный для понимания, все слишком переплетено и запутано. Еще недавно я бы подумал, что это я не все понимаю, но сейчас я уже видел как это можно сделать лучше.</p>
  <p id="wSJo"></p>
  <h3 id="joyb">Переосмыслил</h3>
  <p id="XTRg">Я писал этот Понг с нуля, взяв из исходника только некоторые функции и концепции. Полностью поменял структуру, добавил модуль с настройками, отдельно вынес все функции рисования и многое другое.</p>
  <p id="x1Q4">Учитывая мой совсем маленький опыт программирования, чуть меньше 2 месяцев, я очень горжусь проделанной работой.</p>
  <p id="Y4MT"></p>
  <h3 id="riNe">Расписал</h3>
  <p id="ZTWl">В статье я постарался расписать каждую функцию и изложить ход своих мыслей. Это заняло прилично времени, но возможно кому-то зайдет такой гайд, лично мне иногда мне не хватало описаний логики функций, пояснений того, что куда передается и зачем.</p>
  <p id="KWZn">Буду рад, если подскажите как можно улучшить код или найдете и укажите на ошибки в комментариях, даже орфографические🙂</p>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="STrY">Гитхаб с моим Понгом: <a href="https://github.com/Buninman/Pong" target="_blank">https://github.com/Buninman/Pong</a></p>
  </section>
  <p id="UxPc">А вот <a href="https://youtu.be/tHnPWQKX-wE" target="_blank">видео-гайд</a> изначального пинг-понга от автора, и его <a href="https://github.com/EpicLegend/ping-pong" target="_blank">код на гитхабе</a></p>
  <p id="G8Lw"></p>
  <section style="background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="FGVl">Кстати, оригинальная игра 1972 года называется Pong, а пинг-понг. Хотя идеей-прародительницей для нее действительно был настольный теннис.</p>
    <p id="ek6M">Что примечательно, Понг была первой коммерчески успешной видеоигрой.</p>
  </section>
  <figure id="KIau" class="m_column">
    <img src="https://img1.teletype.in/files/c0/ba/c0ba3d85-a7fd-4aee-be24-5e86a8aa2345.gif" width="800" />
  </figure>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="QUf9" data-align="center"><a href="https://buninman.github.io/Pong/" target="_blank">Тут можно поиграть в готовый Понг</a></p>
  </section>
  <hr />
  <p id="O1rH"></p>
  <h2 id="ayxU" data-align="center">Начало</h2>
  <p id="8C7h"></p>
  <figure id="MXgP" class="m_column">
    <img src="https://img1.teletype.in/files/c5/8d/c58dea63-c7a9-4bf2-a84b-94c621bbd893.png" width="1920" />
  </figure>
  <p id="1OO0">Набросал схему, чтоб было понятнее, что куда экспортируется. Но на этот раз мы пойдем не по порядку схемы, а немного по-другому. Сначала рассмотрим те модули, которые используются везде, такие как файл настроек и рисование.</p>
  <ol id="A4Xz">
    <li id="JfKq"><a href="#OEBg">setting.js</a></li>
    <li id="B7W3"><a href="#k6Ov">canvas.js</a></li>
    <li id="WUc5"><a href="#WlQe">printer.js</a></li>
    <li id="l6FL"><a href="#dvEw">game.js</a></li>
    <li id="S9OV"><a href="#xQFs">ball.js</a></li>
    <li id="MqwH"><a href="#UcJA">player.js</a></li>
    <li id="SJsH"><a href="#tW70">index.html</a></li>
    <li id="sXzC"><a href="#jDR0">style.css</a></li>
  </ol>
  <p id="iF9I"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="OEBg" data-align="center">setting.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="5i8w">Модуль настроек. Я сложил сюда все значения которые используются в игре, кроме надписей и размеров шрифтов. Доступ к экземпляру настроек есть у всех модулей и он всегда будет присвоен переменной <code>set</code>.</p>
    <pre id="QfPg" data-lang="javascript">export default class Setting {
  constructor() {
    // Высота и ширина игрового поля, и всех слоев канваса
    this.boxWidth = 800
    this.boxHeight = 500
    // Радиус закругления углов игрового поля
    this.boxRound = 20
    // Серый цвет заливки игрового поля
    this.boxColor = &#x27;#333333&#x27;
    // Толщина линий
    this.lineWidth = 6
    // Цвет линий. Темно-серый, как основной фон окна брузера в CSS
    this.lineColor = &#x27;#232323&#x27;
    // Светло-серый цвет, используется для текста таймера и бегунка
    this.textColor = &#x27;#EBEBEB&#x27;
    // Вспомогательные красный и желтый цвета,
    // используются для подсветки технических нюансов
    this.supportColorRed = &#x27;#FA0556&#x27;
    this.supportColorYellow = &#x27;#FAC405&#x27;</pre>
    <hr />
    <p id="zTYg">Далее идут все параметры мячика. В отдельный объект вынесены те параметры, которые изменяются в процессе игры.</p>
    <pre id="QfPg" data-lang="javascript">    // Скорость мячика
    this.ballSpeed = 7
    // Радиус мячка, диаметр получается 16px
    this.ballRadius = 8
    // Координаты мячика по умолчанию,
    // равны половине длины и ширины поля, это центр
    this.ballXDefault = (this.boxWidth / 2)
    this.ballYDefault = (this.boxHeight / 2)
    // Цвет мячика, я сделал таким же светло-серым как textColor
    this.ballColor = &#x27;#EBEBEB&#x27;
    // Счетчик отбитых мячей, также к нему привязано увеличение
    // скорости мячика. Увеличиваем скорость при каждом ударе
    this.ballHitScore = 0
    
    this.ball = {
      // Текущие координаты мячика, которые меняются в процессе
      // игры, изначально они равны дефолтным - мячик в центре поля
      x: this.ballXDefault,
      y: this.ballYDefault,
      // Ускорение мячика по осям. Изначально равно 0, но
      // позже получает рандомное значение от 0.8 до 1, с + или - 
      dx: 0,
      dy: 0,
      // Еще одно значение скорости, оно нужно, т.к. скорость мяча
      // постепено растет и переодически надо ее возвращать
      // к дефолтному this.ballSpeed
      speed: this.ballSpeed
    }</pre>
    <hr />
    <p id="nxpx">Дальше идут параметры обоих игроков. В отдельные объекты вынесены параметры уникальные для каждого игрока. Мы будем передавать эти отдельные объекты при создании экземпляра класса <em><a href="#UcJA">Player</a></em></p>
    <pre id="QfPg" data-lang="javascript">    // Радиус игрока. Правильнее было бы назвать толщиной, т.к.
    // игрок это векторная линия. Но в расчетах столкновений, 
    // я использую крайние точки как окружности, поэтому это радиус.
    // Реальная толщина игрока - это два его радиуса, 14px.
    this.playerRadius = 7        
    // Высота игрока. Растояние от верхней до нижней точки игрока.
    // Но т.к. платформы игроков имеют закругления, реальный размер
    // получается на два радиуса больше, 94px
    this.playerHeight = 80
    // Скорость игрока. Используется как коэффициент для ускорения
    this.playerSpeed = 8
    // Пространство от краев игрока до стенки сверху и снизу
    this.playerBorder = this.playerRadius * 3
    // Пространство от центра платформы игрока до стенки за ним
    this.playerSpace = this.playerRadius * 6
    // Изначальная координата игрока Y равна половине высоты
    // игрового поля минус половина высоты игрока, таким образом
    // игрок будет распологаться посередине при любой заданной длине
    this.playerYDefault =
                   (this.boxHeight / 2) - (this.playerHeight / 2)
    this.playerL = {
      // Счетчик очков
      score: 0,
      // Координата Х для появления надписей &quot;+1&quot; на игровом поле.
      // Для левого игрока равна ширине поля минус 2 растояния
      // до игрока. Таким образом она находится на поле противника,
      // справа
      goalPointX: this.boxWidth - this.playerSpace * 2,
      // Параметр определяет выравнивание текста &quot;+1&quot;.
      // Я также использую этот параметр для определения стороны
      // в которую полетит мяч после забития гола
      align: &#x27;right&#x27;,
      // Кординаты игрока. X равен заданному растоянию playerSpace,
      // а Y дефолтному значению, общему для обоих игроков
      x: this.playerSpace,
      y: this.playerYDefault,
      // Зарезервированная переменная со значением Y по умолчанию
      yDefault: (this.boxHeight / 2) - (this.playerHeight / 2),
      // Цвет игрока. Оранжевый
      color: &#x27;#A55F02&#x27;,
      // Создаем массив с парами ключ-значение.
      // Номер клавиши и строка
      // с направлением. Узнать номер клавиш можно тут:
      // https://puzzleweb.ru/javascript/char_codes-key_codes.php
      keys: [[87,&#x27;up&#x27;], [83,&#x27;down&#x27;]],  
    }
    this.playerR = {
      score: 0,
      goalPointX: this.playerSpace * 2,
      align: &#x27;left&#x27;,
      // Координата X для правого игрока равна всей ширине поля,
      // минус заданное растояние playerSpace
      x: this.boxWidth - (this.playerSpace),
      y: this.playerYDefault,
      // Цвет игрока. Голубой
      color: &#x27;#38887A&#x27;,
      keys: [[38,&#x27;up&#x27;], [40,&#x27;down&#x27;]],
    }
  }
}</pre>
  </section>
  <blockquote id="LOYl" data-align="center"><a href="#ayxU">Наверх</a></blockquote>
  <p id="7PYi"></p>
  <p id="HPqM"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="gCmF" data-align="center">canvas.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="3JaW">Модуль можно использовать в других проектах, практически в неизменном виде, т.к. он содержит непосредственные функции рисования примитивов на 2D-канвасе, которые в свою очередь используются модулем <em><a href="#bexA">printer.js</a></em>.</p>
    <p id="x4iN">Для начала рассмотрим конструктор класса <em>Canvas:</em></p>
    <pre id="tinL" data-lang="javascript">export default class Canvas {
  constructor(setting) {
    // Передаем переменной set общие настройки
    this.set = setting
    // Создаем элемент canvas и переменную для доступа к нему
    this.canvas = document.createElement(&#x27;canvas&#x27;)
    // Создаем в канвасе 2d-контекст, нужен для рисования фигур
    this.ctx = this.canvas.getContext(&#x27;2d&#x27;)
    // Задаем канвасу высоту и ширину
    this.canvas.width = this.set.boxWidth
    this.canvas.height = this.set.boxHeight
    // Находим в html тег game (id=&quot;game&quot;) и как дочерний эллемент
    // создаем в нем наш canvas
    document.querySelector(&#x27;#game&#x27;).appendChild(this.canvas)
  }</pre>
    <hr />
    <p id="vUhp">Рисование текста. В него передается сам текст, координаты, размер текста, цвет, выравнивание и выравнивание по базовой линии (координате <code>y</code>)</p>
    <pre id="b9Im" data-lang="javascript">  drawText(text, x, y, fontSize, color = this.set.textColor, 
                            align = &quot;center&quot;, baseline = &#x27;middle&#x27;) {
    // Указываем цвет заливки
    this.ctx.fillStyle = color
    // Указываем шрифт и атрибуты
    this.ctx.font = &#x60;bold ${fontSize} &#x27;Fira Mono&#x27;, monospace&#x60;
    // Указываем выравнивание по краю
    this.ctx.textAlign = align
    // Указываем выравнивание по базовой линии
    this.ctx.textBaseline = baseline
    // Пишем текст, передаем туда строку с текстом
    // и ккординаты начальной точки
    this.ctx.fillText(text, x, y)
  }</pre>
    <hr />
    <p id="JKOr">С помощью трех функций ниже мы рисуем игровое поле, которое состоит из прямоугольника с закругленными краями, круга посередине и трех линий.</p>
    <p id="onlx">Помимо игрового поля, линией мы рисуем игрока, а круг также используется для мячика.</p>
    <figure id="lqK0" class="m_original">
      <img src="https://img2.teletype.in/files/96/13/9613590c-019e-4682-806f-5b94ea14ed20.png" width="1320" />
    </figure>
    <pre id="0vH7" data-lang="javascript">  drawLine(xS, yS, xF, yF, lineWidth, color) {
    // Указываем, что линия будет с закруглениями на концах
    this.ctx.lineCap = &#x27;round&#x27;
    // beginPath() начинает вектор
    this.ctx.beginPath() 
    // Аргументами указываем координаты начальной точки линии
    this.ctx.moveTo(xS, yS)
    // Аргументами  указываем координаты конечной точки линии
    this.ctx.lineTo(xF, yF)
    // Указываем толщину линии, ее мы также передаем аргументом
    this.ctx.lineWidth = lineWidth
    // Указываем цвет обводки
    this.ctx.strokeStyle = color
    // Рисуем обводку (линию)
    this.ctx.stroke()
    // Завершем создание вектора
    this.ctx.closePath()
  }
  
  drawRectangleRound(x, y, width, height, radius, color) {
    // beginPath() начинает вектор
    this.ctx.beginPath()
    // Указываем координаты начальной точки линии
    this.ctx.moveTo(x + radius, y)
    // Указываем координаты следующей точки линии
    this.ctx.lineTo(x + width - radius, y)
    // Указываем координаты точки, до куда будет идти закругление
    this.ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
    // Указываем координаты следующей точки линии и т.д
    this.ctx.lineTo(x + width, y + height - radius)
    this.ctx.quadraticCurveTo(x + width, y + height,
                                     x + width - radius, y + height)
    this.ctx.lineTo(x + radius, y + height)
    this.ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
    this.ctx.lineTo(x, y + radius)
    this.ctx.quadraticCurveTo(x, y, x + radius, y)
    // Завершем создание вектора
    this.ctx.closePath()
    // Указываем цвет заливки
    this.ctx.fillStyle = color
    // Создаем заливку
    this.ctx.fill()
  }
  
  drawCircle(x, y, radius, fillColor, stroke = true) {
    // beginPath() начинает вектор
    this.ctx.beginPath()
    // Создаем арку. Агругументами выступают координаты
    // центра окружности, радиус, начальный угол в радианах
    // и конечный угол в радианах.
    // Math.PI*2 это число Пи умноженное на 2, дает замкнутый круг
    this.ctx.arc(x, y, radius, 0, Math.PI * 2)
    // Указываем цвет заливки
    this.ctx.fillStyle = fillColor
    // Создаем заливку
    this.ctx.fill()
    // Если нам не нужна обводка, то аргументам мы передаем false,
    // а по умолчанию обводка есть
    if (stroke) {
      // Указываем толщину линии
      this.ctx.lineWidth = 6
      // Указываем цвет обводки
      this.ctx.strokeStyle = this.set.lineColor
      // Рисуем обводку
      this.ctx.stroke()
    }
    // Завершем создание вектора
    this.ctx.closePath()
  }</pre>
    <hr />
    <p id="rb9T">В основе рисования круга функцией <code>drawCircle() </code>используется метод <code>arc()</code> в котором используется значение угла в радианах. Про радианы на википедии есть <a href="https://ru.wikipedia.org/wiki/Радиан" target="_blank">понятная статья</a> с картинкой, если интересно. ༼ つ ◕_◕ ༽つ</p>
    <p id="viv7">Функция <code>drawArc()</code>,  также использует в основе своей метод <code>arc()</code>, но тут мы не замыкаем дугу, а оставляем ее небольшой кусочек. Можно было бы обойтись одной функцией, но нужно было бы передавать в нее больше аргументов. Поэтому для таймера я сделал отдельную функцию.</p>
    <pre id="OQxD" data-lang="javascript">  drawArc(radius, sAngle, eAngle, color = this.set.textColor) {
    const centerW = (this.set.boxWidth / 2)
    const centerH = (this.set.boxHeight / 2)
    
    // lineCap определяет то что обводка будет закругляться на конце
    this.ctx.lineCap = &#x27;round&#x27;
    this.ctx.beginPath()
    this.ctx.arc(centerW, centerH, radius, sAngle, eAngle)
    this.ctx.lineWidth = 6
    this.ctx.strokeStyle = color
    this.ctx.stroke()
    this.ctx.closePath()
  }</pre>
    <hr />
    <p id="PcGe">Функция отчищает канвас. Используется в метод <code>clearRect()</code>, который принимает начальные и конечные точки прямоугольника, который надо отчистить. В данном случае мы чистим канвас целиком.</p>
    <pre id="RZfY" data-lang="javascript">  clear() {        
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
  }
}</pre>
  </section>
  <blockquote id="Zdcv" data-align="center"><a href="#ayxU">Наверх</a></blockquote>
  <p id="Hr1i"></p>
  <p id="12Mj"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="bexA" data-align="center">printer.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="05cQ">Модуль работает напрямую с функциями канваса. В нем создаются сразу несколько канвасов, которые выступают слоями и перерисовываются независимо друг от друга. </p>
    <p id="EtVB">Все внутренние переменные в функциях объявлены только для удобства читаемости и компактности кода в гайде.</p>
    <p id="cvCi">Для начала рассмотрим конструктор класса <em>Printer:</em></p>
    <pre id="GEpe" data-lang="javascript">// В файл подключаем класс Canvas
import Canvas from &#x27;./canvas.js&#x27;

export default class Printer {
  // В принтер передаем только настройки
  constructor(setting) {    
    // Передаем общие настройки переменной set,
    this.set = setting
    // а переменной ball настройки мячика, для удобства
    this.ball = setting.ball
    // Создаем новый Map с пятью классами Canvas под разные нужды,
    // порядок будущих слоев влияет на их видимость, первый будет 
    // перекрывать вторым и т.д.
    this.canvas = new Map([
      // На слое background рисуется неподвижные и необновляемые
      // элементы игры. Игровое поле
      [&#x27;background&#x27;, new Canvas(this.set)],
      // Слой для отрисовки счета игроков
      [&#x27;score&#x27;, new Canvas(this.set)],
      // Слой для впомогательных функций, не нужен для игры
      [&#x27;support&#x27;, new Canvas(this.set)],
      // Дополнительный слой для разных задач
      [&#x27;other&#x27;, new Canvas(this.set)],
      // Слой для текста, появляющегося на экране
      [&#x27;text&#x27;, new Canvas(this.set)],
      // Слой для игровых элементов с постоянной перерисовкой,
      // таких как мячик и игроки
      [&#x27;gamelayer&#x27;, new Canvas(this.set)]
      ])
    // Для сокращения ширины кода, помещаю пути обращения
    // к Canvas в переменные, можно этого не делать
    this.bgCan = this.canvas.get(&#x27;background&#x27;)
    this.scoreCan = this.canvas.get(&#x27;score&#x27;)
    this.supCan = this.canvas.get(&#x27;support&#x27;)
    this.othCan = this.canvas.get(&#x27;other&#x27;)
    this.txtCan = this.canvas.get(&#x27;text&#x27;)
    this.gameCan = this.canvas.get(&#x27;gamelayer&#x27;)
    
    // Переменная ballDirectionAngle нужна передачи значения угла,
    // под которым требуется рисовать белый бегунок, отображающий    
    // направление броска мячика
    this.ballDirectionAngle = 0
  }</pre>
    <hr />
    <hr />
    <p id="eLDb">Функция ниже рисует игровое поле, это фон с закругленными краями и разные темные линии. Рисуется на специальном слое канваса - &#x27;background&#x27;.</p>
    <figure id="wft2" class="m_original">
      <img src="https://img4.teletype.in/files/72/7f/727f0a41-a08a-4066-9e91-87f5fba467dd.png" width="1320" />
    </figure>
    <pre id="KeDE" data-lang="javascript">  drawBackground() {        
    // Высота и ширина игрового поля и всех слоев канваса
    const width = this.set.boxWidth        
    const height = this.set.boxHeight        
    // Радиус закругления углов игрового поля 
    const boxRound = this.set.boxRound        
    // Цвет заливки игрового поля         
    const boxColor = this.set.boxColor        
    // Толщина и цвет линий             
    const lineW = this.set.lineWidth        
    const lineColor = this.set.lineColor        
    // Пространство от центра платформы игрока до стенки за ним
    const plSpace = this.set.playerSpace        
    // Пространство от краев игрока до стенки сверху и снизу
    const plBorder = this.set.playerBorder        
    
    // Рисуем основной прямоугольник игрового поля с закруглениями
    this.bgCan.drawRectangleRound(0, 0, width, height,
                                               boxRound, boxColor)
    // Рисуем вертикальную линию посередине
    this.bgCan.drawLine((width / 2), 0, (width / 2),
                                           height, lineW, lineColor)
    // Рисуем круг посередине, с радиусом в 1/4 высоты поля
    this.bgCan.drawCircle((width / 2), (height / 2),
                                    (height / 4), boxColor)
    // Рисуем 2 линии под игроками с отступами от края
    this.bgCan.drawLine(plSpace, plBorder, plSpace,
                             (height - plBorder), lineW, lineColor)
    this.bgCan.drawLine((width - plSpace), plBorder,
          (width - plSpace), (height - plBorder), lineW, lineColor)
  }</pre>
    <hr />
    <p id="ygja">Функция <code>drawBriefing()</code> рисует инструкцию с клавишами управления. Рисую ее на слое для игры &#x27;game&#x27;, т.к. при первоначальном отчете он не перерисовывается и мы можем им воспользоваться.</p>
    <p id="WUkf">К моменту вызова <code>drawBriefing()</code> у нас уже будут нарисованы игроки с помощью функции <code>drawPlayer()</code>, о ней ниже.</p>
    <figure id="YVVv" class="m_original">
      <img src="https://img4.teletype.in/files/34/91/3491e556-e7b0-4b0c-8a75-de95873c81a0.png" width="1320" />
    </figure>
    <pre id="rpfE" data-lang="javascript">  drawBriefing() {
    // Цвета левого и правого игроков
    const plLColor = this.set.playerL.color
    const plRColor = this.set.playerR.color
    // Координаты x и y для текста с инструкцией
    // Немного сложно, но все координаты отталкиваются от статичных
    // значений и масштабируются с игровым полем
    const controlXL = (this.set.playerSpace * 2)
    const controlXR = this.set.boxWidth - (this.set.playerSpace * 2)
    const controlY = (this.set.boxHeight / 17)
    
    // Рисуем текст инструкций. Для каждого игрока свой цвет.
    // Параметры &#x27;left&#x27; и &#x27;right&#x27; отвечаю за выравнивание текста
    this.gameCan.drawText(&#x27;keys:&#x27;, controlXL , (controlY * 8),
                                         &#x27;15px&#x27;, plLColor, &#x27;left&#x27;)
    this.gameCan.drawText(&#x27;W and S&#x27;, controlXL, (controlY * 9),
                                         &#x27;20px&#x27;, plLColor, &#x27;left&#x27;)
    this.gameCan.drawText(&#x27;keys:&#x27;, controlXR, (controlY * 8),
                                        &#x27;15px&#x27;, plRColor, &#x27;right&#x27;)
    this.gameCan.drawText(&#x27;Arrows&#x27;, controlXR, (controlY * 9),
                                        &#x27;20px&#x27;, plRColor, &#x27;right&#x27;)
  }</pre>
    <hr />
    <p id="Egt8">Функция <code>drawScore()</code> отвечает за визуальное отображение текущего счета на игровом поле. Рисуется на специальном слое &#x27;score&#x27;.</p>
    <figure id="jOhg" class="m_original">
      <img src="https://img2.teletype.in/files/1b/b4/1bb4a14f-3d1d-4f97-a181-edd046c68ed0.png" width="1320" />
    </figure>
    <pre id="utEX" data-lang="javascript">  drawScore() {
    // Цвета левого и правого игроков
    const plLColor = this.set.playerL.color
    const plRColor = this.set.playerR.color
    // Значение количества очков каждого игрока
    const plLScore = this.set.playerL.score
    const plRScore = this.set.playerR.score
    // Координаты x и y для отбражения очков игроков
    // отталкиваются от статичных значений ширины и высоты поля
    const scoreXL = (this.set.boxWidth / 9 * 4)
    const scoreXR = (this.set.boxWidth / 9 * 5)
    const scoreY = (this.set.boxHeight / 20)
    
    // Рисуем текст счета. Для каждого игрока свой цвет.
    // Параметр &#x27;left&#x27; рисует текст справа от точки координат,
    // а параметр &#x27;right&#x27;, наоборот, слева.
    // Таким образом цифры счета никогда не слипнутся (⊙_⊙)
    this.scoreCan.drawText(plLScore, scoreXL, scoreY, &#x27;40px&#x27;,
                                         plLColor, &#x27;right&#x27;, &#x27;top&#x27;)
    this.scoreCan.drawText(plRScore, scoreXR, scoreY, &#x27;40px&#x27;,
                                          plRColor, &#x27;left&#x27;, &#x27;top&#x27;)
  }</pre>
    <hr />
    <p id="wDUE">Функции ниже проверяют какое направление для мяча выбрано и запускают цикл рисования белой полосочки бегающей по кругу. Вызывается <code>drawBallDirection()</code> из модуля <a href="#dvEw"><em>game.js</em></a>, при старте каждого матча.</p>
    <p id="xC0M">В основе рисования этого &quot;белого бегунка&quot; используется метод <code>arc()</code> в котором используется значение угла в радианах. Про радианы на википедии есть <a href="https://ru.wikipedia.org/wiki/Радиан" target="_blank">понятная статья</a> с картинкой, если интересно. ༼ つ ◕_◕ ༽つ</p>
    <figure id="rQcw" class="m_original">
      <img src="https://img3.teletype.in/files/a3/aa/a3aa50e8-f5d4-491c-bc74-2e4f46d45288.gif" width="1200" />
    </figure>
    <pre id="4eBt" data-lang="javascript">  // В функцию передается переменная int, которая содержит
  // корректировщик коээфициента значения угла. Она может иметь
  // значение 2 для таймера после гола и 4 для начального таймера
  drawBallDirection(int = 2) {   
    // Ось координат канваса начинается сверху слева, поэтому
    // если направление мячика по X больше 0, то он летит вправо,
    // если направление мячика по Y больше 0, то он летит вниз     
    // следовательно здесь мячик летит по диагонали вправо вниз
    if (this.ball.dx &gt; 0 &amp;&amp; this.ball.dy &gt; 0) {
      // Я подобрал необходимые коэффициенты для       
      // значений угла окружности, в котором бегунок 
      // должен будет остановиться
      
      // В зависимости от направления полета мячика
      // передаем нужное значение во внешнюю переменную
      this.ballDirectionAngle = 6.3
    }
    if (this.ball.dx &lt; 0 &amp;&amp; this.ball.dy &gt; 0) {
        this.ballDirectionAngle = 6.8
    }        
    if (this.ball.dx &lt; 0 &amp;&amp; this.ball.dy &lt; 0) {
        this.ballDirectionAngle = 7.3
    }
    if (this.ball.dx &gt; 0 &amp;&amp; this.ball.dy &lt; 0) { 
        this.ballDirectionAngle = 7.8
    }
    // Запускаем цикл рисования белого бегунка,        
    // передаем в него значение угла минус значение переменной int.
    // Я нарочно увеличил значения углов на 2 оборота окружности,
    // чтоб при вычитании значения оставались положительными.
    // В противном случае появляются погрешности¯\_(ツ)_/¯
    this.loopBallDirection(this.ballDirectionAngle - int) 
  }
  
  // Функция представляет собой цикл, перерисовывающий бегунок,
  // пока он не достигнет нужного угла на окружности  
  loopBallDirection(someAngle) {    
    // Радиус окружности такой же, как радиус круга на игровом поле
    const rad = (this.set.boxHeight / 4)        
    // При первом вызове функции переменной angle присваивается   
    // значение угла, полученного из drawBallDirection(),
    // но в последствии оно берется из цикла
    let angle = someAngle        
    
    // Рисуем часть окружности через функцию канвасе      
    this.othCan.drawArc(rad, Math.PI * angle - 0.3, Math.PI * angle)
    // С помощью встроенной в JS функции setTimeout(),        
    // делаем задержку в 0.06 секунд, или &#x27;60&#x27; милисекунд        
    setTimeout(() =&gt; {
      angle += 0.1 
      if(angle &lt;= this.ballDirectionAngle) {                
      this.clear(&#x27;other&#x27;)                
      this.loopBallDirection(angle)            
      }        
    }, 60)
  }</pre>
    <hr />
    <p id="hkwM">Первая функция ниже является посреднической, она используется для рисования текста по центру игрового поля, а две другие функции уже используют ее внутри себя для рисования различного текста на поле.</p>
    <figure id="IuXJ" class="m_original">
      <img src="https://img3.teletype.in/files/62/3a/623ac7ea-5e68-496b-8da4-c104eb5b6bf8.png" width="1320" />
    </figure>
    <pre id="1wxb" data-lang="javascript">  // Функция рисует текст в центре экрана, по умолчанию использует    
  // белый цвет и &#x27;90px&#x27; размер шрифта, это счетчик стартового таймера
  centerText(text, fontSize = &#x27;90px&#x27;, color = this.set.textColor) {
    // Координаты центра игрового поля
    const centerW = (this.set.boxWidth / 2)
    const centerH = (this.set.boxHeight / 2)
    
    this.txtCan.drawText(text, centerW, centerH, fontSize, color)
  }
  
  // Функция рисует счетчик ударов по цетру экрана.
  // Можно было обойтись одной фунцией centerText(), но так понятнее
  drawBallHit() {
    // Используем цвет линий, чтоб счетсчик не отвлекал внимание
    this.centerText(this.set.ballHitScore, &#x27;70px&#x27;, this.set.lineColor)
  }
  
  // Функция вызывается из экземпляра Player и принимает координату Х,
  // цвет игрока и выравнивание
  drawGoal(x, color, align) {
    // рисуем &quot;+1&quot; на поле проигравшего. Цветом забившего игрока
    this.txtCan.drawText(&#x27;+1&#x27;, x, this.ball.y, &#x27;20px&#x27;, color, align)
    // Рисуем надпись &quot;Goal&quot; в центре. Цветом забившего игрока  
    this.centerText(&#x27;Goal!&#x27;, &#x27;50px&#x27;, color)
    // Через 0.8 сукунд отчищаем слой &#x27;text&#x27;
    setTimeout(() =&gt; {
      this.clear(&#x27;text&#x27;) }, 800)
  }</pre>
    <hr />
    <p id="GZLL">Функции рисуют мячик и игрока. Мячик это круг, а игрок это линия с обводкой и закругленными краями.</p>
    <figure id="0zJd" class="m_original">
      <img src="https://img2.teletype.in/files/95/a5/95a5066d-b094-44ee-9e93-53806c21fb0e.png" width="1320" />
    </figure>
    <pre id="Wnc2" data-lang="javascript">  // Рисует мячик используя его текущее местоположение
  drawBall() {    
    let ballX = this.ball.x
    let ballY = this.ball.y
    let radius = this.set.ballRadius
    let color = this.set.ballColor
    
    this.gameCan.drawCircle(ballX, ballY, radius, color, false)
  } 
  
  // Рисуем игрока используя его текущее местоположение.
  // Принимает координату Х и две координаты Y, верхнюю и нижнюю.
  // Рисует между ними линию. Также принимает толщину линии
  // и цвет игрока
  drawPlayer(xS, yS, yF, lineWidth, color) {
    this.gameCan.drawLine(xS, yS, xS, yF, lineWidth, color)
  }</pre>
    <hr />
    <p id="wD9m">Функция очистки канваса. Как аргумент принимает название слоя канваса который надо почистить и вызывает для него метод <code>clear()</code>.  </p>
    <pre id="S54s" data-lang="javascript">  // Функция отчищает нужный канвас    
  // Передаем в нее имя нужного слоя в виде &#x27;строки&#x27; текста
  clear(canvas) {
    this.canvas.get(canvas).clear()    
  }</pre>
    <hr />
    <p id="xLgt">Функции ниже нужны только для проверки игровых механик, они делают видимыми внутренние механизмы игры, такие как &quot;тень&quot; игроков, желтая зона и возможные направления вылета мячика</p>
    <figure id="Cmlh" class="m_original">
      <img src="https://img2.teletype.in/files/dc/b2/dcb2dd14-9cae-4aac-8073-35df1c4de6d7.png" width="1320" />
    </figure>
    <pre id="EaOR" data-lang="javascript">  // Функция рисует фактический размер игрока.
  // В движении зона отбития платформы увеличивается
  drawShadowPlayer(xS, yS, yF) {    
    const color = this.set.supportColorYellow
    const plWidth = (this.set.playerRadius * 2)
  
    this.supCan.drawLine(xS, yS, xS, yF, plWidth, color)
  }
  
  // Если мячик находится в желтой зоне (перед игроком),
  // то он отскакивает от координаты X игрока. Тут мы подсвечиваем
  // эту желтую зону
  drawYellowZone(x, yS, yF) {    
    const color = this.set.supportColorYellow
    const center = (this.set.boxWidth / 2)

    this.supCan.drawLine(x, yS, center, yS, 1, color)
    this.supCan.drawLine(x, yF, center, yF, 1, color)
  }
  
  // Функция подсвечивает все направления вылета мяча,
  // нужны были для расчета угла остановки белого бегунка
  drawAngleZone() {    
    const color = this.set.supportColorRed
    const radius = (this.set.boxHeight / 4)
    
    this.supCan.drawArc(radius, Math.PI * 0.2, Math.PI * 0.3, color)
    this.supCan.drawArc(radius, Math.PI * 0.7, Math.PI * 0.8, color)
    this.supCan.drawArc(radius, Math.PI * 1.2, Math.PI * 1.3, color)
    this.supCan.drawArc(radius, Math.PI * 1.7, Math.PI * 1.8, color)
    }
  }</pre>
  </section>
  <blockquote id="e6w6" data-align="center"><a href="#ayxU">Наверх</a></blockquote>
  <p id="nW2Z"></p>
  <p id="e2Tm"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="dvEw" data-align="center">game.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Lh2S">На этот модуль ссылается <a href="#tW70">index.html</a> отвечает за запуск самой игры. В нем создаются почти все классы и запускается цикл отрисовки кадров анимации. Он также является хабом, через который модули взаимодействую друг с другом.</p>
    <p id="eIfU">Для начала рассмотрим конструктор класса <em>Game:</em></p>
    <pre id="jRm0" data-lang="javascript">// В файл подключаем Настройки, Игрока, Мячик и Принтер
import Setting from &#x27;./setting.js&#x27;
import Player from &#x27;./player.js&#x27;
import Ball from &#x27;./ball.js&#x27;
import Printer from &#x27;./printer.js&#x27;

class Game {
  constructor() {        
    // Передаем переменной set общие настройки
    this.set = new Setting()
    // В Printer передаем настройки
    this.print = new Printer(this.set)
    // В Ball передаем весь класс Game
    this.ball = new Ball(this)
    // Создаем два класса Player и передаем туда весь класс Game, 
    // а также отдельно настройки каждого игрока
    this.playerL = new Player(this, this.set.playerL)
    this.playerR = new Player(this, this.set.playerR)
    // Переменная &quot;requestId&quot; служит для запуска и остановки анимации.
    // Меняя значение на false, мы сможем останавливать анимацию
    this.reqId = true        
    // Инициируем первый запуск игры
    this.firstLaunch()
  }</pre>
    <hr />
    <p id="e1Wa">Функция <code>firstLaunch()</code> отвечает только за первый запуск и срабатывает один раз при запуске. Функции которые она вызывает внутри себя будут подробнее расписаны внутри своих классов.</p>
    <pre id="s8Na" data-lang="javascript">  firstLaunch() { 
    // Рисуем игровое поле
    this.print.drawBackground()
    // Вспомогательные функции. Вызвав ее здесь, мы увидим
    // 4 возможных направления для полета мячика
    this.support()
    // Рисуем игроков
    this.playerL.draw()
    this.playerR.draw()
    // Рисуем цифры счета, они пока равны 0   
    this.print.drawScore()
    // Функция drawBriefing() рисует инструкцию по управлению  
    this.print.drawBriefing()
    // dropBall() выбирает рандомное направление для мячика 
    this.ball.dropBall()
    // drawBallDirection(4) проверяет какое направление для мяча 
    // выбрано и запускает цикл рисования белой полосочки, 
    // бегающей по кругу
    this.print.drawBallDirection(4)
    // Рисуем цифру 3. Передаем значение в метод класса Print
    this.print.centerText(&#x27;3&#x27;)

    setTimeout(() =&gt; {
      // Отчищаем слой с цифрой 3 и рисуем цифру 2
      // с помощью встроенной в JS функции setTimeout(),
      // делаем задержку выполнений блока {} кода в 0.8 секунд, 
      // или &#x27;800&#x27; милисекунд
      this.print.clear(&#x27;text&#x27;),
      this.print.centerText(&#x27;2&#x27;) }, 800)
    setTimeout(() =&gt; {
      // Каждое следующее действие происходит еще на &#x27;800&#x27; милисекунд
      // позднее предыдущего 
      this.print.clear(&#x27;text&#x27;),
      this.print.centerText(&#x27;1&#x27;) }, 1600)
    setTimeout(() =&gt; {
      // Стираем цифру и пишем &quot;Go&quot;
      this.print.clear(&#x27;text&#x27;),
      this.print.centerText(&#x27;Go&#x27;)}, 2400)
    setTimeout(() =&gt; {
      // На последней функции отчищаем слои и запускаем игру.
      // В функцию start() передаем переменную this.reqId, 
      // значение которой изначально стоит true
      this.print.clear(&#x27;text&#x27;),
      this.print.clear(&#x27;other&#x27;)
      this.start(this.reqId) }, 3200)
    }</pre>
    <hr />
    <p id="X7ER">Далее две функции создающие постоянный цикл анимации. Тут используется функция <em><code>requestAnimationFrame()</code></em>, о ней подробно можно прочитать в <a href="https://developer.mozilla.org/ru/docs/Web/API/window/requestAnimationFrame" target="_blank">документации</a>, она перерисовывает кадр.</p>
    <pre id="1BUA" data-lang="javascript">  // Функция запускает анимацию, при условии что переданная
  // переменная равна true  
  start(reqId) {
    if (reqId) {
      // Если reqId = true, то метод requestAnimationFrame()
      // вызвает указанную функцию для обновления данных перед
      // следующим перерисовыванием 
      this.reqId = requestAnimationFrame((t) =&gt; this.timeLoop(t))
    }
  }
  
  timeLoop(t) { 
    // Отчищаем игровой слой, это нужно чтоб игроки и мяч
    // не оставляли за собой след из предыдущих отрисовок
    this.print.clear(&#x27;gamelayer&#x27;)
    // Функции обновления мячика и игроков, они, в свою очередь, 
    // вызывают все нужные функции внутри своих классов
    this.ball.update()
    this.playerL.update()
    this.playerR.update()
    // Вспомогательные функции. Вызвав ее здесь, мы увидим
    // границы желтых зон, от которых зависит направление
    // в котором отбивается мячик от платформы
    this.support()
    // Снова вызываем start() вызывая зацикленность анимации.
    // В качестве значения передаем requestId, он содержит
    // метод requestAnimationFrame() и выдаст true
    this.start(this.reqId)
  }</pre>
    <hr />
    <p id="IZb3">Функция <code>reStart()</code> отвечает за остановку анимации и перезапуск матча. Ее задачи частично схожи с функцией <code>firstLaunch()</code>.</p>
    <p id="7KNz">Эта функция срабатывает при забитии гола и в нее передается свойство <code>align</code> того игрока, который забил мяч.</p>
    <pre id="ggc1" data-lang="javascript">  reStart(align) {
    // присваиваем reqId значение false, это останавливает анимацию
    this.reqId = false        

    // Делаем задержку в 0.8 секунд, и выполняем следующее:    
    setTimeout(() =&gt; {        
      // Отчищаем игровой слой
      this.print.clear(&#x27;gamelayer&#x27;)
      // Возвращаем игрокам и мячику значения позиций по умолчанию
      this.playerL.defaultSet()            
      this.playerR.defaultSet()            
      this.ball.defaultSet()            
      // Снова рисуем игроков и мячик, уже в стартовых позициях   
      this.playerL.draw()
      this.playerR.draw()
      this.ball.draw()
      // Вспомогательные функции. Вызвав ее здесь, мы увидим
      // 4 возможных направления для полета мячика   
      this.support()            
      // dropBall() выбирает рандомное направление для мячика 
      // Значение align укажет направление броска в забившего
      // предыдущий гол. Рандомость будет заключатся только в 
      // напрвлении вверх или вниз
      this.ball.dropBall(align)            
      // Функция запускает белый бегунок по направлению,
      // определенному выше в dropBall()
      this.print.drawBallDirection()            
    }, 800)
    
    // Следующие действия произойдут уже через 1.6 секунды
    setTimeout(() =&gt; {
      // Отчищаем слой other, это удалит белый бегунок с экрана
      this.print.clear(&#x27;other&#x27;)
      // Снова присваиваем reqId значение true
      // и перезапускаем игровой цикл
      // Эти действия произойдут уже через 1.6 секунды,
      // после предыдущего setTimeout()
      this.reqId = true
      this.start(this.reqId)
    }, 2400)
  }</pre>
    <hr />
    <p id="i7uw">Следующая функция не влияет на работоспособность и нужна исключительно для визуального понимая работы &quot;желтой зоны&quot; и механизма отбития мячика платформами.</p>
    <p id="mXgw">Подробнее о желтой зоне в модуле <em><a href="#UcJA">Player</a></em></p>
    <pre id="MlYo" data-lang="javascript">  // Функция вызывается в firstLaunch(), timeLoop() и reStart() 
  // и запускает отрисовку всех вспомогательных функций
  support() {
    // Отчищает свой слой канваса
    this.print.clear(&#x27;support&#x27;)
    // Рисует желтые зоны игроков
    this.playerL.support()
    this.playerR.support()
    // Рисует 4 направления для мяча
    this.print.drawAngleZone()
  }
}</pre>
    <hr />
    <p id="cV8B">Функция ниже запускается первой, после загрузки <code>index.html</code>, создает класс <em>Game</em> и по факту запускает весь остальной JavaScript код</p>
    <pre id="9dYS" data-lang="javascript">// Функция создает объект Game после того как все файлы
// будут подгружены браузером
window.onload = () =&gt; {
  const game = new Game()
}</pre>
  </section>
  <blockquote id="QjkC" data-align="center"><a href="#ayxU">Наверх</a></blockquote>
  <p id="NZJ7"></p>
  <p id="mv2l"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="xQFs" data-align="center">ball.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="UNRv">Модуль отвечает за мячик. За рандомность его направления, разворот, отрисовку и т.п.</p>
    <pre id="l4q0" data-lang="javascript">export default class Ball {
  // При создании в Ball мы передали в него весь класс Game    
  constructor(Game) {  
    // Через game мы будем получать доступ в методу reStart()  
    this.game = Game
    // Передаем переменной set общие настройки
    this.set = Game.set
    // Для удобства, сразу выделим в отдельную переменную ball,
    // настройки мячика которые изменяюся по ходу игры
    this.ball = Game.set.ball
    // Переменная для доступа к модулю Printer
    this.print = Game.print
  }</pre>
    <hr />
    <p id="stMN">Две функции ниже отвечают за рандомность полета мячика. Первая выбирает величину ускорения от 0.8 до 1, а вторая выбирает направления этого ускорения - с минусом или плюсом.</p>
    <p id="25Mx">Мячик двигается путем прибавления значения ускорения к его координатам. Например, чтобы сместить мячик вправо, мы можем прибавить <code>1</code> к координате <em><code>x</code></em>, а чтобы сместить его вниз, прибавить <code>1</code> к координате <code>y</code> (ось координат начинается в верхнем левом углу). Но таким образом, мячик будет лететь строго под 45°.</p>
    <p id="GTgf">Чтоб этого не происходило есть функция <code>getRandom()</code>, она возвращает рандомное число в промежутке от 0.8 до 1. Получается, что мы прибавляем к координатам <code>x</code> и <code>y</code> разные значения и мячик никогда НЕ двигается строго под 45°. Этого практически не заметно, но такое поведение не дает мячику зацикливаться в одной траектории и делает игру более разнообразной.</p>
    <p id="dH3K">Функция <code>getRandomDirection()</code> в случайном порядке присваивает отрицательное или положительное значение результату <code>getRandom()</code>. </p>
    <pre id="hyx0" data-lang="javascript">  getRandom() {
    // Метод Math.random() генерирует случайное положительное        
    // значение в диапазоне от 0 до &lt;1,       
    // а формула Math.random() * (max - min) + min позволяет
    // получить рандомное число в нужном диапазоне от min до max  
    return Math.random() * (1 - 0.8) + 0.8        
  }
  
  getRandomDirection() {  
    // Math.random() генерирует случайное значение        
    // Math.round() округляет это значение до целого 0 или 1
    // Boolean() возвращает 0 как false, а 1 как true          
    if (Boolean(Math.round(Math.random()))) {        
      // Если Boolean() вернул true, возвращаем случайное число   
      // в нужном нам диапазоне функцией getRandom()        
      return this.getRandom()            
    // Если Boolean() вернул false, то возвращаем тоже самое,   
    // но со знаком &quot;минус&quot;
    } else { return -this.getRandom() }
  }</pre>
    <hr />
    <p id="aUkO">Следующие две функции отвечает за движение мячика.</p>
    <p id="LtVk">Так, функция <code>dropBall()</code> выбирает изначальное направление для полета мячика. Чтоб он мог полететь из центра в любой из 4 углов поля.</p>
    <p id="H7nT">Используя описанные выше функции, она генерирует рандомные значения и присваивает их переменным <code>dx</code> и <code>dy</code>, это значения ускорения для каждой из координат мячика.</p>
    <p id="JJzJ">А функция <code>move()</code> присваевает получившиеся значения <code>dx</code> и <code>dy</code> реальным координатам мячика. Ее мы будем вызывать на каждом кадре.</p>
    <pre id="hyx0" data-lang="javascript">  // Функция принимаем значение align от игрока,
  // это дает нам понять какой игрок забил гол в прошлом матче
  dropBall(player) {        
    // С помощью getRandomDirection() мы генерируем        
    // рандомное ускорение с рандомным знаком + или -  
    this.ball.dx = this.getRandomDirection()  
    this.ball.dy = this.getRandomDirection()           
    // Так как мы не знаем какое значение нагенерировано,        
    // приминяем следующее:            
    switch (player) {
      // Если значение left, то подает левый игрок,
      // значит мячик должен лететь вправо.             
      case &#x27;left&#x27;:
        // Чтоб исключить возможность полета влево  
        // Используем метод Math.abs(), который возвращает
        // положительно значение.
        // Присваеваем dx точно положительное значение dx
        this.ball.dx = Math.abs(this.ball.dx)
        break  
      // Для полета влево мы также применяем Math.abs(),  
      // но после превращения значения в положительное,       
      // делаем его отрицательным с помощью - 
      case &#x27;right&#x27;:                
        this.ball.dx = -Math.abs(this.ball.dx)
        break        
    }        
    // Если значение player не было передано, т.е оно undefined, 
    // то ничего не меняется и все значения останутся
    // полностью случайны, такое случается на первом старте игры
  }
  
  // Двигает мяч в пространсте прибавляя сгенерированные ранее
  // значения к его координатам, умножая на коэффециент скорости
  // который мы задали в настройках мячика как speed
  move() {
    // Оператор += сначала складывает исходное значение (это х)
    // и расчетное значение (результат умножения),
    // а потом присваивает полученное значение переменной х
    this.ball.x += (this.ball.dx * this.ball.speed)
    this.ball.y += (this.ball.dy * this.ball.speed)
  }</pre>
    <hr />
    <p id="kCLl">Функция <code>checkCollisionWithWalls()</code> отвечает за просчет столкновений мячика со стенками игрового поля и, в случае столкновения мяча со стенкой за игроком, вызывает функцию <code>goalProcess()</code>.</p>
    <p id="uW7D">Ось координат в JavaScript начинается с верхнего левого угла, что показано на рисунке ниже. Следовательно верхняя и левая стенки будут иметь координаты 0, а правая и нижняя равны длине и ширине поля.</p>
    <figure id="GZG4" class="m_original">
      <img src="https://img2.teletype.in/files/13/bd/13bd6e5b-33c8-4c58-8834-56952d23f6fe.png" width="1320" />
    </figure>
    <pre id="nxIU" data-lang="javascript">  checkCollisionWithWalls() {  
    // В данном случае ballX и ballY это координаты мяча в будующем,
    // которые будут на следующем кадре, но без учета скорости
    let ballX = (this.ball.x + this.ball.dx)   
    let ballY = (this.ball.y + this.ball.dy)    
    // Координата Х правой стены (ширина поля) минус размер 
    // радиуса мяча, чтоб при столкновении мяч не утопал в стену
    const rightWall = (this.set.boxWidth - this.set.ballRadius)
    // Координата Х левой стены (это 0) плюс размер радиуса мяча.
    // Просто отступ от края размером с половинку мячика   
    const leftWall = this.set.ballRadius
    // Координата Y верхней стены (также 0),
    // Просто берем радиус мячика    
    const TopWall = this.set.ballRadius    
    // Координата Y нижней стены (высота поля) минус радиус
    const BottomWall = (this.set.boxHeight - this.set.ballRadius)
    
      // Если координаты мячика стали больше координат правой стены,
      // то:
      if (ballX &gt;= rightWall) {        
        // Меняем направление по оси Х с помощью функции reverseBall()
        this.ball.dx = this.reverseBall(this.ball.dx)
        // Вызываем функцию goalProcess() которая завершает матч
        this.goalProcess(this.set.playerL)
        }
      // Если координаты мячика стали меньше координат левой стены,
      // то:          
      if (ballX &lt;= leftWall) {        
        this.ball.dx = this.reverseBall(this.ball.dx)
        this.goalProcess(this.set.playerR)
      }
      // Если мячик коснулся верхней или нижней стенки, то:  
      if (ballY &gt;= BottomWall || ballY &lt;= TopWall) {        
        // Разворачиваем мяч по оси Y  
        this.ball.dy = this.reverseBall(this.ball.dy)
      }
    }</pre>
    <hr />
    <p id="gkLe">Как следует из названия, функция <code>reverseBall() </code>разворачивает мячик по одной из оси координат. меняя значение его ускорения с отрицательного на положительное и наоборот.</p>
    <pre id="UTTR" data-lang="javascript">  // В функцию передается значение ускорения dx или dy
  reverseBall(dir) {
    // Если значение ускорения положительное, то
    if (dir &gt; 0) {
      // Возвращаем новое рандомное значение для ускорения
      // со знаком минус, т.е. отрицательное
      return -this.getRandom()
    // Если значение ускорения отрицательное, то
    } else {
      // Также возвращаем новое рандомное значение,
      // но уже положительное
      return this.getRandom()
    }
  }</pre>
    <hr />
    <p id="0cwT">Функция <code>goalProcess()</code> запускает процесс завершения матча после гола. Аргументом в нее передаются настройки игрока, который забил мяч.</p>
    <pre id="uQi0" data-lang="javascript">  // Запускает процесс завершения матча после гола.
  // Сюда передается align игрока, который забил мяч
  goalProcess(winner) {
    winner.score++
    // Прибавляем к счету забившего +1 очко,
    // отчищаем старый счет со слоя &#x27;score&#x27; и рисуем новый счет
    this.print.clear(&#x27;score&#x27;)
    this.print.drawScore()
    // Отчищаем слой &#x27;text&#x27;, чтоб удалить с центра поля
    // счетчик количества отбитий мяча в матче
    this.print.clear(&#x27;text&#x27;)
    // Обнуляем счетчик отбитий, чтоб в следующем матче
    // он пошел с нуля
    this.set.ballHitScore = 0
    // Вызываем функцию рисования Гола, она просто пишет
    // в центре надпись &quot;Goal!&quot; и рисует &quot;+1&quot; на поле соперника
    this.print.drawGoal(winner.goalPointX, winner.color, winner.align)
    // Вызываем метод reStart() класса Game, он остановит анимацию,
    // обнулит положение всех элементов и нарисует их заного
    this.game.reStart(winner.align)
  }</pre>
    <hr />
    <p id="Y0tJ">Для большего интереса, я создал функцию, которая увеличивает скорость мячика на <code>0.1</code> каждый раз, когда он отражается от платформы игрока.</p>
    <p id="D8WY">Она также прибавляет <code>1</code> к счетчику ударов мяча о платформу, так мы видим сколько раз игрокам удалось отбить мяч.</p>
    <pre id="7UZv" data-lang="javascript">  // Увеличивает скорость мяча на 0.1    
  // и прибавляет 1 к счетчику ударов мяча о платформу.    
  // Отчищает слой &#x27;text&#x27; и рисует заного  
  speedМagnifier() {  
    // Оператор += сначало прибавляет 0.1,        
    //а потом присваивает полученное значение    
    this.ball.speed += 0.1        
    // Оператор ++ прибавляет 1 к значению счетчика ударов
    this.set.ballHitScore++        
    // Отчищает слой &#x27;text&#x27; и рисует новое значение счетчика
    // на игровом поле
    this.print.clear(&#x27;text&#x27;)
    this.print.drawBallHit()
  }</pre>
    <hr />
    <p id="nZCa">Далее идут довольно простые функции. </p>
    <p id="vH7u"><code>defaultSet()</code> возвращает настройки мячика к дефолтным значениям, это требуется для перезапуска партии при забитии гола.</p>
    <p id="1w65"><code>draw()</code> просто вызывает функцию рисования мяча. Можно было обойтись без нее, вызывая рисование мяча напрямую из класса <em>Printer</em> везде где это необходимо, но так мне мой код нравится больше.</p>
    <p id="rsSZ"><code>update()</code> вызывает все функции, которые необходимо пересчитывать для отрисовки кадров, она вызывается в цикле анимации из класса <em><a href="#dvEw">Game</a>.</em></p>
    <pre id="cX2x" data-lang="javascript">  defaultSet() {
    // Ставит мячик на центр поля обнуляя координаты
    this.ball.x = this.set.ballXDefault
    this.ball.y = this.set.ballYDefault
    // Возвращает исходную скорость
    this.ball.speed = this.set.ballSpeed
  }
  
  // Функция отправляет запрос на отрисовку мячика.
  // Создана для удобства
  draw() {
    this.print.drawBall()    
  }
  
  // Функция вызывает функции проверки столкновений,
  // движения мячика и отрисовки. Создана для удобства,
  // вызывается из метода timeLoop() в классе Game
  update() {
    this.checkCollisionWithWalls()
    this.move()
    this.draw()
  }
}</pre>
  </section>
  <blockquote id="VlNJ" data-align="center"><a href="#ayxU">Наверх</a></blockquote>
  <p id="YQrM"></p>
  <p id="MATx"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="UcJA" data-align="center">player.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="nZZN">Модуль отвечает за платформу игрока, ее положение и взаимодействие с мячиком. Класса <em>Player</em> создается два, и в каждый передаются свойства разных игроков, это делается в классе <em><a href="#dvEw">Game</a></em>.</p>
    <p id="9efO">Для начала рассмотрим конструктор класса <em>Player:</em></p>
    <pre id="wLBh" data-lang="javascript">export default class Player {
  // В Player мы передали весь класс Game и отдельно свойства игрока
  constructor(Game, playerSet) {
    // Передаем переменной set общие настройки     
    this.set = Game.set
    // Для удобства, выделим в отдельную переменную ball
    // все изменяемые настройки мячика, а через classBall,
    // мы будем получать доступ к классу Ball и его методам
    this.ball = Game.set.ball
    this.classBall = Game.ball
    // Для доступа к модулю Printer
    this.print = Game.print
    // Переменной player присваиваем свойсва игрока переданные
    // в класс Player, уникальные для каждого экземпляра Player
    this.player = playerSet
    // Создаем новый Map (это коллекция ключ/значение) из keys.
    // В данном случаем ключ - это номер клавиши, 
    // а значение - это строка с направлением движения
    // Пример =&gt; keys: [[87,&#x27;up&#x27;], [83,&#x27;down&#x27;]], 
    this.keyMap = new Map(playerSet.keys)
    // Создаем два слушателя событий. В них передаем событие
    // (нажатие или отжатие клавиши) и слушателя
    document.addEventListener(&#x27;keydown&#x27;,
                             (e) =&gt; this.keyController(e, true))
    document.addEventListener(&#x27;keyup&#x27;,
                             (e) =&gt; this.keyController(e, false))
    // Две переменные, которые нужны для виртуального
    // расширения платформ во время движения, чтоб был шанс
    // отбить мяч &quot;в последний момент&quot; ╰(*°▽°*)╯
    // то как это работает, покажет функция support()
    this.shadowUp = 0
    this.shadowDown = 0
    // Переменная служит индикатором нахождения мяча в желтой зоне.
    // Это зона перед платформой.
    // Желтую зону также покажет функция support()
    this.yellowZone = true
    // Переменная служит для запрета разворота мячика, чтоб он
    // не мог менять направления чаще чем каждые полсекунды
    // используется в функции checkCollisionWithBall()
    this.ballReversStatus = true
  }</pre>
    <hr />
    <p id="m1Sr">Функции ниже занимаются движением игроков. <code>keyController()</code> отрабатывает нажатия с клавиатуры, а <code>move()</code> выполняет движения в зависимости от нажатых клавиш.</p>
    <p id="zuom">Нужно помнить, что ось координат в JavaScript начинается с верхнего левого угла, как показано на рисунке ниже. Следовательно верхняя и левая стенки будут иметь координаты 0, а правая и нижняя равны длине и ширине поля.</p>
    <figure id="RYdz" class="m_original">
      <img src="https://img4.teletype.in/files/3a/c5/3ac596b2-c2e6-4035-91f2-2b9e11f349de.png" width="1320" />
    </figure>
    <pre id="oeh1" data-lang="javascript">  keyController(e, state) {
    // Метод has() показывает существует ли элемент
    // с указанным значением в объекте
    // И если нажата клавиша, которая есть в keyMap,
    // то он выдает true
    if(this.keyMap.has(e.keyCode)) {
      // get() возвращает связанный с ключем элемент, он
      // вернет &#x27;up&#x27; или &#x27;down&#x27; в зависимости от нажатой клавиши.
      // Создаем переменную с именем результата метода get()
      // и присваиваем ей статус true или false
      this[this.keyMap.get(e.keyCode)] = state
    }
  }
  
  // Двигает платформу игрока, прибавляя 1 к его координатам,
  // умножая на коэффециент скорости
  move() {
    // Переменные созданы только для уменьшения ширины кода,
    // чтоб он влез в статью без горизонтальной прокрутки¯\_(ツ)_/¯
    const plHeight = this.set.playerHeight
    const plSpeed = this.set.playerSpeed
    const plBorder = this.set.playerBorder
    const boxHeight = this.set.boxHeight
    
    // Если this.up = true, т.е. клавиша &#x27;вверх&#x27; нажата, то
    if (this.up) {
      // Бордер это растояние от задней стены, до центра игрока.
      // На такое же растояние платформы &#x27;недоезжают&#x27; до краев поля.
      // Если растояние от Y игрока (это верхний край платформы)
      // больше чем бордер, то
      if (this.player.y &gt; plBorder) {
        // Мы уменьшаем текущую координату Y на скорость 
        // платформы игрока из настроек (по умолчанию это 10).
        // Т.е. двигаем игрока вверх на 10 пикселей
        this.player.y -= plSpeed
      // Если платформа достигла ограничения или перескочила его
      // (это возможно т.к. платформы движуться по 8 пикселей), то  
      } else {
        // Мы возвращаем платформу в последнее возможное положение,
        // на растояние бордера от края поля
        this.player.y = plBorder  
      }
      // Если this.up = true, т.е. клавиша &#x27;вверх&#x27; нажата, то
      // присваиваем переменной shadowUp двойное значение скорости
      // это 16 пикселей
      this.shadowUp = (plSpeed * 2)
    }
    // Если this.down = true, т.е. клавиша &#x27;вниз&#x27; нажата, то
    else if (this.down) {
      // Т.к. игрок это верхняя точка платформы, надо прибавить
      // длину игрока, для получения координаты нижней его точки
      if ((this.player.y + plHeight + plBorder) &lt; boxHeight) {
        // Мы увеличиваем текущую координату Y на скорость 
        // платформы игрока из настроек (по умолчанию это 10).
        // Т.е. двигаем игрока вниз на 10 пикселей
        this.player.y += plSpeed
      } else {
        // Возвращаем платформу в последнее возможное положение,
        // на растояние бордера от нижнего края поля
        this.player.y = (boxHeight - plHeight - plBorder)
      }
      // Если this.down = true, т.е. клавиша &#x27;вниз&#x27; нажата, то
      // присваиваем переменной shadowDown двойное значение скорости
      // это 16 пикселей
      this.shadowDown = (plSpeed * 2)
    // Если клавиши не нажаты, возвращаем нашу &quot;тень&quot; в ноль  
    } else {
      this.shadowUp = 0
      this.shadowDown = 0
    }
  }</pre>
    <figure id="vOUp" class="m_original">
      <img src="https://img1.teletype.in/files/cb/a5/cba5386d-6a72-41db-886d-7ecae24d6434.png" width="1320" />
    </figure>
    <hr />
    <p id="LxE8">функция <code>checkYellowZone()</code> проверяет находится ли мячик в желтой зоне перед игроком и присваивает переменной <code>yellowZone</code> значения <code>true</code> или <code>false</code> в зависимости от результата проверки. </p>
    <p id="uiMf">Если мячик был отбит платформой находясь в желтой зоне, значит он был отбит плоскостью платформы. А если он задел платформу, но не был в желтой зоне, то значит это было ребро платформы.</p>
    <pre id="vlmw" data-lang="javascript">  checkYellowZone() {
    // Длина игрока, растояние от его верхней до нижней точки  
    const plHeight = this.set.playerHeight
    
    // Если Y мячика больше (мячик ниже) верхней точки игрока
    // и Y мячика меньше (мячик выше) нижней точки игрока
    if (this.ball.y &gt; (this.player.y - this.shadowUp)
    &amp;&amp; this.ball.y &lt; (this.player.y + plHeight + this.shadowDown)) {
      // Значит мячик находится перед игроком, в желтой зоне
      this.yellowZone = true
    // Если нет, то не в желтой ¯\_(ツ)_/¯  
    } else {
      this.yellowZone = false
    }
  }  </pre>
    <hr />
    <p id="49aq">Функция <code>checkCollisionWithBall()</code> отвечает за столкновение мяча с платформой. Она вычисляет разницу между координатами <code>x</code> и <code>y</code> объектов, а затем вычисляет их фактическое расстояние <code>d</code>. И если сумма радиусов объектов меньше, чем расстояние между ними, значит имело место столкновение этих объектов и можно производить нужные нам действия, с помощью функции <code>hitBall()</code>.</p>
    <p id="1zzk">Формулу расчета столкновений я взял из <a href="https://habr.com/ru/post/487962/?ysclid=l9qra0gr6r515789840" target="_blank">этой</a> статьи на хабре.</p>
    <figure id="kGNA" class="m_original">
      <img src="https://img2.teletype.in/files/9f/f5/9ff5e2bb-a63a-461d-b1d9-ecae32a51f20.png" width="1320" />
    </figure>
    <pre id="KLX7" data-lang="javascript">  checkCollisionWithBall() {
    // Длина игрока, растояние от его верхней до нижней точки
    const plHeight = this.set.playerHeight
    // Вычисляем разницу между координатами X мячика и Y игрока
    let dx = this.ball.x - this.player.x
    // Разница между Y мячика и Y игрока (верхним краем игрока),
    // также учитываем тень, если она есть
    let dy = this.ball.y - (this.player.y - this.shadowUp)
    // Разница координаты Y мячика и нижнего края игрока,
    // с учетом его тени, если она есть
    let dyF =this.ball.y -(this.player.y + plHeight + this.shadowDown)
    // Сумма радиусов мячика и платформы
    let radSum = this.set.ballRadius + this.set.playerRadius
    // Растояние от центра мячика, до края платформы.
    // Math.sqrt() вычисляет квадратный корень, а
    // Math.pow() возводит значение dx в степень 2 (в квадрат)
    let dY = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
    // Растояние от центра мячика, до нижнего края платформы
    let dYF = Math.sqrt(Math.pow(dx, 2) + Math.pow(dyF, 2))
    // Убрал из расчета кординату Y, чтобы видеть столкновение
    // координат Х независимо от положения мячика по Y.
    // Это надо для расчета удара о плоскость платформы
    let dX = Math.sqrt(Math.pow(dx, 2))
    
    // Если растояние между центрами объектов меньше суммы их
    // радиусов (в данном случае это Х мячика и Х платформы), то
    if (dX &lt;= radSum) {
      // Если мячик находится в желтой зоне
      // и он не менял направление последние полсекунды, то
      if (this.yellowZone &amp;&amp; this.ballReversStatus) {
        // Вызываем функцию hitBall() для разворота мяча
        // и передаем в нее только значение dx, т.к в желой зоне
        // мячик отбивается от плоскости платформы только по оси Х
        this.hitBall(this.ball.dx)
      }
    }
    // Если ускорение мячика положительное, т.е. мячик летит вниз, то
    if (this.ball.dy &gt; 0) {
      // Если есть столкновение с верхним краем платформы, то
      if (dY &lt;= radSum) {
        // Если мячик не в желтой зоне, то
        if (!this.yellowZone) {
          // Вызываем функцию hitBall() для разворота мяча по обои
          // осям, мячик развернутся на 180°
          this.hitBall(this.ball.dx, this.ball.dy)
        }
      }
    }
    // Если ускорение мячика отрицательное, т.е. мячик летит вверх, то
    if (this.ball.dy &lt; 0) {
      // Если есть столкновение с нижним краем платформы, то
      if (dYF &lt;= radSum) {
        // Если мячик не в желтой зоне, то
        if (!this.yellowZone) {
          // Вызываем функцию hitBall() для разворота мяча по обои
          // осям, мячик также развернутся на 180°
          this.hitBall(this.ball.dx, this.ball.dy)
        }
      }
    }
  }
  
  // Аргументом мы передаем только dx или dx и dy мячика
  hitBall(dx, dy) {
    // Разворачиваем мячик по оси Х с помощью метода reverseBall().
    // Он всегда разворачивается по оси Х при ударе о платформу
    this.ball.dx = this.classBall.reverseBall(dx)
    
    // Если мы передали в функцию значение dy, то
    if (dy) {
      // Разворачиваем мячик по оси Y. Это удар о ребро платформы
      this.ball.dy = this.classBall.reverseBall(dy)
    }
    // Т.к. мячик отбит платформой, запускаем функцию,
    // которая увеличит его скорость
    this.classBall.speedМagnifier()
    // Запрещаем мячику разворачиваться
    this.ballReversStatus = false
    // А через 500 милисекунд разрешаем мячику разворачиваться.
    // Такое поведение нужно, чтоб он не мог застрять в платформе
    // постоянно разворачиваясь
    setTimeout(() =&gt; {
      this.ballReversStatus = true
    }, 500)
  }</pre>
    <hr />
    <p id="puAa">Далее идут довольно простые функции. </p>
    <p id="ETY9"><code>defaultSet()</code> возвращает положение игрока к дефолтному значению, это требуется для перезапуска партии при забитии гола.</p>
    <p id="G6r4"><code>draw()</code> просто вызывает функцию рисования игрока. Можно было обойтись без нее, вызывая рисование напрямую из класса <em>Printer</em> везде где это необходимо, но так мне мой код нравится больше.</p>
    <p id="2xD6"><code>update()</code> вызывает все функции, которые необходимо пересчитывать для отрисовки кадров, она вызывается в цикле анимации из класса <em><a href="#dvEw">Game</a>.</em></p>
    <pre id="xOkb" data-lang="javascript">  // Функция обнуляет положение игрока,
  // так как X в процессе игры не меняется, а вторая координата Y
  // вычисляется из первой, достаточно обнулить только Y
  defaultSet() {
    this.player.y = this.set.playerYDefault
  }
  
  draw() {
    // Кордината верхней точки игрока
    let x = this.player.x
    let yStart = this.player.y
    // Кордината нижней точки игрока
    let yFinish = (this.player.y + this.set.playerHeight)
    // Цвет и толщина игрока (2 радиуса)
    const plColor = this.player.color
    const plWidth = (this.set.playerRadius * 2)
    
    // Т.к. игрок это векторная линия с закругленными краями,
    // передаем в принтер кординаты верхней и нижней точки,
    // толщину линии (2 радиуса) и цвет
    this.print.drawPlayer(x, yStart, yFinish, plWidth, plColor)
  }
  
  // Функция вызывает функции проверки желтой зоны, столкновений,
  // движения игрока и отрисовки. Создана для удобства.
  // Вызывается из метода timeLoop() в классе Game
  update() {
    this.checkYellowZone()
    this.checkCollisionWithBall()
    this.move()
    this.draw()
  }</pre>
    <hr />
    <p id="z8oP">Функция <code>support()</code> занимается визуальной отрисовкой желтой зоны для проверки ее работы. Вызывается из класса <em><a href="#dvEw">Game</a>.</em></p>
    <pre id="P41a" data-lang="javascript">  support() {
    const plHeight = this.set.playerHeight
    let x = this.player.x        
    let yS = this.player.y - this.shadowUp
    let yF = this.player.y + this.set.playerHeight + this.shadowDown
    
    this.print.drawShadowPlayer(x, yS, yF)
    if (this.yellowZone) {
      this.print.drawYellowZone(x, yS, yF)
    }
  }
}</pre>
  </section>
  <blockquote id="rvz1" data-align="center"><a href="#ayxU">Наверх</a></blockquote>
  <p id="65pf"></p>
  <p id="9gu7"></p>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="tW70" data-align="center">index.html</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="EneV">В теге <code>&lt;body&gt;</code> прописан <code>id=&quot;game&quot;</code> для создания канваса. Хотя можно было попробовать обойтись без него и посылать <em>canvas</em> прямо на тег <code>&lt;body&gt;</code>. Внутри тега находиться только скрипт.</p>
    <pre id="rGAj" data-lang="html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;    
  &lt;meta charset=&quot;UTF-8&quot;&gt;    
  &lt;title&gt;Pong by Buninman.ru&lt;/title&gt;    
  &lt;meta name=&quot;description&quot; content=&quot;Created by Buninman.ru&quot;&gt;    
  &lt;meta property=&quot;og:title&quot; content=&quot;The best Pong game&quot;&gt;    
  &lt;meta property=&quot;og:image&quot; content=&quot;img/pongOG.png&quot;&gt;    
  &lt;meta property=&quot;og:description&quot; content=&quot;Created by Buninman.ru&quot;&gt;  
    &lt;meta http-equiv=&quot;expires&quot; content=&quot;0&quot;&gt;    
  &lt;link rel=&quot;stylesheet&quot; href=&quot;pong/style.css&quot;&gt;    
  &lt;link rel=&quot;apple-touch-icon&quot; sizes=&quot;180x180&quot; href=&quot;img/icon.png&quot;&gt; 
  &lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; sizes=&quot;32x32&quot; href=&quot;img/fv32.png&quot;&gt;
  &lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; sizes=&quot;16x16&quot; href=&quot;img/fv16.png&quot;&gt;
&lt;/head&gt;

&lt;body id=&quot;game&quot;&gt;    
  &lt;script src=&quot;pong/game.js&quot; type=&quot;module&quot;&gt;&lt;/script&gt;
&lt;/body&gt;

&lt;/html&gt;</pre>
  </section>
  <blockquote id="Yt4F" data-align="center"><a href="#ayxU">Наверх</a></blockquote>
  <p id="aQH0"></p>
  <p id="Mm8f"></p>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="jDR0" data-align="center">style.css</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="0IAF">В css минимальный набор стилей. Мы делаем весь &lt;body&gt; одним большим флекс-боксом во весь экран.</p>
    <pre id="qbE5" data-lang="css">body {
  margin: 0px;    
  height: 100vh;    
  width: 100vw;    
  display: flex;    
  justify-content: center;    
  align-items: center;    
  background-color: #232323;    
}

#game canvas {  
  display: block;    
  position: absolute;
}</pre>
  </section>
  <blockquote id="4UtZ" data-align="center"><a href="#ayxU">Наверх</a></blockquote>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.buninman.ru/ligatures</guid><link>https://blog.buninman.ru/ligatures?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman</link><comments>https://blog.buninman.ru/ligatures?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman#comments</comments><dc:creator>buninman</dc:creator><title>Лигатуры для программистов</title><pubDate>Fri, 30 Sep 2022 09:14:12 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/cc/4e/cc4ee551-c962-4db5-9d59-cb24c4caf7b3.png"></media:content><category>Разное</category><description><![CDATA[<img src="https://img1.teletype.in/files/09/4a/094a20cb-f518-4a80-becf-f555afd9b291.png"></img>Есть на свете шрифт, до краев наполненный разными причудами, которые могут прийтись по вкусу маленьким программистам😃]]></description><content:encoded><![CDATA[
  <blockquote id="eBlw">Есть на свете шрифт, до краев наполненный разными причудами, которые могут прийтись по вкусу маленьким программистам😃</blockquote>
  <p id="U6Dh"></p>
  <p id="eKyL">Лигатурой можно назвать любой знак образованный путём соединения двух и более графем, например, вот:</p>
  <figure id="z0aM" class="m_column">
    <img src="https://img1.teletype.in/files/09/4a/094a20cb-f518-4a80-becf-f555afd9b291.png" width="1000" />
  </figure>
  <p id="NQcj">Соединяя знаки, мы получаем лигатуры. Они часто использовались в древности, для ускорения письма, в книгопечатании и тп. Сейчас лигатуры почти не встречаются, за исключением некоторых шрифтов, в которых буковки как бы слипаются😏</p>
  <p id="Idyd"></p>
  <h3 id="ayge">Причем тут программисты?</h3>
  <p id="95rM">Во всех языках программирования используются разные символы, многие из которых состоят из двух или нескольких знаков. Для лучшей читаемости, умные мужи, придумали эти знаки превращать в лигатуры, которые занимают такое же количество знаков на экране, но обладают лучше читаемостью.</p>
  <p id="tIgO">Например, знак &quot;меньше или равно&quot; &lt;= выглядит совсем не так, как мы писали его в школе, а знак &quot;не равно&quot; !==, вообще не понятен обывателю. Но использование лигатур способно это исправить.</p>
  <p id="mshR"><code>Fira Code</code> - это бесплатный шрифт, созданный специально для написания кода. Он содержит целую кучу разных лигатур и, вышеупомянутый, знак &quot;меньше или равно&quot; выглядит в нем так <code>&lt;=</code>, а вот <code>!==</code>&quot;не равно&quot;, круто?🙂</p>
  <p id="hL5i">На картинке ниже можно посмотреть некоторые примеры лигатур и то, как они преображают код. Также, тут можно заметить, что длина строк никак не меняется, знаки занимают ровно такое же количество символов, как и раньше:</p>
  <figure id="vJAs" class="m_column">
    <img src="https://img1.teletype.in/files/c9/70/c970df03-3b13-4cca-a571-d7e22d34a9f8.png" width="1000" />
  </figure>
  <p id="z9uO">К сожалению, большинство даже не знает о существовании таких лигатур. Я и сам узнал о них только потому, что Teletype принудительно использует <code>Fira Code</code> во всех вставках блоков кода. Чтобы в редакторе и в моих конспектах все было одинаково, я нашел способ установить сей шрифт в Visual Studio Code.</p>
  <p id="zRVe">Сперва было не привычно, но сейчас я привык и не собираюсь отказывать от такой фишки, мне нравится как выглядит код с лигатурами. При этом проблем с чтением чужого кода без лигатур у меня нет. Поэтому могу смело рекомендовать всем, хотя бы, в качестве эксперимента.</p>
  <p id="ovf1"></p>
  <h3 id="p6cl">Как установить?</h3>
  <p id="z04e">Сам шрифт можно свободно <a href="https://fonts.google.com/specimen/Fira+Code" target="_blank">скачать на Google Fonts</a>.</p>
  <p id="hQSh">Зачем, в настройках Visual Studio Code выбираем:</p>
  <pre id="N0ZF">File &gt;==&gt; Preferences &gt;==&gt; Settings &gt;==&gt; Text Editor &gt;==&gt; Font</pre>
  <p id="vBAR">В правой колонке, в графе &quot;Font Family&quot; пишем <code>Fira Code</code>, но проще всего сразу жмякнуть чуть ниже  &quot;Edit in setting.json&quot; =&gt; откроется небольшой файл с кодом, где нужно привести следующие строки к такому виду: </p>
  <pre id="zrae">&quot;editor.fontFamily&quot;: &quot;Fira Code&quot;,
&quot;editor.fontLigatures&quot;: true,   </pre>
  <h2 id="a4jc" data-align="center">Готово!</h2>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.buninman.ru/snake</guid><link>https://blog.buninman.ru/snake?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman</link><comments>https://blog.buninman.ru/snake?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman#comments</comments><dc:creator>buninman</dc:creator><title>Игра Змейка на JavaScript</title><pubDate>Tue, 06 Sep 2022 06:22:35 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/cf/59/cf59fa25-54c4-4b9b-bf15-7fa30a9b731e.png"></media:content><category>Обучение</category><description><![CDATA[<img src="https://img4.teletype.in/files/7c/27/7c270a37-33d6-4e93-bbf6-67cec58192f2.png"></img>Первая версия змейки появилась в 1976 году, а последняя - сегодня.]]></description><content:encoded><![CDATA[
  <blockquote id="dsPM">Первая версия змейки появилась в 1976 году, а последняя - сегодня.</blockquote>
  <p id="l4pS"></p>
  <p id="RWib">Пока проходил <a href="https://buninman.ru/js_stashchuk" target="_blank">курс по JS</a>, стало немного скучновато и я решил найти какой-нибудь гайд на ютубе, чтобы попрактиковаться.</p>
  <p id="AlmO">У автора этой змейки есть две версии: в <a href="https://youtu.be/TSdGHbI6veI" target="_blank">первом видео</a> он делает змейку одним JS файлом, а <a href="https://youtu.be/yq4x4zSSz70" target="_blank">во-втором видео</a> уже на модулях с ООП (Объектно ориентированное программирование). Я сделал оба варианта, но именно второй мне захотелось разобрать по частям, чтоб лучше понять что к чему, т.к. он был намного менее понятен чем первый, там много классов и сложная модульная структура.</p>
  <p id="SNul">Гитхаб автора со змейкой: <a href="https://github.com/EpicLegend/snake2d-opp" target="_blank">https://github.com/EpicLegend/snake2d-opp</a></p>
  <p id="2GBo">Все что будет ниже, я писал для того, чтоб просто разобрать непонятные мне части кода, но в процессе начал вносить в змейку и свои доработки. Поэтому код немного отличается от кода автора.</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="RVcH">Мой Гитхаб с моей змейкой: <a href="https://github.com/Buninman/Snake" target="_blank">https://github.com/Buninman/Snake</a></p>
  </section>
  <p id="JRlT">Буду рад на указание ошибок, даже орфографических🙂</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="pcKm">Основные изменения моей змейки:</p>
    <ul id="EeOm">
      <li id="MvBA">Поменял дизайн игрового поля, добавил вниз блок с подсказкой;</li>
      <li id="nCWp">Сделал счетчик рекорда, он запоминает наибольший накопленный результат;</li>
      <li id="EPOF">Добавил возможность менять скорость змейки клавишами PgUp, PgDwn;</li>
      <li id="YmWG">Теперь есть режим бога, в котором змейка не может себя съесть. Кнопка G;</li>
      <li id="ensi">Убрал возможность змейке разворачиваться на 180º и кусать себя. Больше она не умирает от случайного нажатия.</li>
    </ul>
  </section>
  <figure id="7IMS" class="m_column">
    <img src="https://img4.teletype.in/files/7c/27/7c270a37-33d6-4e93-bbf6-67cec58192f2.png" width="1920" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="R5Rx" data-align="center"><a href="http://bunindesign.com/snake.html" target="_blank">Тут можно поиграть в готовую змейку</a></p>
  </section>
  <hr />
  <p id="OyFa"></p>
  <p id="MMf3" data-align="center"></p>
  <h2 id="9FRF" data-align="center">Начало</h2>
  <p id="ndhY"></p>
  <figure id="tcY4" class="m_column">
    <img src="https://img1.teletype.in/files/08/44/08445e26-39bf-4c49-8dcc-eb88ee6ba965.png" width="1920" />
  </figure>
  <p id="bdqI">Набросал схему устройства приложения, чтоб было понятнее что куда идет в плане импорта объектов. По такому порядку и пойдем:</p>
  <ol id="IKTv">
    <li id="3YkN"><a href="#YXWz">supportFunction.js</a></li>
    <li id="PzJd"><a href="#ATCr">config.js</a></li>
    <li id="ckEw"><a href="#ONaS">score.js</a></li>
    <li id="1P9K"><a href="#gPHO">canvas.js</a></li>
    <li id="o49b"><a href="#JTZE">berry.js</a></li>
    <li id="DoT3"><a href="#g4Bl">snake.js</a></li>
    <li id="BS3m"><a href="#33UY">gameLoop.js</a></li>
    <li id="ZY8F"><a href="#KcCl">game.js</a></li>
    <li id="pz9b"><a href="#8JKn">snake.html</a></li>
    <li id="KZUN"><a href="#rKwa">style.css</a></li>
  </ol>
  <p id="NV7i"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="YXWz" data-align="center">supportFunction.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="gxlP">В модуле всего одна функция, подробнее о ней можно посмотреть в <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random" target="_blank">документации</a>, она целиком оттуда.</p>
    <pre id="gxlP" data-lang="javascript">export const getRandomInt = (min, max) =&gt;
    Math.floor( Math.random() * (max - min) + min )
    
    // Math.random() возвращает псевдослучайное число
    // с плавающей запятой в диапазоне от 0 до менее 1
    // (включая 0, но не 1)
    
    // Math.floor() возвращает наибольшее целое число,
    // меньшее или равное заданному числу. Округляет</pre>
    <p id="jTCH">Эта функция возвращает случайное число между указанными значениями. Возвращаемое значение не меньше (и, возможно, равно) <code>min</code>и меньше (и не равно)<code>max</code>. </p>
    <p id="RdRp"><code><em>getRandomInt()</em></code> вызывается в файле <a href="#tnaL"><em>berry.js</em></a> и имеет такой вид:</p>
    <pre id="jof7" data-lang="javascript">Для x: getRandomInt(0, this.canvas.element.width /
                       this.config.sizeCell) *
                       this.config.sizeCell
Для y: getRandomInt(0, this.canvas.element.height /
                       this.config.sizeCell) *
                       this.config.sizeCell</pre>
    <p id="AfNK">Соответственно на <code>min</code> передается <code>0</code>, а на <code>max</code> передается:</p>
    <pre id="1IHb" data-lang="javascript">Для x: ширинаПоля (688) / размерЯчейки (16) - это 43
Для y: высотаПоля (480) / размерЯчейки (16) - это 30</pre>
    <p id="1UHa">Таким образом, когда функция срабатывает, мы получаем число в диапазоне от 1 до 42 для <code>x</code> и от 1 до 49 для <code>y</code>.</p>
  </section>
  <p id="Mz5C"></p>
  <p id="Lg08"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="ATCr" data-align="center">config.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="xFgu">Этот модуль претерпел самые большие изменения, вместо 8 строк кода, теперь тут 38.</p>
    <p id="Ck5R">Значения <em>config.js</em> используются в <em><a href="#JTZE">berry.js</a></em>, <em><a href="#g4Bl">snake.js</a></em> и <em><a href="#33UY">gameloop.js</a></em></p>
    <pre id="xFgu" data-lang="javascript">export default class Config {
    constructor() {        
        this.speedBlock = document.querySelector(&#x27;#speed&#x27;)
        this.godModeColor = document.querySelector(&#x27;.game-header&#x27;)
        this.textBlock = document.querySelector(&#x27;.keys-conf&#x27;)
        // Ищем значения в HTML коде, по названиям классов и id
        // Тут мы ищем индикатор скорости, цвет плашки под ним
        // и блок текста с инструкцией
        
        this.step = 0
        this.maxStep = 6
        // step и maxStep нужны чтоб пропускать игровой цикл,
        // их использует функция в файле gameLoop.js.
        // Также maxStep регулирует скорость змейки,
        // чем выше значение тем реже перерисовывается змейка

        this.sizeCell = 16
        this.sizeBerry = this.sizeCell / 4
        // sizeCell это размер ячейки, а sizeBerry это размер ягодки
        
        this.godMode = false
        // Переменная отвечает за режим бога. Изначально он выключен

        this.colorLight = &#x27;#A55E00&#x27;        
        this.colorDark = &#x27;#5E3908&#x27;
        // Переменные с цветами змейки. Есть темный оттенок
        // и светлый, для головы змейки
        
        this.drawSpeed()
        // Метод отрисовывает значение скорости
    }
    
    speedLevel() {        
        return 10 - this.maxStep
        // Чтоб значения скорости были понятнее я сделал
        // функцию которая меняет порядок чисел, теперь 1
        // это самая медленная скорость, а 8 - самая быстрая
    }
    
    drawSpeed() {        
        this.speedBlock.innerHTML = this.speedLevel()
        // innerHTML обращается к значению в HTML
        // и меняет на текущую скорость змейки
    }
    
    drawGodMode() {        
        if (this.godMode) {  
        // Если режим бога сейчас true то: 
                
            this.colorLight = &#x27;#FA0556&#x27;
            this.colorDark = &#x27;#A00034&#x27;
            // Меняем цвета в переменных на красные.
            // Они также подхватываются змейкой
            
            this.textBlock.innerHTML = &#x27;God Mode: ON&#x27;
            // Вместо инструкции пишем о включенном режиме
            
            this.godModeColor.style.backgroundColor = this.colorDark
            // style.backgroundColor получает значение цвета
            // параметра background в css-файле.
            // Тут мы меняем цвет плашки под счетом
            
        } else {
        // Если режим бога сейчас false:
        
            this.colorLight = &#x27;#A55E00&#x27;
            this.colorDark = &#x27;#5E3908&#x27;
            // Меняем цвета в переменных на оранжевые
            
            this.textBlock.innerHTML
                 =&#x27;Use the Arrows and PageUp/PageDown&#x27;
            // Пишем стандартную инструкцию внизу поля
            
            this.godModeColor.style.backgroundColor = this.colorDark
            // Меняем цвет плашки
        }
    }
}</pre>
    <p id="zZWD">🙁Не смог сделать так, чтоб ягодка меняла цвет вместе со змейкой. Сделал ягодку белой.</p>
    <p id="FguN">Свойства <code>step</code> и <code>maxStep</code> используются в модуле <em><a href="#r0Gl">gameLoop.js</a>, </em>внутри функции <code>animate()</code>:</p>
    <pre id="fUxJ" data-lang="javascript">animate() {
    requestAnimationFrame(this.animate)
    // Вызывает повторно функцию animate()
    // так образуется непрерывный цикл
        
    if (++this.config.step &lt; this.config.maxStep) {
        return
        // Если ++step меньше maxStep, то ничего не делать.
        // Таким образом, надо 6 раз вызвать функцию,
        // чтоб змейка сделала 1 шаг
    }  
              
    this.config.step = 0
    // После движения змейки, счетчик возвращается на 0
    
    this.update()
    this.draw()
}</pre>
    <blockquote id="EwXj"><code>++</code> увеличивает значение на единицу и возвращает значение.</blockquote>
    <p id="3ftz">Оператор <code>++</code> будет прибавлять 1 к step до тех пор, пока на шестом вызове функции она не пройдет дальше. После функция обнуляет значение <code>step.</code></p>
  </section>
  <p id="CHcd"></p>
  <p id="LjaY"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="ONaS" data-align="center">score.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="fzNM">Модуль отвечает за отрисовку очков за съеденные ягодки. Сюда же я добавил функцию <code>drawRecord()</code>, которая вызывается в случае смерти змейки сохраняет наш прогресс в виде рекорда.</p>
    <p id="mNNO">Значение <em>score.js</em> используются в <em><a href="#KcCl">game.js</a></em></p>
    <pre id="fzNM" data-lang="javascript">export default class Score {
    constructor(scoreBlock, recordBlock, score = 0) {
        this.scoreBlock = document.querySelector(scoreBlock)
        this.recordBlock = document.querySelector(recordBlock)
        // Ищем значения в HTML коде, по id. Тут мы ищем
        // счетчики очков и рекорда        
        this.score = score
        // Создает свойство со значением очков
        this.draw()
        // Вызывает метод объекта draw() при создании экземпляра
    }
    
    incScore() {
        this.score++
        // Добавляет 1 к счету за съеденную ягодку
        this.draw()
        // Запускает метод draw() который поменяет значение счетчика
    }

    setToZero() {
        this.score = 0
        // Обнуляет счетчик очков. Это нужно в случае сметри
        this.draw()
        // Запускает метод draw()
    }
    
    drawRecord() {
        if (this.score &gt; this.record) {
        // Если значение очков больше чем текущий рекорд, то
            this.record = this.score
            // Меняем значение рекорда на текущее значение очков
            this.recordBlock.innerHTML = this.record
            // Меняем значение рекорда в HTML
        }    
    }
    
    draw() {        
        this.scoreBlock.innerHTML = this.score
        // innerHTML обращается к значению в HTML
        // и меняет значение счета в HTML
    }
}</pre>
  </section>
  <p id="WdUA"></p>
  <p id="ge6L"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="gPHO" data-align="center">canvas.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="e5Aq">Модуль отвечает за создание и размеры игрового поля. Я менял только размер игрового поля.</p>
    <p id="jtn2">Значение <em>canvas.js</em> используются в <em><a href="#KcCl">game.js</a></em></p>
    <pre id="6JtR" data-lang="javascript">export default class Canvas {
    constructor(container) {
        this.element = document.createElement(&#x27;canvas&#x27;)
        // .createElement создает HTML-элемент &lt;canvas&gt; в HTML-файле

        this.context = this.element.getContext(&#x27;2d&#x27;)
        // .getContext(&#x27;2d&#x27;) указывает JS что мы хотим рисовать в 2д.
        // Нам становится доступен набор свойст и методов
        // таких как beginPath(), которым нарисована ягодка
        
        this.element.width = 688
        this.element.height = 800
        // Это высота и ширина 2д контекста, т.к. одна ячейка
        // у нас 16рх, то значения должны быть кратны 16рх
        
        container.appendChild(this.element)
        // Как я понял, .appendChild создает дочерний элемент.
        // Он помещает наш созданный 2д контекст (игровое поле)
        // в &lt;canvas&gt; внутри HTML-файла
    }
}</pre>
  </section>
  <p id="CTzm"></p>
  <p id="fxxR"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="JTZE" data-align="center">berry.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="7jhK">Модуль отвечает за все что происходит с ягодкой. </p>
    <p id="OLid">Значение <em>berry.js</em> используются в <em><a href="#KcCl">game.js</a></em></p>
    <pre id="SayO" data-lang="javascript">import Config from &#x27;./config.js&#x27;
import {getRandomInt} from &#x27;./supportFunction.js&#x27;

export default class Berry {
    constructor(canvas) {
        this.x = 0
        this.y = 0
        // Это координаты ягодки        
        this.canvas = canvas
        // в Berry передаются параметры игрового поля,
        // нам нужны оттуда только высота и ширина
        this.config = new Config()
        // Передаем данные с Config, нам оттуда нужны размеры ячейки
        // и размер ягодки
        this.randomPosition()
        // Запускаем функцию получения рандомных кординат для ягодки
    }  
    
    draw(context) {
    // Функция рисования ягодки. Сюда передается наш 2д контекст
    // из класса Canvas
    
        context.beginPath()
        // Создает новый векторый контур. Начинаем рисование
        
        context.fillStyle = &#x27;#EBEBEB&#x27;
        // Заливка этого контура белым цветом. Если поменять ее
        // на динамичную переменную this.config.colorLight, то
        // цвет ягодки все равно не будет меняться
        // Я не понимаю почему🙁
        
        context.arc(this.x + (this.config.sizeCell / 2),
        // arc() создает векторную дугу. Замкнутая дуга это круг
        // В arc() нужно передать координаты x и y,
        // радиус, начальный угол и конечный угол
                    // Берем рандомную координату x, прибавляем
                    // к ней половину размера ячейки.
                    
                    this.y + (this.config.sizeCell / 2),
                    // Тоже самое для координаты y,
                    // Это дает нам положение центра ячейки
                    // Центр нашей ягодки
                    
                    this.config.sizeBerry, 0, 2 * Math.PI)
                    // Берем размер ягодки из Config, это радиус
                    // 0 - это начальный угол
                    // 2 * Math.PI - это конечный угол, такой
                    // параметр указан в учебнике по JS
                    
        context.fill()
        // Метод fill() создает заливку для нашей ягодки,
    }
    
    randomPosition() {
        this.x = getRandomInt(0, this.canvas.element.width / 
                                 this.config.sizeCell) * 
                                 this.config.sizeCell        
        this.y = getRandomInt(0, this.canvas.element.height / 
                                 this.config.sizeCell) *
                                 this.config.sizeCell
        // Функция получения рандомных координат, два раза
        // вызывает функцию генерации случайных чисел
        // и присваивает их x и y координатам
    }
}</pre>
    <p id="99IP">Функция <code>getRandomInt()</code> расписана в модуле <a href="#YXWz"><em>supportFunction.js</em></a> </p>
  </section>
  <p id="UTZo"></p>
  <p id="7Ian"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="g4Bl" data-align="center">snake.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="si1R">Модуль за все что связано со змейкой. Я добавил функцию сохранение рекорда после смерти змейки, поменял параметры старта и изменил назначение клавиш.</p>
    <p id="4K9n">Значение <em>snake.js</em> используются в <em><a href="#KcCl">game.js</a></em></p>
    <pre id="LXsD" data-lang="javascript">import Config from &#x27;./config.js&#x27;

export default class Snake {
    constructor() {
        this.config = new Config()
        // Передаем данные с Config, нам нужен размер ячейки
        this.x = 336
        this.y = 480
        // Начальные координаты змейки,
        // я сделал чтоб она появлялась внизу посередине
        this.dx = 0
        this.dy = -this.config.sizeCell
        // Начальное направление движения змейки,
        // по x - 0, а по y на -16рх, т.е. вверх.
        // Ось кординат начинается сверху слева
        // Ось y идет сверху вниз
        this.tails = []
        // Пустой массив для записи данных о хвосте змейки
        this.maxTails = 1
        // Параметр длины змейки. Я сделал по умолчанию 1,
        // т.е. только голова
        this.control()
        // Запускаем функцию отвечающую за упарвление змейкой        
    }
    
    update(berry, score, canvas) {
    // Функция обновления змейки. Сюда передаются данные ягодки,
    // счета и игрового поля
    
        this.x += this.dx
        this.y += this.dy
        // Прибавляем к координатам змейки размер движения.
        // Змейка перемещается путем прибавления или вычитания
        // размера ячейки от следующих координат положения головы
        
        if (this.x &lt; 0) {
        // Если координата x меньше 0 то
            this.x = canvas.element.width - this.config.sizeCell
            // Присваиваем ей значение ширины игрового поля
            // минус одну ячейку.
            // Т.е. если змейка доходит до края, то
            // она появится с другой стороны
            
        } else if (this.x &gt;= canvas.element.width) {
        // то, при условии, что координата x больше или равна
        // ширине игрового поля
                   this.x = 0
                   // Кордината x = 0, начало поля.
                   // Т.е. если змейка доходит до края, то
                   // она появится с другой стороны
        }

        if (this.y &lt; 0) {
            this.y = canvas.element.height - this.config.sizeCell
        } else if (this.y &gt;= canvas.element.height) {
                   this.y = 0
        // Логика с вертикальной осью такая же, но в расчет берем
        // высоту поля, а не ширину
        }
        
        this.tails.unshift({ x: this.x, y: this.y})
        // Метод .unshift() добавляет элемент в начало массива
        // с хостом. Добавляем туда объект с координатами x и y.
        // Это секция хвоста
        
        if (this.tails.length &gt; this.maxTails) {
        // Если длина массива с хвостом больше чем параметр длинны
        // хвоста, то 
            this.tails.pop()
            // Удаляем последний элемент массива
            // Метод .pop() удаляет элемент в конце массива
        }
        
        this.tails.forEach((el, index) =&gt; {
        // Метод .forEach() перебирает все элементы массива,
        // все элементы змейки, и применяет к ним функцию.
        // В функцию передаются все элементы массива и их индексы
        
            if (el.x === berry.x &amp;&amp; el.y === berry.y) {
            // Если координаты x и y в какой-то из секций змейки
            // совпадают с координатами ягоды, то
                this.maxTails++
                // Добавляем 1 к параметру длины змейки
                score.incScore()
                // Вызываем метод incScore(). Это метод нашего
                // объекта Score, он прибавляем 1 единичку к счету
                berry.randomPosition()
                // Вызываем метод randomPosition(). Это метод нашего
                // объекта Berry, он переносит ягодку на новые
                // случайные координаты
            }

            for (let i = index + 1; i &lt; this.tails.length; i++) {
            // Цикл for запускает функцию пока условие верно.
            // Переменная i это индекс элементов массива, прибавляем
            // к ней 1, чтоб игнорировать элемент 0, это голова.
            // Цикл будет продолжаться пока i меньше длинны массива
            // Каждый цикл увеличиваем i на 1
            
                if ( el.x == this.tails[i].x &amp;&amp;
                     el.y == this.tails[i].y &amp;&amp;
                     !this.config.godMode) {
                // Если координаты x и y какого элемента равны
                // каким-то другим координатам из массива, кроме
                // головы, и если режим бога НЕ включен, то
                    this.death()
                    // Метод death() возвращает все параметры
                    // на начальные, перезапускает игру
                    score.drawRecord()
                    // Метод объекта Score, записывает наш счет
                    // в рекорд, если текущий рекорд меньше
                    score.setToZero()
                    // Обнуляет счетчик очков
                    berry.randomPosition()
                    // Вызываем метод randomPosition(). Это метод
                    // объекта Berry, он переносим ягодку на новые
                    // случайные координаты
                }            
            }        
        })    
    }
            
    draw(context) {
    // Функция рисования змейки. Сюда передается наш 2д контекст
    // из класса Canvas
    
        this.tails.forEach((el, index) =&gt; {
        // Метод .forEach() перебирает все элементы массива
            if (index == 0) {
            // Если индекс элемента массива равен 0,
                context.fillStyle = this.config.colorLight
                // то красим в яркий цвет. Это голова
            } else {
                context.fillStyle = this.config.colorDark
                // Иначе красим в темный цвет, это тело змейки
            }

            context.fillRect( el.x, el.y, this.config.sizeCell,
                                          this.config.sizeCell)
            // Метод fillRect() рисует прямоугольник по двум
            // координатам и двум величинам, это размер ячейки
        }) 
    }
        
    death() {
    // Метод death() возвращает все параметры
    // на начальные, перезапускает игру 
        this.x = 336
        this.y = 480
        // Ставит змейку на нужные координаты        
        this.dx = 0
        this.dy = -this.config.sizeCell
        // Выставляет напрвление движения 
        this.tails = []
        // Обнуляем массив хвостов
        this.maxTails = 1
        // Делает параметр колическва хвостов равным 1
    }
    
    control() {        
        document.addEventListener(&quot;keydown&quot;, e =&gt; {
        // Метод addEventListener() отслеживает события
        // Первый аргумен указывает на тип события - кнопка нажата.
        // Второй агрумент указывает на &quot;слушателя&quot;, тот раздражитель
        // который должен это событие вызвать и ту функцию которое
        // это событие произведет. В нашем случае это будут кнопки
        // клавиатуры
        
            if (e.code == &#x27;ArrowUp&#x27;) {
            // Если код слушателя равен клавише &quot;Стрелка вверх&quot; то
            
                if (this.dx == 0 &amp;&amp;
                    this.dy == this.config.sizeCell) {
                    return
                    // Если сейчас прибавление координат идет по
                    // оси y, то ничего не делаем - return
                    // Эта проверка запрещает змейке начать
                    // двигаться в противоположную от текущей
                } else {
                    this.dx = 0
                    this.dy = -this.config.sizeCell
                    // Иначе начинаем отнимать координаты
                    // от текущего положения головы змейки
                    // по оси y. Это заставит змейку двигаться
                    // вверх от текущих координат y
                }
                
            } else if (e.code == &#x27;ArrowLeft&#x27;) {
                if (this.dx == this.config.sizeCell &amp;&amp;
                    this.dy == 0) {
                    return
                } else {
                    this.dx = -this.config.sizeCell
                    this.dy = 0
                    // Для движения влево все тоже самое,
                    // но начинаем отнимать размер ячейки от
                    // оси x, не трогая значение y
                }
            
            } else if (e.code == &#x27;ArrowDown&#x27;) {
                if (this.dx == 0 &amp;&amp;
                    this.dy == -this.config.sizeCell) {
                    return
                 } else {
                    this.dx = 0
                    this.dy = this.config.sizeCell
                }
            
            } else if (e.code == &#x27;ArrowRight&#x27;) {
                if (this.dx == -this.config.sizeCell &amp;&amp;
                    this.dy == 0) { 
                    return
                } else {
                    this.dx = this.config.sizeCell
                    this.dy = 0
                }
            
            }
            
            if (e.code == &#x27;KeyG&#x27;) {
            // Если код слушателя равен клавише &quot;G&quot;, то
                if (this.config.godMode) {
                // Если параметр godMode равен true (включен)
                    this.config.godMode = false
                    // То выключаем его, делая false
                    this.config.drawGodMode()
                    // Запускаем функцию смены цветов и надписей
                    // Интерфейс и змейка становятся оранжевыми
                    
                } else {
                // Если параметр godMode равен false
                    this.config.godMode = true
                    // Включаем его, делая true
                    this.config.drawGodMode()
                    // Запускаем функцию смены цветов и надписей
                    // Интерфейс и змейка становятся красными
                }
            }        
        })    
    }
}</pre>
    <p id="Zo2m">Занятные детали компьютерной логики:</p>
    <ul id="N5he">
      <li id="vIZT">Змейка никуда не движется и постоянно растет, каждый ход к ней прирастает новая голова и отваливаться одна клетка от хвоста. </li>
    </ul>
    <ul id="rYXx">
      <li id="ml7a">Догнав ягодку у нас один раз НЕ отваливается хвост, так как к <code>maxTails</code> прибавляет 1.</li>
      <li id="CSuP">Если выставить изначальное значение <code>maxTails = 10</code>,  то можно заметить, что змейка НЕ появляется с хвостом длиной в 10 клеток, а просто 10 ходов у нее не укорачивается хвост.</li>
    </ul>
    <ul id="GIbq">
      <li id="FKO8">Скорость змейки определяется количеством пустых вызовов функции <code>animate()</code>, зависящей от параметра <code>maxStep</code> чем больше мы будем вызывать функцию бесполезно, тем медленнее будет бежать змейка</li>
    </ul>
  </section>
  <p id="l2Yq"></p>
  <p id="TXAb"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="33UY" data-align="center">gameLoop.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Tbzn">Модуль отвечает за обновление игрового пространства. Сюда я добавил контроль нажатия клавиш, чтоб регулировать скорость движения змейки.</p>
    <p id="dNsB">Значение <em>gameLoop.js</em> используются в <em><a href="#KcCl">game.js</a></em></p>
    <pre id="tI9d" data-lang="javascript">import Config from &#x27;./config.js&#x27;

export default class GameLoop {
    constructor(update, draw) {
    // В конструктор передаются методы из класса Game
        this.update = update
        // update() отвечает за изменения в игре
        this.draw = draw
        // а draw() за отрисовку на экране
        
        this.config = new Config()
        // Передаем данные Config
        
        this.animate = this.animate.bind(this)
        // animate будет содержать метод animate().
        // Чтоб сохранить значение this, используем
        // метод bind()
        
        this.animate()
        // Запускаем метод animate() она отвечает
        // за непрерывное обновление игрового поля
        this.control()
        // Запускаем метод control() он отвечает
        // за нажатие клавиш
    }
    
    animate() {
    // Функция отвечает за непрерывный цикл внесения изменений
    // в игру
    
        requestAnimationFrame(this.animate)
        // Вызывает повторно функцию animate()
        // так образуется непрерывный цикл
        
        if (++this.config.step &lt; this.config.maxStep) {
            return
            // Если ++step меньше maxStep, то ничего не делать
            // таким образом, надо 6 раз вызвать функцию,
            // чтоб змейка сделала 1 шаг
        }  
              
        this.config.step = 0
        // После движения змейки, счетчик возвращается на 0
    
        this.update()
        // Вызывается функция update(), которая обновляет 
        // данные по змейке, ягодке и очкам
        this.draw()
        // Вызывается функция, которая запускает draw() у ягодки
        // и змейки, предварительно отчистив игровое поле
    }
    
    control() {
    // Функция отвечает за изменение скорости змейки.
    // Чем выше значение maxStep, тем медленнее ползает змейка,
    // я выбрал диапазон от 2 до 9
    
        document.addEventListener(&#x27;keydown&#x27;, e =&gt; {
        // Метод addEventListener() отслеживает события
        // также как в snake.js
        
            if (e.code == &#x27;PageUp&#x27;) {
                if (this.config.maxStep &gt; 2) {
                // Если maxStep больше 2, то мы можем позволить
                // себе уменьшить его значение
                    this.config.maxStep--
                    // Отнимаем 1 от maxStep
                    this.config.drawSpeed()
                    // Рисуем новое значение скорости
                }
            } else if (e.code == &#x27;PageDown&#x27;) {
                if (this.config.maxStep &lt; 9) {
                // Если maxStep меньше 9, то мы можем позволить
                // себе увеличить его значение
                    this.config.maxStep++
                    // Прибавляем 1 к maxStep
                    this.config.drawSpeed()
                    // Рисуем новое значение скорости
                }
            }        
        })    
    }    
}</pre>
  </section>
  <p id="JOPP"></p>
  <p id="DoEy"></p>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="KcCl" data-align="center">game.js</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <pre id="9kIL" data-lang="javascript">import Canvas from &#x27;./canvas.js&#x27;
import Snake from &#x27;./snake.js&#x27;
import Berry from &#x27;./berry.js&#x27;
import Score from &#x27;./score.js&#x27;
import GameLoop from &#x27;./gameLoop.js&#x27;

class Game {
    constructor(container) {
        this.canvas = new Canvas(container)
        // Создается объект игрового поля,
        // со значением ссылки на класс в HTML-файле
        
        this.snake = new Snake()
        // Создается объект змейки

        this.berry = new Berry(this.canvas)
        // Объект ягодки
        
        this.score = new Score(&#x27;#score&#x27;, &#x27;#record&#x27;, 0)
        new GameLoop(this.update.bind(this), this.draw.bind(this))
        // Создается объект GameLoop в аргументах которого передаютя
        // container.update где container это значение в HTML-файле,
        // а update это метод объекта.
        // .bind(this) это метод, который передает методу update()
        // нужное значение this, опять же значение в HTML-файле
    }
    
    update() {
        this.snake.update(this.berry, this.score, this.canvas)
        // Вызываем медод объекта Snake, передавая в него данные
        // ягодки, счета и игрового поля
    }
    
    draw() {
    // Функция отвечает за отрисовку всего на игровом поле
        this.canvas.context.clearRect(0, 0,
            this.canvas.element.width,
            this.canvas.element.height)
        // Метод .clearRect() очищает прямоугольную область пикселей.
        // аргументы 0, 0, это начальные координаты, а ширина и высота
        // игрового поля размер отчищаемой области
        
        this.snake.draw(this.canvas.context)
        this.berry.draw(this.canvas.context)
        // Вызываем методы draw() у змейки и у ягодки, передавая
        // туда свойства и функции 2D-контекста из Канваса
    }
}

new Game(document.querySelector(&#x27;.canvas-wrapper&#x27;))
// Это старт игры, создается объект Game.
// querySelector ищет значение в HTML.
// У нас это пустой &lt;div&gt; с классом .canvas-wrapper</pre>
  </section>
  <p id="HWIz"></p>
  <blockquote id="2v05" data-align="center">HTML &amp; CSS</blockquote>
  <p id="8t2P"></p>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="8JKn" data-align="center">snake.html</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <pre id="857G" data-lang="html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;    
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;My Snake&lt;/title&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot;
          content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;css/style.css&quot;&gt;
    &lt;link rel=&quot;apple-touch-icon&quot; sizes=&quot;180x180&quot; 
          href=&quot;img/apple-touch-icon.png&quot;&gt;
    &lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; sizes=&quot;32x32&quot; 
          href=&quot;img/favicon-32x32.png&quot;&gt;
    &lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; sizes=&quot;16x16&quot; 
          href=&quot;img/favicon-16x16.png&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div id=&quot;game&quot;&gt;    
        &lt;div class=&quot;game-header&quot;&gt;
            
            &lt;div class=&quot;game-score&quot;&gt;                
                &lt;span class=&quot;score-text&quot;&gt;Speed:&lt;/span&gt;
                &lt;span class=&quot;score-count&quot; id=&quot;speed&quot; &gt;0&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class=&quot;game-score&quot;&gt;
                &lt;span class=&quot;score-text&quot;&gt;Score:&lt;/span&gt;
                &lt;span class=&quot;score-count&quot; id=&quot;score&quot; &gt;0&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class=&quot;game-score&quot;&gt;
                &lt;span class=&quot;score-text&quot;&gt;Record:&lt;/span&gt;
                &lt;span class=&quot;score-count&quot; id=&quot;record&quot; &gt;0&lt;/span&gt;
            &lt;/div&gt;
            &lt;!--
            Блоков game-score стало три, поэтому добавил
            уникальные id, по которым теперь ориенируется
            JavaScript вместо классов
            ---&gt;
        &lt;/div&gt;        
        &lt;div class=&quot;canvas-wrapper&quot;&gt;&lt;/div&gt;
        
        &lt;div class=&quot;game-footer&quot;&gt;            
            &lt;div class=&quot;keys-conf&quot;&gt;                
                &lt;span&gt;Use the WASD keys&lt;/span&gt;            
            &lt;/div&gt;        
        &lt;/div&gt;
        &lt;!--
        Новый блок game-footer отвечает за текст под игровым полем.
        Научил JS его менять в режиме бога
        ---&gt;
    &lt;/div&gt;
    &lt;script src=&quot;js/game.js&quot; type=&quot;module&quot;&gt;&lt;/script&gt;
&lt;/body&gt; 
&lt;/html&gt;</pre>
  </section>
  <p id="eJk4"></p>
  <p id="GyWG"></p>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="rKwa" data-align="center">style.css</h3>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <pre id="0tPF" data-lang="css">body {
    margin: 0px;    
    font-family: sans-serif;
}
#game {
    background: #353336;    
    width: 100vw;    
    height: 100vh;
    display: flex;    
    flex-direction: column;    
    justify-content: center;    
    align-items: center;
}
#game-canvas {
    display: block;    
    border-radius: 5px; 
}
.game-header {    
    margin-bottom: 15px;    
    width: 708px;    
    height: 70px;    
    background: #5E3908;    
    display: flex;    
    align-items: center;    
    justify-content: space-between;    
    border-radius: 10px;
}
.game-score {
    padding: 10px 15px;    
    min-width: 160px;    
    height: 30px;    
    background: #121214;    
    border-radius: 5px;
    display: flex;    
    justify-content: center;    
    align-items: center;
}
.score-text {
    font-size: 2vmin;    
    color: #EBEBEB;
}
.score-count {
    font-size: 3vmin;
    padding-left: 6px;    
    color: #EBEBEB;    
    font-weight: bold;
}
.canvas-wrapper {
    background-color: #161618;    
    border-radius: 5px;    
    border: 10px solid #161618;
    background-image: url(&quot;data:image/svg+xml,%3Csvg width=&#x27;16&#x27; height=&#x27;16&#x27; viewBox=&#x27;0 0 16 16&#x27; fill=&#x27;none&#x27; xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%3E%3Cg opacity=&#x27;0.2&#x27;%3E%3Ccircle cx=&#x27;8&#x27; cy=&#x27;8&#x27; r=&#x27;4&#x27; fill=&#x27;%23525053&#x27;/%3E%3C/g%3E%3C/svg%3E%0A&quot;);
}
.game-footer {
    padding-top: 1vmin;    
    padding-bottom: 1vmin;
    display: flex;    
    justify-content: space-between;
}
.keys-conf {  
    width: 708px;    
    height: 30px;    
    display: flex;    
    justify-content: center;    
    align-items: center;
    font-size: 2vmin;    
    color: #797979;    
    font-weight: regular;
}</pre>
    <p id="K2Gc">Основные изменения коснулись <code>.game-header</code> и <code>.game-score</code>, добавились классы <code>.game-footer</code> и <code>.keys-conf</code>.</p>
    <p id="2di3">Уверен как-то можно заменить уродскую <code>url</code> с svg-картинкой на нарисованную через css, если кто знает, напишите.</p>
  </section>
  <p id="7Rlm" data-align="center"></p>
  <blockquote id="PjJ4" data-align="center"><a href="#9FRF">Наверх</a></blockquote>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.buninman.ru/js_stashchuk</guid><link>https://blog.buninman.ru/js_stashchuk?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman</link><comments>https://blog.buninman.ru/js_stashchuk?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman#comments</comments><dc:creator>buninman</dc:creator><title>Курс по JavaScript</title><pubDate>Fri, 02 Sep 2022 15:13:01 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/ee/d2/eed28ed1-e48f-48de-b003-837334cfe71e.png"></media:content><category>Обучение</category><description><![CDATA[<img src="https://img4.teletype.in/files/3f/c4/3fc42406-fa92-4f2d-b7ce-8554581884bc.jpeg"></img>Все, что вы тут найдете, это ничто иное как один большой конспект курса по JavaScript от Стащук Богдана.]]></description><content:encoded><![CDATA[
  <p id="PQIX">Все, что вы тут найдете, это ничто иное как один большой конспект курса по JavaScript от Стащук Богдана.</p>
  <p id="yrM4">Когда начинал проходить курс, хотел каждую тему делать отдельной статьей, но потом понял, что это сильно затрудняет навигацию и поиск, поэтому сделал все в одной статье. Букв, конечно, пришлось написать изрядное количество🙄</p>
  <p id="UWV0">Возможно кому-то конспект пригодится😊 Если найдете ошибки, а в нем они есть, я уверен, то пишите в комментариях - исправлю.</p>
  <p id="9DES">Курс есть в свободном доступе на <a href="https://youtu.be/CxgOKJh4zWE" target="_blank">ютубе.</a></p>
  <hr />
  <p id="ZLne"></p>
  <p id="4Tky"></p>
  <h2 id="DhPs">Оглавление</h2>
  <h3 id="L0bV">1. <a href="#XZvR">Отзыв о курсе</a> </h3>
  <h3 id="5biF">2. <a href="#N2bX">Введение</a></h3>
  <h3 id="lmBE">3. <a href="#H4B7">Переменные</a></h3>
  <blockquote id="Rp9I">3.1. Имена переменных</blockquote>
  <blockquote id="NY2X">3.2. Типы значений</blockquote>
  <blockquote id="hUUL">3.3. Объявления переменных</blockquote>
  <h3 id="gMnf">4. <a href="#9rG8">Объекты</a></h3>
  <blockquote id="0lbn">4.1. Действия со свойствами объекта</blockquote>
  <blockquote id="CYiM">4.2. Вложенные свойства объектов</blockquote>
  <blockquote id="IgJv">4.3. Использование переменных как значения свойств </blockquote>
  <blockquote id="uDm0">4.4. Глобальные объекты</blockquote>
  <blockquote id="VNAj">4.5. Методы объекта</blockquote>
  <h3 id="QZCh">5. <a href="#HEZz">JSON</a></h3>
  <h3 id="0BMV">6. <a href="#TC8I">Мутации</a></h3>
  <blockquote id="xlY0">6.1. Способы избежать мутаций</blockquote>
  <h3 id="WJim">7. <a href="#GIMU">Функции</a></h3>
  <blockquote id="oUib">7.1. Неявный возврат значения</blockquote>
  <blockquote id="iCQ9">7.2. Параметры функции</blockquote>
  <blockquote id="QChg">7.3. Мутации объектов через функции</blockquote>
  <blockquote id="hB7V">7.4. Коллбэк функция</blockquote>
  <blockquote id="uFl4">7.5. JSDOC-комментарии</blockquote>
  <h3 id="wyTZ">8. <a href="#bhSM">Области видимости</a></h3>
  <h3 id="Cjzq">9. <a href="#JkiP">Ложные значение</a></h3>
  <h3 id="iYZC">10. <a href="#1xHQ">Операторы</a></h3>
  <h3 id="rVLW">11. <a href="#kiJ8">Конкатенация строк</a></h3>
  <h3 id="wBnw">12. <a href="#QsF8">Обработка ошибок</a></h3>
  <h3 id="LYOz">13. <a href="#s1lL">Инструкции</a></h3>
  <h3 id="E3O3">14. <a href="#jFL1">Массивы</a></h3>
  <blockquote id="lCzd">14.1. Чтение значений массива</blockquote>
  <blockquote id="OyEK">14.2. Методы массивов</blockquote>
  <h3 id="8SDb">15. <a href="#mhYq">Деструктуризация</a></h3>
  <h3 id="q8DY">16. <a href="#Q0Lp">Условные инструкции</a></h3>
  <h3 id="w6oZ">17. <a href="#zf4q">Тернарный оператор</a></h3>
  <h3 id="X4da">18. <a href="#G2Xo">Циклы</a></h3>
  <blockquote id="8jFC">18.1. Цикл <em>for</em></blockquote>
  <blockquote id="VUeE">18.2. Метод массивов <em>forEach</em></blockquote>
  <blockquote id="dKZo">18.3. Циклы <em>while и do while</em></blockquote>
  <blockquote id="EEwb">18.4. Цикл <em>for in</em> для объектов</blockquote>
  <blockquote id="6nto">18.5. Метод <em>forEach()</em> для объектов</blockquote>
  <blockquote id="o8pi">18.6. Цикл <em>for of</em></blockquote>
  <h3 id="fSed">19. <a href="#RyWC">Модули</a></h3>
  <h3 id="1SX2">20. <a href="#k6X7">Классы и прототипы</a></h3>
  <blockquote id="BPx6">20.1. Статический метод</blockquote>
  <blockquote id="w9VQ">20.2. Расширение других классов</blockquote>
  <blockquote id="f9ay">20.3. Примитивные значения</blockquote>
  <h3 id="aYxa">21. <a href="#Ez0U">Промисы</a></h3>
  <h3 id="kl6G">22. <a href="#dEYM">Асинхронные функции</a></h3>
  <blockquote id="S3vb">22.1. Переход с промисов на ASYNC/AWAIT</blockquote>
  <p id="60Hi"></p>
  <p id="R1Ct"></p>
  <hr />
  <h2 id="XZvR">1. Отзыв о курсе</h2>
  <p id="hjMn">Я только начинаю изучение JS и курс Богдана показался мне самым понятным из тех что я нашел. Подкупили простые слайды и приятный голос, - поэтому я начал именно с его курса. И не ошибся.</p>
  <p id="L9cD">Просмотр курса занял не слишком много времени, но мое понимание JS с полного нуля знатно так прокачалось. Делая змейку по гайду с ютуба я понял 90% написанного кода, даже не пройдя курс целиком.</p>
  <p id="w0uj">Видео смотрел на скорости 2х и не было ни одного момента где было бы что-то не понятно в словах, дикция у автора отличная. Поэтому настоятельно рекомендую ускорять видео, если будете проходить этот курс, в оригинале речь показалось очень медленной. </p>
  <h3 id="QwFg"><strong>Не понятно</strong></h3>
  <p id="tUJA">Единственный непонятный раздел в курсе - это коллбэк функции, тут мне пришлось загуглить чтоб разобраться что к чему. Все остальное объясняется очень хорошо и можно обойтись тем, что есть в курсе.</p>
  <h3 id="v0B7">Задания</h3>
  <p id="EgGO">На момент написания отзыва я прошел только 14 заданий из 80, но все задания проходил используя только информацию с курса, не гугля. Это сложно и иногда, чтоб найти тему задания приходилось проматывать много видео и искать где же Богдан это объяснял. Думаю стоило добавить в заданиях ссылки на разделы курса, в которых тема задания раскрывается. </p>
  <h3 id="RKve">Курс рекомендую👍</h3>
  <p id="hO7j"></p>
  <figure id="rIdi" class="m_column">
    <img src="https://img4.teletype.in/files/3f/c4/3fc42406-fa92-4f2d-b7ce-8554581884bc.jpeg" width="1920" />
  </figure>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="xAdn" data-align="center">Сайт автора курса: <a href="https://stashchuk.com/" target="_blank">stashchuk.com</a></p>
  </section>
  <p id="maM9"></p>
  <p id="52gm"></p>
  <hr />
  <h2 id="N2bX">2. Введение</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Y7Ul">Самое важно в JavaScript это <strong>Выражение</strong>, <strong>Функции</strong> и <strong>Объекты</strong></p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Exfb">Практически все сущности в JS это <strong>объекты</strong> или ведут себя как объекты</p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="ja02"><strong>Объект</strong> — это набор свойств<strong> <em>&quot;имя: значение&quot; </em></strong>или <strong><em>&quot;ключ: значение&quot; </em></strong></p>
  </section>
  <p id="6Ovv">Разбор выражения <em>console.log</em>, который выводит в консоль <em>Hello World:</em></p>
  <pre id="aWmh" data-lang="javascript">console.log(&#x27;Hello World&#x27;)

console       // Это объект у которого есть свойства (имя: значение)
.             // Это точечная запись. Оператор
log()         // Это метод (функция) одного из свойств объекта consol
()            // Это оператор вызова функции
&#x27;Hello World&#x27; // это значение типа String (Строка). 
              // Может быть как в двойных кавычках, так и в одинарных</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="CnAT">Выражения всегда возвращают значения</p>
  </section>
  <p id="RbzV">Примеры выражений:</p>
  <pre id="A5D7" data-lang="javascript">&#x27;abc&#x27;                 // Вернет значение &#x27;abc&#x27;
10                    // Вернет 10
5 + 2                 // Вернет результат 7. Тут есть оператор +
с = 10                // = это оператор присвоения. Результатом будет 10
&#x27;Good &#x27; + &#x27;Morning&#x27;   // Вернет &#x27;Good Morning&#x27;
a &lt;= b || c !== d     // Вернет true или false. Логические операторы
                      // меньше или равно, </pre>
  <p id="x24X">Выражение с побочными действиями (SideEffects):</p>
  <pre id="8q09" data-lang="javascript">a = 15  // Это выражение присваивания, где переменной a присваивается
        // значение 10, также это выражение возвращает значение 15
a++     // увеличивает значение переменной на единицу и вернет значение
myFunction(c, d)  // внутри функции проиходят какие-то действия</pre>
  <blockquote id="TIpo" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="rjLS"></p>
  <p id="GBQy"></p>
  <hr />
  <h2 id="H4B7">3. Переменные</h2>
  <p id="gjFX">Переменная — это коробка в которую мы можем положить значения, достать значение, заменить его на другое значение. Также мы можем дать этой коробке название.</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="lbK1">Переменные дают возможность <strong>повторного</strong> доступа к значениям</p>
  </section>
  <p id="hWaq"></p>
  <h3 id="Lmc2">3.1. Имена переменных</h3>
  <p id="rbmw">Не стоит называть переменные <em>a b c</em> и не стоит сокращать названия. Названия переменных должны быть понятны не только вам, но и другим разработчикам. Должны отражать суть и назначение переменных.</p>
  <p id="aU7o">Есть 3 типа имен для переменных:</p>
  <section style="background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="CMLw">PascalCase - для типов и классы.</p>
  </section>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="rI8w">DB_USER - значения известны до запуска приложения и не меняются. Константы.</p>
  </section>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="nvxB">camelCase - для всех остальных переменных.</p>
  </section>
  <p id="9uPf"></p>
  <h3 id="laMo">3.2. Типы значений</h3>
  <p id="Te0E">Тип переменной определяется типом значения, которое ей присвоено.</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="mMgT">В JavaScript мы можем переопределять тип переменной сколько угодно раз так как JS это динамически типизируемый язык.</p>
  </section>
  <p id="jwzR">Существуют <strong>примитивные</strong> типы значений, это те значения которые хранятся непосредственно в переменных. К примитивным относятся:</p>
  <ul id="Oeni">
    <li id="udi1"><strong><em>number</em> </strong>(число) — это любое числовое значение, в том числе и дробные (1, -2, 3, 5.6, 1.15).</li>
    <li id="9N4i"><strong><em>string</em> </strong>(строка) — это любой текст. Можно использовать как двойные, так и одинарные кавычки.</li>
    <li id="4eXg"><em><strong>boolean</strong></em> (логический) — всего 2 значения <em>true</em> и <em>false</em>.</li>
    <li id="cQuI"><em><strong>undefined</strong></em> (неопределенный) — только значение <em>undefined</em>. Значение не определено.</li>
    <li id="TlbJ"><em><strong>null</strong></em> (нулевой) — только значение <em>null</em>. Для указания отсутствия значения у переменной.</li>
    <li id="gH7k"><em><strong>symbol</strong></em> (символ) — используется для создания уникальных значений (В этом курсе про этот тип переменных информации нет).</li>
  </ul>
  <p id="Kd7m">Помимо примитивного, есть <strong>ссылочный</strong> тип переменной. Такая переменная хранит в себе только <strong>ссылку</strong> на значение:</p>
  <ul id="BH1x">
    <li id="7YtL"><em><strong>object</strong></em> (объект) — это ссылочный тип.</li>
  </ul>
  <p id="SjfB">Так как переменная типа объект хранит только ссылку на объект, то изменение этого объекта коснутся всех переменных имеющих туже ссылку:</p>
  <pre id="HQQr" data-lang="javascript">const objectA = {
  name: &#x27;Alex&#x27;,
  age: 40
}
const objectB = objectA
// Создаем еще один объект копирующий первый
objectB.name = &#x27;Victor&#x27;
// Меняем значение свойства name в ObjectB
console.log(objectA.name)
// Получаем значение Victor, первый объект также изменился</pre>
  <p id="gQ4E"></p>
  <h3 id="coLe">3.3. Объявления переменных</h3>
  <p id="BxDM">Чтобы создать переменную и присвоить ей значение, можно использовать ключевые слова <em>const</em>, <em>let</em> или <em>var</em>: </p>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="CRCg"><em>const</em> - не позволяет менять значение переменной. Объявление и присваивание обязательно прописывается одной строкой. </p>
  </section>
  <pre id="bMpe" data-lang="javascript">const myAge = 40
// объявили переменную myAge и сразу присвоили ей значение 40
myAge = 50
// попытались присвоить переменной myAge новое значение 50
// и получили ошибку!)</pre>
  <p id="10HB">Стоит использовать <em>const</em> максимально, где это возможно. Т.к. он решает некоторые проблемы связанные с динамической типизацией языка JS.</p>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="McUq"><em>let</em> - для всех остальных переменных.</p>
  </section>
  <pre id="DA1F" data-lang="javascript">let myAge
// объявили переменную myAge
myAge = 50
// присвоили переменной myAge значение 50
let youHere = true
//объявили переменную youHere и сразу присвоили ей значение true
youHere = false
//присвоили переменной youHere новое значение false</pre>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="6aLr"><em>var</em> - устаревший тип и использовать его больше не имеет смысла.</p>
  </section>
  <blockquote id="7HFk" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="yEe9"></p>
  <p id="VyhZ"></p>
  <h2 id="9rG8">4. Объекты</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="azkd"><strong>Объект</strong> — это набор <strong>свойств.</strong></p>
  </section>
  <p id="45aF">Практически все сущности в JS это объекты или ведут себя как объекты. Например, массив и функция - это объекты, а число и строка ведут себя как объекты, хотя являются примитивными значениями.</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="prUe"><strong>Свойство</strong> — это пара <em><strong>имя: значение.</strong></em></p>
  </section>
  <p id="qNel">Значением также может быть другой объект (<a href="#8orm">вложенный объект</a>):</p>
  <pre id="seqH" data-lang="javascript">const objectA = {
height: 550,
weight: 440,
xyz: {
  x: -1,
  y: 2,
  z: 3.5
  },
title: &#x27;cube&#x27;</pre>
  <p id="ymDe">После каждого свойства ставиться запятая, а после последнего свойства в объекте запятую можно опустить.</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="q0bq">Порядок свойств в объекте <strong>не имеет</strong> значения</p>
  </section>
  <p id="3zth"></p>
  <h3 id="Qprf">4.1. Действия со свойствами объекта</h3>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="K1LS">С помощью оператора <em><strong>точка</strong></em> . мы получаем доступ к свойствам объектов. </p>
  </section>
  <p id="zsIm">По аналогии с вызовом метода <em>log</em> объекта <em><strong>console</strong></em> используем точку после названия объекта для получения значения свойств объекта <em>aboutMe:</em></p>
  <pre id="mpPo" data-lang="javascript">const aboutMe = {
  city: &#x27;Moscow&#x27;,
  designer: true,
  country: &#x27;Russia&#x27;
}
console.log(aboutMe.city)
// Получим &#x27;Moscow&#x27;
console.log(aboutMe.designer)
// Получим true</pre>
  <p id="oTZB">Чтобы присвоить новое значение используем оператор <em>=</em>, а чтобы удалить свойство - ключевое слово<em>delete. Если мы присвоим значение несуществующему свойству, то это свойство создастся автоматически:</em></p>
  <pre id="500L" data-lang="javascript">const aboutMe = {
  city: &#x27;Moscow&#x27;,
  designer: true,
  country: &#x27;Russia&#x27;
}
aboutMe.city = &#x27;Perm&#x27;
// Переопределим значение свойства city
aboutMe.time = 3
// Добавим новое свойство time со значением 3 в объект
delete aboutMe.designer
// Удалим свойство designer из объекта
console.log(aboutMe)
// Получим объект {city: &#x27;Perm&#x27;, country: &#x27;Russia&#x27;, time: 3}</pre>
  <p id="btTQ">Кроме точки, доступ к свойствам объекта можно получить с помощью <em>[ ]</em> (bracket notation).</p>
  <pre id="fzrM" data-lang="javascript">const myCity = {
  city: &#x27;Kiev&#x27;
}
myCity[&#x27;year&#x27;] = 2022
console.log(myCity.year)
// Получим 2022, мы добавили новое свойство year со значением 2022</pre>
  <p id="AFTQ">Синтаксис с <em>[ ]</em> используется когда название свойства является значением той или иной переменной, например:</p>
  <pre id="xm68" data-lang="javascript">const myCity = {
  city: &#x27;Kiev&#x27;,
  year: 2022
}
const countryPropertyName = &#x27;country&#x27;
// Создадим новую переменную со значение country
myCity[countryPropertyName] = &#x27;Ukraine&#x27;
// Добавили свойство с именем взятым из значения переменной
// countryPropertyName, т.е. country
console.log(myCity)
// Получим объект {city: &#x27;Kiev&#x27;, year: 2022, country: &#x27;Ukraine&#x27;}</pre>
  <p id="RFDm">В <em>[ ]</em> мы можем указать любое выражение, и результат этого выражения, станет названием свойства. </p>
  <p id="e3el"></p>
  <h3 id="ze07">4.2. Вложенные свойства объектов</h3>
  <p id="a1ZI">Оператор точка также позволяет получить доступ к вложенным объектам. Можно комбинировать с bracket notation:</p>
  <pre id="CcAO" data-lang="javascript">const myCity = {
  city: &#x27;Kiev&#x27;,
  date: {
    month: 4,
    day: 22
  }
}
console.log(myCity.date.day)
// Получим 22
console.log(myCity.date[&#x27;day&#x27;])
// Получим также 22</pre>
  <p id="WoIT"></p>
  <h3 id="D8FW">4.3. Использование переменных как значения свойств </h3>
  <p id="5miw">Мы можем использовать имена переменных в качестве значений свойств. При создании объекта в него будут скопированы значения этих переменных:</p>
  <pre id="VX4z" data-lang="javascript">const name = &#x27;Alex&#x27;
const age = 40

const userProfile = {
  userName: name,
  userAge: age,
  userCity: &#x27;Pskov&#x27;
}
console.log(userProfile)
// Получим объект {userName: &#x27;Alex&#x27;, userAge: 40, userCity: &#x27;Pskov&#x27;}</pre>
  <p id="gMiB">Для удобства, можно сокращать написание свойства до названия переменной. Но тогда имя свойства будет такое же, как имя переменной:</p>
  <pre id="OOaE" data-lang="javascript">const name = &#x27;Alex&#x27;
const age = 40

const userProfile = {
  name,
  age,
  userCity: &#x27;Pskov&#x27;
}
console.log(userProfile)
// Получим объект {name: &#x27;Alex&#x27;, age: 40, userCity: &#x27;Pskov&#x27;}</pre>
  <p id="jMPa"></p>
  <h3 id="QW2r">4.4. Глобальные объекты</h3>
  <p id="6Shh">Глобальный объект - это встроенный объект с огромным количеством свойств и методов. Для браузеров это <em>window</em> или <em>globalThis</em>, для <strong>node.js</strong> это <em>global</em> или <em>globalThis.</em></p>
  <p id="0oFP">Мы можем использовать методы глобального объекта. Метод<em> log()</em> один из таких методов:</p>
  <pre id="Enhr" data-lang="javascript">window.console.log(&#x27;Hello World!&#x27;)
// Объект console является одним из свойств глобального объекта window</pre>
  <p id="UNEn"></p>
  <h3 id="lbGC">4.5. Методы объекта</h3>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="WCgZ"><strong>Метод</strong> — это свойство объекта, значение которого - функция</p>
  </section>
  <p id="yj8N">Мы можем вызвать метод объекта использую точечную запись<em> .</em> (оператор точка) и <em>()</em> круглые скобки<em>:</em></p>
  <pre id="kPpX" data-lang="javascript">myCity.city // Получаем доступ к значению свойства
myCity.helloMyCity() // Вызываем метод объекта</pre>
  <p id="G3gd">В объекте <em>myCity</em> <em>city является обычным свойством</em>, а <em>helloMyCity() методом:</em></p>
  <pre id="jtkB" data-lang="javascript">const myCity = {
  city: &#x27;Pskov&#x27;,
  helloMyCity: function () {
    console.log(&#x27;Hello my City&#x27;)
  }
}
myCity.helloMyCity()</pre>
  <p id="4SEJ">Для удобства, можно опускать ключевое слово <em>function</em> и ставить круглые скобки сразу после названия свойства:</p>
  <pre id="PFSE" data-lang="javascript">const myCity = {
  city: &#x27;Pskov&#x27;,
  helloMyCity() { // Сократили слово function
    console.log(&#x27;Hello my City&#x27;)
  }
}
myCity.helloMyCity()</pre>
  <blockquote id="ARMG" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="fI34"></p>
  <p id="Jn6O"></p>
  <hr />
  <h2 id="HEZz">5. JavaScript Object Notation (JSON)</h2>
  <p id="2u1C">Формат обмена данными с серверами. Если добавить переносы, то формат записи <em>JSON</em> похож на формат записи JS:</p>
  <pre id="YKtV">{
  &quot;id&quot;: 10,
  &quot;title&quot;: &quot;user&quot;,
  &quot;status&quot;: false
}</pre>
  <p id="2uTA">Но передается <em>JSON</em> в виде строки (<em>string</em>):</p>
  <pre id="ZTcE" data-lang="javascript">&#x27;{&quot;id&quot;:10,&quot;title&quot;:&quot;user&quot;,&quot;status&quot;:false}&#x27;</pre>
  <p id="E7z8">Чтобы конвертировать данные <em>JSON</em> в объект JS, существует метод <em>parse()</em> объекта <em>JSON</em>, а для конвертации из JS в <em>JSON</em>, есть метод <em>stringify()</em></p>
  <pre id="nyyI" data-lang="javascript">JSON.parse() // конвертирует строку JSON в JS
JSON.stringify() // конвертирует объект JS в JSON</pre>
  <p id="7u6T">После выполнения метода <em>JSON.parse()</em>, мы получим привычный JS набор свойств:</p>
  <pre id="8hB6" data-lang="javascript">{
  id: 10,
  title: &#x27;user&#x27;,
  status: false
}</pre>
  <p id="TzRs">Пробуем конвертировать объект в <em>JSON</em> и обратно:</p>
  <pre id="DMqh" data-lang="javascript">const normalJavaScriptObject = {
  id: 10,
  title: &#x27;user&#x27;,
  status: false
}
const jsonString = JSON.stringify(normalJavaScriptObject)
// Создаем новую переменную и присваиваем ей значение JSON
console.log(jsonString)
// Получим строку &#x27;{&quot;id&quot;:10,&quot;title&quot;:&quot;user&quot;,&quot;status&quot;:false}&#x27;
const jsonParse = JSON.parse(jsonString)
// Создаем новую переменную и конвертируем строку JSON в объект
console.log(jsonParse)
// Получим объект {id: 10, title: &#x27;user&#x27;, status: false}</pre>
  <blockquote id="XHtx" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="aNMt"></p>
  <p id="gnae"></p>
  <hr />
  <h2 id="TC8I">6. Мутации</h2>
  <p id="NUdo">Значения переменных <a href="#opfY">примитивного типа</a> хранятся в самих переменных, поэтому при присвоении одной переменной значения другой переменной происходит копирование этого значения (copy by value):</p>
  <pre id="lDc1" data-lang="javascript">const a = 10
let b = a
// Создаем переменную b и копируем в нее значение переменной a, равное 10
b = 30
// Меняем значение переменной b на 30
console.log(a)
// Видим что значение переменной a по прежнему 10
console.log(b)
// А значение переменной b теперь 30</pre>
  <p id="7hdz">Переменные ссылочного типа, содержат ссылку на объект, поэтому при присвоении одной переменной этой ссылки из другой переменной, переменные будут иметь одну и туже ссылку на один и тот же объект в памяти (copy by reference):</p>
  <pre id="9wW1" data-lang="javascript">const user = {
  name: &#x27;Sofia&#x27;,
  age: 50
}
user.age = 22
// Присвоим значение 22 свойству age 
user.isAdult = true
// Создадим новое свойство isAdult со значением true
console.log(user.age)
console.log(user.isAdult)
// Убедимся, что значения age и isAdult переменной user равны 22 и true

const userCopy
// Создадим новую переменную и присвоим ей значение переменной user
userCopy.age = 44
userCopy.isAdult = false
// Изменим значения свойств для новой переменной userCopy

console.log(user.age)
console.log(user.isAdult)
// Значения свойств age и isAdult первой переменной user теперь
// равны 44 и false, т.к. мы изменили объект по ссылке
// через переменную userCopy</pre>
  <p id="3r7j">Мы изменили объект через копию переменной имеющую такую же ссылку на объект. Такое поведение называется <strong>мутированием</strong>.</p>
  <p id="oYde"></p>
  <h3 id="lgOi">6.1. Способы избежать мутаций</h3>
  <p id="A5xW"><strong>Метод<em> Object.assign(). </em></strong>Чтобы избежать мутаций можно использовать метод <em>assign()</em> объекта <em>Object.</em> При создании копии объекта, этот метод скопирует все свойства первого объекта и создаст новый объект:</p>
  <pre id="jRh9" data-lang="javascript">const user = {
  name: &#x27;Sofia&#x27;,
  age: 50
}
const userCopy = Object.assign({}, user)</pre>
  <p id="9kqq"><strong>Оператор <em>три точки</em>(...)</strong>.Также можно воспользоваться оператором <em>три точки</em> (spread), он разделяет объект на свойства. И если мы применим такой оператор при создании нового объекта, то получим полностью новый объект, с новой ссылкой.</p>
  <pre id="OsRx" data-lang="javascript">const user = {
  name: &#x27;Sofia&#x27;,
  age: 50
}
const userCopy = {...user}</pre>
  <p id="4F31">Но такие методы не подойдут если у копируемого объекта есть вложенные свойства, тоже являющиеся объектами, так как ссылки на них сохранятся.</p>
  <p id="Iiwj"><strong>Методы <em>JSON.parse()</em> и <em>JSON.stringify(). </em></strong>Чтобы избежать мутаций полностью, даже имея вложенные объекты, можно воспользоваться конвертацией объекта в <a href="#EzcE">JSON</a> и обратно:</p>
  <pre id="duR4" data-lang="javascript">const user = {
  name: &#x27;Sofia&#x27;,
  age: 50
}
const userCopy = JSON.parse(JSON.stringify(user))
// Конвертируем объект user в JSON, а потом создаем новую переменную
// userCopy и присваеваем ей полученное значение JSON
// конвертируя его обратно в JS</pre>
  <blockquote id="CPWO" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="sXyk"></p>
  <p id="yl51"></p>
  <hr />
  <h2 id="GIMU">7. Функции</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Ip1p">Функция - это блок кода, который можно выполнять <strong>многократно</strong></p>
  </section>
  <p id="q0G6"><strong>Функция - это объект</strong>, и как любой объект она имеет свойства. Это можно увидеть применив метод метод console.dir() на функции:</p>
  <pre id="jO6b" data-lang="javascript">function myFn() {
  console.log(&#x27;Hello&#x27;)
}
console.dir(myFn)
// Метод dir() покажет свойства функции как объекта</pre>
  <p id="hpha">Функция может быть:</p>
  <ul id="G2hJ">
    <li id="wc2D"><strong>Именованной</strong> (Объявленная функция). Объявляем функцию сразу указывая ее будущее имя. Будет аналогична анонимной функции присвоенной переменной через ключевое слово <em>let</em>;</li>
    <li id="RpG5"><strong>Анонимной</strong>. Анонимными являются функциональные выражения и стрелочные функции;</li>
    <li id="Q1eh"><strong>Присвоенная переменной</strong>. Если функциональное выражение и стрелочную функцию присвоить переменной, то будет функция присвоенная переменной;</li>
    <li id="tZa0"><strong>Аргументом</strong>. Например, при вызове другой функции (<a href="#CAc3">callback function</a>);</li>
    <li id="vPVt"><strong>Методом</strong>, т.е. значением свойства в объекте.</li>
  </ul>
  <pre id="va0F" data-lang="javascript">function myFn() {
  console.log(&#x27;Hello&#x27;)
}
// Именованая функция

function () {
  console.log(&#x27;Hello&#x27;)
}
// Функциональное выражение (анонимная функция)

const myFn = function () {
  console.log(&#x27;Hello&#x27;)
}
// Функциональное выражение присвоеное переменной

const myFn = () =&gt; {
  console.log(&#x27;Hello&#x27;)
}
// Стрелочная функция присвоеная переменной

const myFn = () =&gt; console.log(&#x27;Hello&#x27;)
// Если у стрелочной функции всего одно выражение, то {} можно опустить
// Функция будет неявно возвращать значение этого выражения

setTimeout(myFn, 5000)
// Функция как аргумент другой функции

const myObject = {
  name = &#x27;Alex&#x27;
  print: function
// Функция как значение одного из свойств объекта (метод)</pre>
  <h3 id="tyRb"></h3>
  <p id="yaMt"><strong>7.1. Неявный</strong> возврат значения</p>
  <p id="6J6z">Функция, как и любое выражение всегда возвращает значение. Если в функции <strong>явно</strong> (командой <em>return</em>) не указать какое именно значение она должна вернуть, то она всегда <strong>неявно</strong> (без команды <em>return</em>) возвращает <em>undefined:</em></p>
  <pre id="OgpL" data-lang="javascript">const myFn = () =&gt; {
  5 + 5
}
// Такая функция вернет undefined, т.к. мы не указали что ей возвращать

const myFn = () =&gt; {
  return 5 + 5
}
// Такая функция вернет сумму 10, т.к. мы ЯВНО указали
// командой ruturn, что ей нужно вернуть 

const myFn = () =&gt; return 5 + 5
// Такая функция также вернет сумму 10, но уже НЕЯВНО, без команды ruturn</pre>
  <p id="ptOv"></p>
  <h3 id="eldE">7.2. Параметры функции</h3>
  <p id="RBQZ">При объявлении функции опционально можно указать параметры, с которыми функция будет работать внутри себя:</p>
  <pre id="YnG9" data-lang="javascript">function mySum(a, b) {
// Указываем параметры a и b, это переменные которые действуют 
// только внутри функции в момент вызова функции
  let sum
  // Объявляем переменную sum, которая будет  
  sum = a + b
  return sum
  // Ключевое слово return указывает то, что должна вернуть функция.
}</pre>
  <p id="fwA8">В момент вызова этой функции в круглых скобках указываются значения параметром функции - аргументы:</p>
  <pre id="PxrD" data-lang="javascript">function mySum(a, b) {
  let sum
  sum = a + b
  return sum
}
myFn(10, 3)
// Аргументы присваиваются параметрам по порядку
// Число 10 будет присвоено параметру a, а число 3 - параметру b</pre>
  <p id="OdQQ">Можно указать какие параметры функция будет использовать по умолчанию, если не передано другое значение:</p>
  <pre id="8WW6" data-lang="javascript">function multByFactor(value, multiplier = 1) {
  return value * multiplier
}
// Аргумент multiplier будет равен 1 если не передано другое значение

multByFactor(10, 2)
// Вернет результат 20
multByFactor(5)
// Вернет результат 5, т.к. второй агрумент будет взят по умолчанию, это 1</pre>
  <p id="qw7s">В примере функция создает объект, добавляет к нему все свойства из объекта post с помощью оператора ... и добавляет новое свойство со значением текущей даты с помощью метода <em>Date():</em></p>
  <pre id="zI3z" data-lang="javascript">const newPost = (post, addedAt = Date()) =&gt; ({
  ...post,
  addedAt,
})
// Тело функции ({}) дополнительно обернуто круглыми скобками,
// это позволяет неявно вернуть объект, как результат функции

const firstPost = {
  id: 1,
  author: &#x27;Pavel&#x27;
}
newPost(firstPost)
// Функция вернет новый объект с тремя свойствами</pre>
  <p id="csUr"></p>
  <p id="WaUD"></p>
  <h3 id="bOoF">7.3. Мутации объектов через функции</h3>
  <p id="h68j">Аргументом в вызове функции может быть объект и в этом случае параметру функции будет передана ссылка на этот объект и все дальнейшие действия с этим параметром, будут вызывать мутации внешнего объекта по ссылке:</p>
  <pre id="YVFy" data-lang="javascript">const personOne = {
  name: &#x27;Lara&#x27;,
  age: 22
}

function increasePersonAge(person) {
  person.age += 1
  return person
}

increasePersonAge(personOne)
// Вызываем функцию с аргументом переменной personOne
console.log(personOne.age)
// Получим 23, т.к. внешний объект мутировал через изменение person</pre>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="EsQ7">Внутри функции <strong>не рекомендуется</strong> мутировать внешние объекты.</p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="RvPL">Функция которая выполняет <strong>одну</strong> задачу не меняет внешних переменных называется <strong>чистой функцией</strong> (pure function).</p>
  </section>
  <p id="Qu4T">Чтобы избежать мутаций в функции можно использовать<a href="#OfcC"> метод <em>assign()</em></a><em>, <a href="#UR6M">три точки</a> </em>или<em> <a href="#rvUO">JSON</a></em>, также как мы использовали их раннее:</p>
  <pre id="3d7c" data-lang="javascript">const personOne = {
  name: &#x27;Lara&#x27;,
  age: 22
}

function increasePersonAge(person) {
  const updatedPerson = Object.assign({}, person)
  // Создаем новый объект прямо внутри функции и начинаем работать с ним
  updatedPerson.age += 1
  return updatedPerson
}
const updatedPersonOne = increasePersonAge(personOne)
// Создаем новую переменную, вызываем функцию и
// присваиваем результат функции этой новой переменной</pre>
  <p id="MYSf"></p>
  <h3 id="kL03">7.4. Коллбэк функция (callback function)</h3>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Y1pD"><strong>Коллбэк</strong> — это <strong>функция</strong>, которая должна быть выполнена после того, как другая <strong>функция</strong> завершит работу.</p>
  </section>
  <pre id="hDr4" data-lang="javascript">function printFinish() {
  console.log(&#x27;Finish&#x27;)
}
console.log(&#x27;Start&#x27;)
// Печатаем в консоль Start
setTimeout(printFinish, 5000)
// Выполняем функцию printFinish после выполнения функции setTimeout
// setTimeout это встроенная функция, устанавливает таймер выполнения</pre>
  <p id="Uwgr"></p>
  <h3 id="RFxK">7.5. JSDOC-комментарии</h3>
  <p id="du87">Такие комментарии позволяют указать какие типы переменных должны использоваться внутри функции:</p>
  <pre data-lang="javascript" id="1e7F">/**
*
* @param {number} a
* @param {number} b
* @returns {number} Разница чисел
*/
function myFn(a, b) {
  let c = a - b
  return c
}</pre>
  <blockquote id="WeEm" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="X3by"></p>
  <p id="IItX"></p>
  <hr />
  <h2 id="bhSM">8. Области видимости</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="j9Pg">Глобальная область видимости - это наш файл в целом.</p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="vSFS">Локальная область видимости - это область внутри блока { }, между фигурными скобками.</p>
  </section>
  <p id="ijmK">Может существовать целая цепочка областей видимости и переменные объявленные в локальных областях видимости не будут учитываться в глобальной области видимости.</p>
  <p id="ghnk">При этом мы можем обратиться из локальной области видимости к переменной из глобальной области видимости, если в локальной области видимости не создана переменная с таким же именем:</p>
  <pre id="Zki3" data-lang="javascript">let a = &#x27;New York&#x27;
let b = &#x27;New York&#x27;
// Объявим две переменные и присвоим им значения New York

function myFn() {
  let b
  // Объявим переменную b внутри функции
  a = &#x27;Las Vegas&#x27;
  b = &#x27;Las Vegas&#x27;
  // Поменяем значения обеих переменных на Las Vegas
}

myFn()

console.log(a) // Получим &#x27;Las Vegas&#x27;
console.log(b) // Получим &#x27;New York&#x27;</pre>
  <blockquote id="4Uy2" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="gtDU"></p>
  <p id="NQSq"></p>
  <hr />
  <h2 id="JkiP">9. Ложные значение</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="jyCv">Ложные значения, это те значения, которые дают false при приведении их к логическому типу boolean.</p>
  </section>
  <p id="PqxB">К таким значениям относятся:</p>
  <pre id="PqxB" data-lang="javascript">false     // Ложь
0         // Ноль
&#x27;&#x27;        // Пустая строка
undefined // Значение неопределено
null      // Значения нет</pre>
  <p id="jgUt">Привести значение к типу <em>boolean</em> можно с помощью функции <em>Boolean()</em>:</p>
  <pre id="g6pi" data-lang="javascript">const a = &#x27;Alex&#x27;
const b = &#x27;&#x27;

console.log(Boolean(a)) // true
console.log(Boolean(b)) // false</pre>
  <blockquote id="C74o" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="UvFW"></p>
  <p id="AGbI"></p>
  <hr />
  <h2 id="1xHQ">10. Операторы</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="rdhT"><strong>Оператор </strong>— это встроенная в JS функция</p>
  </section>
  <pre id="KUB4" data-lang="javascript">=   // Оператор присваивания
,   // Позволяет объявить несколько переменных в одной строке
.   // Позволяет обращаться к свойствам объектов
()  // Оператор вызова функции
... // Оператор разделения объекта на свойства. Префиксный</pre>
  <h3 id="x1ld">Арифметические операторы</h3>
  <pre id="22Ec" data-lang="javascript">+  // Плюс. Используется для сложения. Конвертации строк в числа
-  // Минус
*  // Умножить
/  // Разделить
++ // Увеличивает значение переменной на 1
+= // Увеличение значения
-= // Уменьшение значения</pre>
  <h3 id="MbYO">Операторы сравнения</h3>
  <pre id="wpFD" data-lang="javascript">=== // Равно, оператор сравнения. Сравнивает как тип, так и значение
!== // Не равно
&lt;=  // Меньше
&gt;=  // Больше</pre>
  <h3 id="VUbh">Логические операторы</h3>
  <pre id="vUXo" data-lang="javascript">! // Не. Префиксный оператор. Возвращает тип boolean (true или false)
&amp;&amp; // И. Бинарный оператор. Возвращает значение одного из операндов
|| // Или. Бинарный оператор. Возвращает значение одного из операндов</pre>
  <h3 id="3TSo">Текстовые операторы</h3>
  <pre id="fw9u" data-lang="javascript">typeof // Проверяет тип значения. Префиксный
instanceof // Проверяет принадлежность объекта к классу. Бинарный
new // Создает новый экземпляр объекта. Префиксный
delete // С помощью него можно удалить свойство в объекте. Префиксный</pre>
  <h3 id="1EcV">Унарные и бинарные операторы</h3>
  <p id="cwSl">У <strong>унарных </strong>операторов всегда <strong>один </strong>операнд (аргумент). Они могут быть <strong>префиксные</strong>, когда оператор стоит <strong>перед </strong>оператором, и <strong>постфиксные</strong>, когда оператор идет <strong>после</strong> операнда.</p>
  <pre id="ng9K" data-lang="javascript">+myName         // + перед операндом
delete myName.a // delete перед операндом
myName++        // ++ после операнда
myName()        // () после операнда</pre>
  <p id="BuDp">У <strong>бинарных </strong>операторов всегда <strong>два </strong>операнда, они имеют <strong>инфиксный</strong> формат, когда оператор находится <strong>между </strong>операндами.</p>
  <pre id="AkAY" data-lang="javascript">a = 5
a + b
a += 5
a === b
a &amp;&amp; b</pre>
  <p id="mqAA">Существует тернарный оператор у которого 3 операнда, подробнее о нем <a href="#zf4q">тут.</a></p>
  <h3 id="ntym">Оператор <em>typyof</em></h3>
  <pre id="fd10" data-lang="javascript">typeof 10 // &#x27;number&#x27;
typeof 10 === &#x27;number&#x27; // true
typeof &#x27;Alex&#x27; // &#x27;string&#x27;

let a
typeof a === &#x27;undefined&#x27; // true</pre>
  <h3 id="JBxP">Оператор <em>!</em></h3>
  <p id="ghp3">Такой оператор чаще всего используется в условных инструкциях.</p>
  <pre id="zbhU" data-lang="javascript">!10  // false
!0   // true
!!10 // true
!!0  // false</pre>
  <h3 id="8H9j">Оператор &amp;&amp; и ||</h3>
  <p id="HknV">Операторы &amp;&amp; и || (<em>и</em> и <em>или</em>) являются операторами короткого замыкания.</p>
  <pre id="xao4" data-lang="javascript">/*Выражение 1*/ &amp;&amp; /*Выражение 2*/</pre>
  <ol id="nojG">
    <li id="Fbbn">Если выражение 1 <strong>ложно</strong>, то выражение 2 игнорируется и возвращается результат выражения 1</li>
    <li id="WWAD">Если выражение 1 истина, то смотрим на выражение 2 и т.д.</li>
  </ol>
  <p id="ZSaT">Т.е. <strong>истина</strong> передает эстафету следующему выражению, а ложное значение прекращает выполнение и возвращает результат выражения, с которым взаимодействовала последним. </p>
  <pre id="wwz2" data-lang="javascript">/*Выражение 1*/ || /*Выражение 2*/</pre>
  <ol id="3yN2">
    <li id="cIJn">Если выражение 1 <strong>истина</strong>, то выражение 2 игнорируется и возвращается результат выражения 1</li>
    <li id="KCBD">Если выражение 1 ложно, то смотрим на выражение 2 и т.д.</li>
  </ol>
  <p id="PxSK">Оператор || (<em>или</em>) работает противоположно оператору &amp;&amp; (<em>и</em>), ищет первое истинное значение и выдает его результат.</p>
  <pre id="6LPQ" data-lang="javascript">const a = 10
a &amp;&amp; console.log(&#x27;Hello&#x27;)
// т.к. значение 10 истина, то мы получим Hello в консоли,
// это результат второго выражения
let b
b &amp;&amp; console.log(&#x27;Hello&#x27;)
// второе выражение не будет выполнено, т.к. значение переменной b ложно</pre>
  <p id="YF09">Это можно использовать для выполнения тех или иных действий, в зависимости от результата других выражений.</p>
  <h3 id="dz4w">Оператор ...</h3>
  <p id="beft">!!!</p>
  <pre id="o9GP" data-lang="javascript">const button = {
  width: 200,
  text: &#x27;Buy&#x27;
}
// Создадим объект с двумя свойствами

const redButton = {
// Создадим еще один объект
  ...button,
  // Добавим в него свойства из объекта button
  color: &#x27;Red&#x27;
  // Добавим третье свойство color
}
console.table(redButton)
// Метод table() выведет объект в консоль в виде таблицы</pre>
  <blockquote id="gNYM" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="58E5"></p>
  <p id="bP2u"></p>
  <hr />
  <h2 id="kiJ8">11. Конкатенация (соединение) строк</h2>
  <p id="jcz9">Для соединения строк можно использовать оператор <em>+</em> , но нужно следить за наличием пробелов между строк и расставлять их при необходимости. Также с оператором <em>+</em> можно использовать но только строки, но и переменные: </p>
  <pre id="C1td" data-lang="javascript">console.log(&#x27;Hello, &#x27; + &#x27;world&#x27;)
// Получим Hello, world

const hello = &#x27;Hello&#x27;
const world = &#x27;world&#x27;
console.log(hello + &#x27;, &#x27; + world&#x27;)
// Также получим Hello, world</pre>
  <p id="0UkZ">Обратные кавычки &#x60;&#x60; также позволяют внутри себя использовать любые выражения с помочью ${ } , это называется <strong>шаблонная строка</strong> (template string literals). </p>
  <pre id="zENm" data-lang="javascript">
const name = &#x27;Denis&#x27;
const city = &#x27;Perm&#x27;
console.log(&#x60;My name is ${name} and I live in ${city}&#x60;)</pre>
  <p id="svDx">При конкатенации строки с числом, JS превратит число в строку:</p>
  <pre id="5Y8B" data-lang="javascript">const string = &#x27;Numder&#x27;
const number = 22
console.log(string + &#x27; &#x27; + number)
// Получим строку &#x27;Number 22&#x27;</pre>
  <blockquote id="IFeD" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="jkWP"></p>
  <p id="MpPR"></p>
  <hr />
  <h2 id="QsF8">12. Обработка ошибок</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="SVOg">После получения ошибки в JavaScript выполнение кода останавливается</p>
  </section>
  <p id="CbdT">С помощью инструкции <em><strong>throw new Error()</strong> можно искусственно создать ошибку</em></p>
  <pre id="sPWo" data-lang="javascript">const fnWithError = () =&gt; {
  throw new Error(&#x27;Some error&#x27;)
}
// Создаем функцию которая выдаст ошибку Some error

fnWithError()
// Вызываем функцию вызывающую ошибку и получаем непойманную ошибку

console.log(&#x27;Continue...&#x27;)
// Эта функция выполнена не будет</pre>
  <p id="uTV5">Непойманная ошибка (<em>Uncaught Error</em>) это та ошибка, которая остановит выполнение кода JS.</p>
  <p id="hVjL"><strong>Решение try/catch.</strong> В блоке кода <em><strong>try</strong></em> мы указываем код, в котором может возникнуть ошибка, а в блоге <em><strong>catch</strong></em> с параметром <em>(error)</em>, тот код, который будет выполнен в случае появления ошибки в блоке <em><strong>try</strong></em>:</p>
  <pre id="01ig" data-lang="javascript">const fnWithError = () =&gt; {
  throw new Error(&#x27;Some error&#x27;)
}
// Создаем функцию которая выдаст ошибку Some error
try {
  fnWithError()
} catch (error) {
    console.error(error)
    // Метод выведет всю информацию об ошибке
    console.log(error.message)
    // Выведет сообщение зашитое в ошибку, some error
}
console.log(&#x27;Continue...&#x27;)
// На этот раз выполнение кода продолжится и мы получим Continue...</pre>
  <blockquote id="jLD1" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="Oh4I"></p>
  <p id="pnuG"></p>
  <hr />
  <h2 id="s1lL">13. Инструкции</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="VCoF">В JavaScript существуют:</p>
    <ul id="Ot3Y">
      <li id="gkrO"><strong>Выражения.</strong> Они всегда возвращают значения.</li>
      <li id="SS4C"><strong>Инструкции.</strong> Они выполняют действия (например, объявление переменной).</li>
      <li id="879E"><strong>Выражения-инструкции.</strong> Выражения может быть инструкцией, но инструкция не может быть выражением (инструкций-выражений НЕТ)</li>
    </ul>
  </section>
  <p id="c1Xe">Инструкции следует завершать <em>; (точкой с запятой)</em>, а также каждую инструкцию писать на отдельной строке кода.</p>
  <p id="nxJb">После { } блока кода инструкций точка с запятой не требуется.</p>
  <pre id="fbPe" data-lang="javascript">const a = 5;
// в конце инструкции стоит ;

if (a &gt; b) {
  console.log(&#x27;Hello&#x27;);
}
// Блоки кода {} условных инструкций не требуют ;

for (let i = 0; i++; i &lt;5) {
  console.log(i);
}
// Блоки кода {} циклов тоже не требуют ;</pre>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="krf0">Точки с запятой в JavaScript можно опускать</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="bCPm">Инструкции <strong>нельзя</strong> использовать как аргументы в функциях</p>
  </section>
  <p id="ABB5">Если в конце выражения поставить точку с запятой, то мы явно укажем, что это инструкция и JS перестанет считать его выражением, даже если это выражение-инструкция</p>
  <pre id="bvYi" data-lang="javascript">function myFn(a) {
  console.log(a);
}
let c = 10
//
myFn(c = c + 1)
// Функция выдаст результат выражения, это 11
myFn(c = c + 1;)
// Поставив точку с запятой, мы указали что это инструкция,
// поэтому функция выполнена не будет</pre>
  <blockquote id="KOPZ" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="EDSc"></p>
  <p id="Cmpr"></p>
  <hr />
  <h2 id="jFL1">14. Массивы</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="lsRV"><strong>Массив</strong> — это объект с цифровыми именами свойств: 0, 1, 2, 3 и т.п.</p>
  </section>
  <p id="NxKQ">Для создания массива используются квадратные скобки, в которых указываются будущие значения массива по порядку.</p>
  <pre id="NxKQ" data-lang="javascript">const myNumbers = [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]
console.log(myNumbers)
// Получим массив [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]</pre>
  <p id="3nlg">Также для создания массива можно использовать ключевое слово <em>new</em> для создание нового экземпляра класса <em>Array()</em>, в качестве аргументов указываются будущие значения массива по порядку.</p>
  <pre id="4hJA" data-lang="javascript">const myNumbers = new Array (&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;)
console.log(myNumbers)
// Получим массив [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="aLXc">Свойства в массиве начинаются с нуля: [0: &#x27;one&#x27;, 1: &#x27;two&#x27;, 2: &#x27;three&#x27;]</p>
  </section>
  <p id="8Dlf"></p>
  <h3 id="4gsP">14.1. Чтение значений массива</h3>
  <p id="9TQA">Вместо точечной записи, для обращения к цифровым свойствам массива используют [] квадратные скобки:</p>
  <pre id="ESNU" data-lang="javascript">const myArray = [1, true, &#x27;Petr&#x27;]
console.log(myArray)
// Получим [1, true, &#x27;Petr&#x27;]
console.log(myArray[0])
// Получим 1
console.log(myArray[1])
// Получим true
console.log(myArray.length)
// Получим 3, это кол-во значений в массиве. Свойство length имеет
// любой массив по умолчанию и обновляется оно автоматически</pre>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="dXek">Обновлять значения поля<em> length</em> вручную <strong>не рекомендуется</strong></p>
  </section>
  <pre id="sNTm" data-lang="javascript">const myNumbers = new Array (1, 2, 3, 4)
// Создадим массив с 4-я значениями
myNumbers[1] = &#x27;new&#x27;
console.log(myNumbers)
// Получим [1, &#x27;new&#x27;, 2, 3, 4] т.к мы присвоили свойству 1 значение &#x27;new&#x27;
myNumbers[4] = &#x27;four&#x27;
// Добавили новое значение в массив, т.к. свойства 4 не было</pre>
  <p id="4Q5o"></p>
  <h3 id="PCTL">14.2. Методы массивов</h3>
  <p id="3W40">Метод <em><strong>push()</strong></em> добавляет аргумент как элемент<strong> в конец</strong> массива:</p>
  <pre id="0835" data-lang="javascript">const myNumbers = [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]
// Создадим массив [0: &#x27;one&#x27;, 1: &#x27;two&#x27;, 2: &#x27;three&#x27;]
myNumbers.push(&#x27;PUSH&#x27;)
// Получим массив [0: &#x27;one&#x27;, 1: &#x27;two&#x27;, 2: &#x27;three&#x27;, 3: &#x27;PUSH&#x27;, ]</pre>
  <p id="Zm7z">Метод <em><strong>pop()</strong></em> удаляет <strong>последний</strong> элемент массива и возвращает его значение:</p>
  <pre id="QMBV" data-lang="javascript">const myNumbers = [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]
// Создадим массив [0: &#x27;one&#x27;, 1: &#x27;two&#x27;, 2: &#x27;three&#x27;]
const myPop = myNumbers.pop()
// Получим массив [0: &#x27;one&#x27;, 1: &#x27;two&#x27;] и переменную myPop
// со значением &#x27;three&#x27;, т.к. метод pop возвращает значение</pre>
  <p id="2pSx">Метод <em><strong>unshift()</strong></em> добавляет аргумент как элемент <strong>в начало</strong> массива. Все названия элементов сдвинуться на 1 вперед:</p>
  <pre id="e5C0" data-lang="javascript">const myNumbers = [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]
// Создадим массив [0: &#x27;one&#x27;, 1: &#x27;two&#x27;, 2: &#x27;three&#x27;]
myNumbers.unshift(&#x27;UNSHIFT&#x27;)
// Получим массив [0: &#x27;UNSHIFT&#x27;, 1: &#x27;one&#x27;, 2: &#x27;two&#x27;, 3: &#x27;three&#x27;]</pre>
  <p id="qrNL">Метод <em><strong>shift()</strong></em> удаляет <strong>первый</strong> элемент массива и возвращает его значение:</p>
  <pre id="a2RA" data-lang="javascript">const myNumbers = [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]
// Создадим массив [0: &#x27;one&#x27;, 1: &#x27;two&#x27;, 2: &#x27;three&#x27;]
const myShift = myNumbers.shift()
// Получим массив [0: &#x27;two&#x27;, 1: &#x27;three&#x27;] и переменную myShift
// со значением &#x27;one&#x27;, т.к. метод shift возвращает значение</pre>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="c9YW">Методы <em>push()</em>,<em> pop()</em>,<em> unshift() </em>и<em> shift() </em><strong>мутируют</strong> изначальный массив!</p>
  </section>
  <p id="CsCF">Метод<em> <strong>forEach()</strong> </em>перебирает все элементы массива и применяет к ним <strong>функцию</strong>, которую мы прописываем как аргумент метода. Возвращает значение <em>undefined</em>.</p>
  <pre id="pqQW" data-lang="javascript">const myNumber = [1, 2, 3, 4]
myNumber.forEach(element =&gt; console.log(element * 2))
// Метод forEach() переберет все элементы масива, умножит их на 2
// и выведет их в консоль</pre>
  <p id="mazO">Метод<em><strong> map()</strong></em> перебирает все элементы массива и применяет к ним функцию также как <em>forEach</em>, но возвращает новый массив.</p>
  <pre id="7LbI" data-lang="javascript">const myNumber = [1, 2, 3, 4]
const newMyNumber = myNumber.map(element =&gt; element * 2)
// Получим новый массив со значениями [2, 4, 6, 8]</pre>
  <blockquote id="CWHN" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="fCQf"></p>
  <p id="P16P"></p>
  <hr />
  <h2 id="mhYq">15. Деструктуризация</h2>
  <p id="yK37">Позволяет, используя<em> {}</em> фигурные скобки, создавать переменные на остнове свойств объектов:</p>
  <pre id="zgdZ" data-lang="javascript">const myName = {
  name: &#x27;James&#x27;,
  surname: &#x27;Crock&#x27;,
  age: 35,
}

const {name, surname} = myName
// Будет создана переменная name со значением &#x27;James&#x27;
// и переменная surname со значением &#x27;Crock&#x27;</pre>
  <p id="Cz5n">Похожим образом можно создавать переменные на основе свойств массивов. В фигурных скобках мы указываем названия будущих переменных, а значения для них берутся из массива по порядку:</p>
  <pre id="6YaB" data-lang="javascript">const fruits = [&#x27;apple&#x27;, &#x27;banana&#x27;]
const [fruitOne, fruitTwo] = fruits
// Получим переменную fruitOne = &#x27;apple&#x27; и fruitTwo = &#x27;banana&#x27;</pre>
  <p id="Iwdf">В <strong>функциях</strong> также можно использовать деструктуризацию для передачи нужных свойств объекта как аргументов</p>
  <pre id="djsJ" data-lang="javascript">const userProfile = {
  name: &#x27;Pavel&#x27;,
  commentsQty: 23,
  hasSignedAgreement: false
}
const userInfo = ({name, commentsQty}) =&gt; {
// В переданом объекте ищем свойства name и commentsQty
  if (!commentsQty) {
  // Если в объекте нет свойства commentsQty, то мы возвращаем:
    return &#x60;User ${name} has no comments&#x60;
  }
  return &#x60;User ${name} has ${commentsQty} comments&#x60;
}
userInfo(userProfile)
// Получим &#x27;User Pavel has 23 comments&#x27;</pre>
  <blockquote id="TXYJ" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="wQhy"></p>
  <p id="SfRF"></p>
  <h2 id="Q0Lp">16. Условные инструкции</h2>
  <p id="YFPl">Инструкция <em>if</em>. Блок кода в {} выполняется однократно если условие правдиво, если ложно - игнорируется:</p>
  <pre id="oUUB" data-lang="javascript">let val = 10

if (val &gt; 5) {
// Если val больше 5, то мы выполняем блок кода в {}
  val += 20
}

console.log(val)
// Получим 30</pre>
  <pre id="GKFX" data-lang="javascript">const person = {
  age: 20
}

if (!person.name) {
// Если у объекта НЕТ свойства name, то мы выполняем блок кода в {}
  console.log(&#x27;Имя не указано&#x27;)
}</pre>
  <p id="8vcU">Инструкция <em>if else</em>. В этой инструкции можно использовать 2 блока кода. Первый блок кода { } выполняется если условие правдиво, а второй { } - если ложно:</p>
  <pre id="yQBo" data-lang="javascript">const person = {
  age: 20,
  name:&#x27;Diana&#x27;
}

if (person.name) {
// Если у объекта ЕСТЬ свойство name, то мы выполняем блок кода в {}
  console.log(&#x60;Имя: ${person.name}&#x60;)
} else {
  // Если свойства name НЕТ, то выполняем блок кода else {}
  console.log(&#x27;Имя не указано&#x27;)
}</pre>
  <p id="yZsA">Можно комбинировать инструкции if else:</p>
  <pre id="9QLD" data-lang="javascript">if (/*Условие1*/) {
 // Блок кода, выполняемый однократно, если Условие1 правдиво
} else if (/*Условие2*/) {
 // Блок кода, выполняемый однократно, если Условие2 правдиво
} else {
 // Блок кода, выполняемый однократно, если предыдущие условия ложны
}</pre>
  <p id="WKIM">Применение инструкции if в функциях:</p>
  <pre id="wGJs" data-lang="javascript">const sumPositiveNumbers = (a, b) =&gt; {
  if (typeof a !== &#x27;number&#x27; || typeof b !== &#x27;number&#x27;) {
  // Если a или b не являются числами, то возвращаем
    return &#x27;One of the arguments is not a number&#x27;
  }
  if (a &lt;= 0 || b &lt;= 0) {
  // Если a или b меньше или равны нулю, то возвращаем
    return &#x27;Numbers are not positive&#x27;
  }
  return a + b
  // Если передыдущие условия ложны, то выполняется сложение
}</pre>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="NOpK">Инструкции <em>if </em>и <em>if else</em> выполняются однократно</p>
  </section>
  <p id="1NCU">Инструкция<strong> <em>switch</em>.</strong> Действие выполняется если выражение в условии равно <em><strong>case</strong></em> (кейс/случай). Если выражение равно нескольким кейсам, то выполнятся оба кейса.</p>
  <p id="PkuS">Если в кейсе стоит ключевое слово <em><strong>brake</strong></em>, то после удачного выполнения этого кейса выполнение инструкции <em><strong>switch</strong></em> прекратиться, даже если какие-то кейсы еще подходят под условия:</p>
  <pre id="9pso" data-lang="javascript">switch (Выражение) {
  case A:
    // Действие, если Выражение === A
    break
    // brake это инструкция для прикращения инструкции после 
    // удачного выполнения действия в кейсе
  case B:
    // Действие, если Выражение === B
    break
  default:
    // Действие по умолчанию
}</pre>
  <pre id="AiUs" data-lang="javascript">const winterMonth = 2

switch (winterMonth) {
  case 12:
    console.log(&#x27;December&#x27;)
    break
  case 1:
    console.log(&#x27;January&#x27;)
    break
  case 2:
    console.log(&#x27;February&#x27;)
    break
  default:
    console.log(&#x27;Its not a winter month&#x27;)
}</pre>
  <blockquote id="mN16" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="3oFE"></p>
  <p id="aS7R"></p>
  <hr />
  <h2 id="zf4q">17. Тернарный оператор</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="LZvB">Конструкция с тернарным оператором является <strong>выражением</strong></p>
  </section>
  <p id="JwPm">У <strong>тернарного</strong> оператора 3 операнда, его операнды пишутся между <em>?</em> и <em>: </em>Конструкция с тернарным оператором является выражением и возвращает результат одного и операндов:</p>
  <pre id="alpF" data-lang="javascript">Условие (Условие) // Условием может быть любое выражение, в том числе
                      // и с другими операторами
  ? Операнд Один      // Выражение, выполняется если условие ПРАВДИВО
  : Операнд Два      // Выражение, выполняется если условие ЛОЖНО</pre>
  <pre id="O9G7" data-lang="javascript">const value1 = 11
const value2 = 25
value1 &amp;&amp; value2
  ? myFunction(value1, value2)
  // Если value1 и value2 правдивы, то вызываем функцию используя их
  // как аргументы
  : myFunction()
  // Если значения ложны, то вызываем функцию без аргументов</pre>
  <pre id="zCps" data-lang="javascript">let value = 11
console.log(value &gt;= 0 ? value : -value)
// Если value больше или равно нулю, то возвращаем результат value
// Получим 11

value = -5
const result = value &gt;= 0 ? value : -value
// Присваиваем переменной результат работы тернарного оператора,
// это возможно т.к. это выражение
console.log(result)
// значение result будет -value, а --5 это 5</pre>
  <blockquote id="qBNg" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="cpk9"></p>
  <p id="GOXV"></p>
  <hr />
  <h2 id="G2Xo">18. Циклы</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="uaF4">Циклы — это<strong> инструкции</strong>, они не возвращают значение, а только совершают действие</p>
  </section>
  <h3 id="rAb2"></h3>
  <h3 id="pZ2j">18.1. Цикл <em>for</em></h3>
  <pre id="mByZ" data-lang="javascript">for (Начальная инструкция; Условие; Итерационное действие) {
  // Блок кода, выполняемый на КАЖДОЙ итерации
}</pre>
  <p id="IPJ5">Итерационное действие повторяет выполняется до тех пор, пока условие верно.</p>
  <pre id="fDC6" data-lang="javascript">for (let i = 0; i &lt; 5; i++) {
  console.log(i)
}</pre>
  <p id="0jNT">Циклы можно использовать для массивов, хотя это <strong>не рекомендуется</strong>. Для массивов есть методы <em>forEach</em>, <em>map</em>, <em>reduce</em></p>
  <pre id="1g65" data-lang="javascript">const myNumbers = [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]

for (let i = 0; i &lt; myNumbers.length; i++) {
  console.log(myNumbers[i])
}
// Получим в консоли:
// one
// two
// three</pre>
  <p id="BEYr"></p>
  <h3 id="cSFc">18.2. Метод массивов <em>forEach</em></h3>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="13uR">Метод <em>forEach</em> вызывается столько раз, сколько элементов в массиве</p>
  </section>
  <pre id="mlLg" data-lang="javascript">const myNumbers = [&#x27;one&#x27;, &#x27;two&#x27;, &#x27;three&#x27;]

myNumbers.forEach((element, index) =&gt; {
  console.log(element, index)
})
// Получим в консоли:
// one 0
// two 1
// three 2</pre>
  <p id="U9IG"></p>
  <h3 id="Vfyg">18.3. Циклы <em>while и do while</em></h3>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="yPC6"><em>Цикл<strong> while</strong></em> выполняет инструкцию пока условие правдиво (true)</p>
  </section>
  <pre id="AuDx" data-lang="javascript">while (Условие) {
  // Блок кода, выполняемый при каждой итерации
}</pre>
  <p id="DgVN">Мы должны сами контролировать изменение изначального условия внутри инструкции:</p>
  <pre id="hslZ" data-lang="javascript">let myNumber = 0

while (myNumber &lt; 5) {
  console.log(myNumber)
  myNumber++
  // Меняем переменную, которое влияет на условие
  // позволяя выйти из цикла
}

// Получим в консоли:
//   0
//   1
//   2
//   3
//   4
// Когда myNumber станет равна 5 цикл закончится</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="ZzRq">Цикл <em><strong>do while</strong>, в</em> отличии от цикла <em>while</em> всегда выполняется хотя бы 1 раз, т.к. инструкция выполняется до просмотра условия</p>
  </section>
  <pre id="sDnI" data-lang="javascript">do {
  // Блок кода, выполняемый при каждой итерации
} while (Условие)</pre>
  <p id="Ahuo">Мы также должны сами контролировать изменение изначального условия внутри инструкции:</p>
  <pre id="6m1t" data-lang="javascript">let myNumber = 0

do {
console.log(myNumber)
myNumber++
} while (myNumber &lt; 5)</pre>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="MMFK">Если условие изначально ложно (false), то условие НЕ выполнится ни разу</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="zWLZ">Если условие будет бесконечно правдиво (true), то цикл будет выполнятся <strong>бесконечно</strong></p>
  </section>
  <p id="s9a9"></p>
  <h3 id="ehhy">18.4. Цикл <em>for in</em> для объектов</h3>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="lEoM">Блок инструкций <em><strong>for in</strong></em> выполняется для каждого свойства объекта или элемента массива</p>
  </section>
  <pre id="xUVO" data-lang="javascript">for (key in Object) {
  // Действия с каждым свойством объекта
  // Значение свойства - Object[key]
}</pre>
  <p id="fTQc"><em>Key</em> будет представлять название свойства в объекте, а <em>Object</em> это переменная указывающая на объект. Используя такой цикл можно перебрать все свойства объекта:</p>
  <pre id="DI7c" data-lang="javascript">const myObject = {
  x: 20,
  y: true,
  z: &#x27;text&#x27;
}

for (const key in myObject) {
  console.log(key, myObject[key])
  // key указывает на название свойства
  // myObject[key] указывает на значение свойств
}

// Получим в консоли:
//   x 20
//   y true
//   z text</pre>
  <p id="vT6P">Точно также цикл <em>for in</em> применяется к массивам:</p>
  <pre id="ULrX" data-lang="javascript">const myArray = [true, 20, &#x27;text&#x27;, null]

for (const key in myArray) {
  console.log(key, myArray[key])
}

// Получим в консоли:
//   true
//   20
//   text
//   null</pre>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="UgaN">Для массивов <strong>рекомендуется</strong> применять метод <em>forEach()</em></p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="kxGR">Цикл <em>for in</em> повторяется столько раз, сколько свойств в объекте или элементов в массиве</p>
  </section>
  <p id="zYRO"></p>
  <h3 id="RNpg">18.5. Метод <em>forEach()</em> для объектов</h3>
  <p id="Mv0K">Можно применить метод массивов <em>forEach()</em> для объектов, предварительно получив все имена свойств (ключи) этого объекта в вид массива с помощью метода <em>keys()</em> глобального объекта <em>Object:</em></p>
  <pre id="G3nT" data-lang="javascript">const myObject = {
  x: 20,
  y: true,
  z: &#x27;text&#x27;
}

Object.keys(myObject).forEach(key =&gt; {
  console.log(key, myObject[key])
})

// Получим в консоли:
//   x 20
//   y true
//   z text</pre>
  <p id="wa9D">Аналогично, но используя метод <em>values()</em> можно перебирать значения свойств:</p>
  <pre id="AI84" data-lang="javascript">const myObject = {
  x: 20,
  y: true,
  z: &#x27;text&#x27;
}

Object.values(myObject).forEach(value =&gt; {
  console.log(value)
})

// Получим в консоли:
//   20
//   true
//   text</pre>
  <p id="yx4a"></p>
  <h3 id="lAEG">18.6. Цикл <em>for of</em></h3>
  <pre id="iI2W" data-lang="javascript">for (Element of Iterable) {
  // Действие с определенным элементом
}</pre>
  <p id="x7QS"><em><strong>Iterable</strong></em> это любое значение по которому можно итерироваться (элементы которого можно перебирать):</p>
  <pre id="Z7tQ" data-lang="javascript">const myString = &#x27;Hello&#x27;

for (const letter of myString) {
  console.log(letter)
}
// Получим в консоли:
//   H
//   e
//   l
//   l
//   o</pre>
  <p id="jXNt">Цикл <em>for of</em> также подходит для массивов:</p>
  <pre id="rf0N" data-lang="javascript">const myArray = [true, 20, &#x27;text&#x27;, null]

for (const element of myArray) {
  console.log(element)
}
// Получим в консоли:
//   true
//   20
//   text
//   null</pre>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="W4VF">Для массивов <strong>рекомендуется</strong> применять метод <em>forEach()</em></p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="ntas"><em> <strong>for of </strong>д</em>ля объектов НЕ подходит, т.к. объекты это НЕ итерируемые элементы</p>
  </section>
  <blockquote id="Uew4" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="rU4w"></p>
  <p id="RQm4"></p>
  <hr />
  <h2 id="RyWC">19. Модули</h2>
  <p id="aHYb">Чтобы код был более читаемым используют модули. Один модуль это отдельный файл JS с частью кода и решающий какую-то одну задачу.</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="BXft">Модули должны быть одноцелевыми</p>
  </section>
  <p id="rtM2">В JS есть возможность экспорта и импорта переменных из модуля в модуль, для этого используется ключевые слова <em><strong>export</strong> <strong>default</strong></em>, <strong><em>export</em></strong> и <em><strong>import</strong></em></p>
  <section style="background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="w8m4">Все экспортируемые переменные <strong>рекомендуется</strong> всегда писать в самом конце файла, а импортируемые в самом начале</p>
  </section>
  <p id="CCQn"></p>
  <h3 id="nNiH"><strong>Export default</strong></h3>
  <p id="FzNK">Если нужно экспортировать из файла <strong><em>file1.js</em></strong> только одну переменную, лучше использовать <em><strong>export default:</strong></em></p>
  <pre id="jZmV" data-lang="javascript">// file1.js

const myName = () =&gt; {
  console.log(&#x27;Bob&#x27;)
}
  
export default myName</pre>
  <p id="Bg5p">В файле <strong><em>file2.js</em> </strong>делаем импорт. Для этого используем ключевое слово import и указываем путь к файлу <strong><em>file1.js</em></strong> с помощью from:</p>
  <pre id="x25h" data-lang="javascript">// file2.js

import printMyName from &#x27;./file1.js&#x27;
// Укажем переменной новое имя printMyName

printMyName()
// Получим &#x27;Bob&#x27;</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="3NUO">При импорте переменной, экспортируемой с помощью <em>export default,</em> можно указать ей любое новое имя</p>
  </section>
  <p id="C1Oo"></p>
  <h3 id="8DTg">Export</h3>
  <p id="dtBu">При экспорте нескольких переменных из файла <strong><em>file1.js</em> </strong>используем ключевое слово <strong><em>export</em>:</strong></p>
  <pre id="ATve" data-lang="javascript">const sum = (a, b) =&gt; a + b
const mult = (a, b) =&gt; a * b

export {sum, mult}</pre>
  <p id="8gOh">В файле <strong><em>file2.js</em> </strong>делаем импорт. Для этого используем ключевое слово import и {} фигурные скобки, в которых перечисляем имена импортируемых переменных:</p>
  <pre id="Jhog" data-lang="javascript">import {sum, mult} from &#x27;./file1.js&#x27;

console.log(sum(10, 2))
console.log(mult(10, 2))</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="FMRs">При импорте нескольких переменных с помощью <em>export</em>, нужно указывать те же самые имена переменных, что были при экспорте. </p>
  </section>
  <p id="Rvyb">Но есть возможность переименовать переменную после экспорта:</p>
  <pre id="R8Se" data-lang="javascript">import {sum as sumRenamed, mult} from &#x27;./file1.mjs&#x27;
// Указываем переменной sum новое имя sumRenamed</pre>
  <blockquote id="uyRN" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="PF1V"></p>
  <p id="3EbJ"></p>
  <hr />
  <h2 id="k6X7">20. Классы и прототипы</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="4o9y"><strong>Классы </strong>позволяют создавать <strong>прототипы</strong> для объектов</p>
    <p id="6OY5">На основании <strong>прототипов</strong> создаются <strong>экземпляры</strong></p>
    <p id="kLkh">Экземпляры<strong> наследуют свойства и методы</strong> прототипов</p>
  </section>
  <p id="v8o1">Для создания класса используется ключевое слово <em><strong>class.</strong></em> Название нового класса пишется с использованием <em>CamalCase</em> нотации (с большой буквы)</p>
  <pre id="ZvOx" data-lang="javascript">class Comment {
// Пишем Comment с большой буквы т.к. это название класса
  constructor(a) {
  // Метод constructor вызывается только когда создается новый экземпляр
  // Его параметры, это аргументы заданые при создании экземпляра
    this.text = a
    this.votesQty = 0
    // Слово this указывает на будущий экземпляр класса.
    // То есть оно как бы замениться именем экземпляра,
    // который мы будем создавать 
  }
  upvote() {
  // Метод upvote будет доступен всем экземплярам нового класса Comment
  // Это называется наследованием
    this.votesQty += 1
  }
}
const firstComment = new Comment(&#x27;First Comment&#x27;)
// Оператор new вызывает функцию constructor с аргументом &#x27;First Comment&#x27;
console.log(firstComment)
// Получим объект firstComment {text: &#x27;First Comment&#x27;, votesQty: 0}
// Ему также доступны наследованные методы constructor и upvote</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="kijN">Наследование происходит по цепочке:</p>
    <ul id="VoPf">
      <li id="EGmm"><strong>Глобальный класс</strong> <em>Object</em> содержит методы доступные всем объектам в JavaScript.</li>
      <li id="wqvh"><strong>Созданный нами класс</strong> <em>Comment</em> содержит созданные нами методы и наследует все методы глобального класса <em>Object</em>.</li>
      <li id="KJJJ"><strong>Экземпляр класса</strong> <em>firstComment</em> наследует все методы обоих родительских классов <em>Comment</em> и <em>Object</em>.</li>
    </ul>
  </section>
  <p id="U56G">Чтобы проверить принадлежность <strong>объекта</strong> к тому или иному <strong>классу</strong> можно использовать оператор <em>instanceof:</em></p>
  <pre id="JVu2" data-lang="javascript">firstComment instanceof Comment
// Получим true т.к. firstComment экземпляр класса Comment
firstComment instanceof Object
// Получим true т.к. firstComment также является экземпляром
// глобального класса Object</pre>
  <p id="cmZr">Чтобы проверить принадлежность <strong>свойства</strong> к тому или иному <strong>объекту</strong> можно использовать метод <em>hasOwnProperty()</em>:</p>
  <pre id="l20f" data-lang="javascript">const firstComment = new Comment(&#x27;First Comment&#x27;)

firstComment.hasOwnProperty(&#x27;text&#x27;)
// Проверяем есть ли у объекта firstComment собственное свойство text
// Получаем true, такое свойство есть
firstComment.hasOwnProperty(&#x27;votesQty&#x27;)
// Получаем true, такое свойство тоже есть
firstComment.hasOwnProperty(&#x27;upvote&#x27;)
// Получаем false, такого свойства нет, это свойство родительского
// класса Comment
firstComment.hasOwnProperty(&#x27;hasOwnProperty&#x27;)
// Получаем false, такого свойства тоже нет, это свойство родительского
// глабального класса Object</pre>
  <p id="VAkG"></p>
  <h3 id="EPqg">20.1 Статический метод</h3>
  <p id="G9VZ">С помощью ключевого слова <em><strong>static</strong></em> можно создавать методы, которые <strong>НЕ будут наследоваться</strong> экземплярами классов:</p>
  <pre id="rFu4" data-lang="javascript">class Comment {
  constructor(text) {
    this.text = text
    this.votesQty = 0
  }
  upvote() {
    this.votesQty += 1
  }
  static mergeComments(first, second) {
    return &#x60;${second} ${second}&#x60;
}

Comment.mergeComments(&#x27;First Comment&#x27;, &#x27;Second Comment&#x27;)
// Метод будет доступен как свойство объекта Comment</pre>
  <p id="jjVz"></p>
  <h3 id="9Wm3">20.2 Расширение других классов</h3>
  <p id="AKoF">С помощью ключевого слова <em>extends</em> можно расширить существующий класс. Создастся новый класс с использованием конструктора из расширяемого класса, в данном случае класса массивов <em>Array</em>:</p>
  <pre id="ARQj" data-lang="javascript">class NumbersArray extends Array {
  sum() {
    return this.reduce((el, acc) =&gt; acc += el, 0)
  }
}

const myArray = new NumbersArray(2, 5, 7)
//
console.log(myArray)
myArray.sum()</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="OaRN">Наследование происходит по цепочке:</p>
    <p id="8vAi">myArray =&gt; NumbersArray =&gt; Array =&gt; Object</p>
  </section>
  <p id="h96e"></p>
  <h3 id="A2da">20.3 Примитивные значения</h3>
  <p id="Y5Sm">Примитивные значения ведут себя как объекты и наследуют методы родительских классов:</p>
  <pre id="5ASb" data-lang="javascript">const myName = &#x27;Vladimir&#x27;
// Создадим переменную примитивного типа String
console.log(myName)
// Получим &#x27;Vladimir&#x27;
console.log(myName.toUpperCase())
// Получим &#x27;VLADIMIR&#x27;. Мы использовали метод класса String
const mySecondName = new String(&#x27;Putin&#x27;)
// Создадим новый экземпляр класса String
mySecondName
// Получим объект, экземпляр класса String:
// String {&#x27;Putin&#x27;}
//   0: &quot;P&quot;
//   1: &quot;u&quot;
//   2: &quot;t&quot;
//   3: &quot;i&quot;
//   4: &quot;n&quot;
//   length: 5</pre>
  <blockquote id="Mv4z" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="vrWS"></p>
  <p id="lusm"></p>
  <h2 id="Ez0U">21. Промисы</h2>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Iy42">Промисы позволяют обрабатывать <strong>отложенные</strong> во времени события</p>
  </section>
  <p id="130J">Промисы нужны чтобы иметь возможность обрабатывать другие запросы пока ждем ответа на предыдущие. Это возможно благодаря асинхронным запросам, ответ на которые JavaScript может получать не сразу, а через какое-то время</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="uCTy">Промис может вернуть <strong>ошибку</strong>, если результат предоставить <strong>невозможно</strong></p>
  </section>
  <p id="hiyh">У промиса может быть 3 состояния:</p>
  <ul id="6Ent">
    <li id="AYsO">Ожидание (pending), когда промис создан;</li>
    <li id="XULL">Исполнен (resolve), когда вернул результат;</li>
    <li id="X2XS">Отклонен (reject), когда вернул ошибку.</li>
  </ul>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="WazU">Промис - это объект, глобальный класс в JS. </p>
  </section>
  <p id="Rie6">Создаются промисы путем создания экземпляра класса <em>Promise</em>:</p>
  <pre id="h5V1" data-lang="javascript">const myPromise = new Promise((resolve, reject) =&gt; {
  // Выполнение асинхронных действий
  //
  // Внутри этой функции нужно в результате вызывать одну из функций
  // resolve или reject
})</pre>
  <p id="WsOU"></p>
  <h3 id="b43K">Получение результата промиса</h3>
  <pre id="X4HT" data-lang="javascript">myPromise
  .then(value =&gt; {
    // Действия в случае успешного исполнения Промиса
    // Значение value - это значение, переданное в вызове функции
    // resove внутри промиса
  })
  .catch(error =&gt; {
    // Действия в случае отклонения Промиса
    // Значение error - это значение, переданное в вызове функции
    // reject внутри промиса
  })</pre>
  <p id="EQzl">Для тестирования отправки и получения запросов можно использовать сервис</p>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="FtRZ" data-align="center"><a href="https://jsonplaceholder.typicode.com/" target="_blank">jsonplaceholder.typicode.com</a></p>
  </section>
  <p id="OfCa">Когда мы отправляем <em>fetch</em>-запрос, он отправляет назад промис.</p>
  <pre id="Urua" data-lang="javascript">fetch(&#x27;https://jsonplaceholder.typicode.com/todos&#x27;)
//
  .then(response =&gt; response.json())
  // Получаем данные при исполнении промиса функцией fetch,
  // но т.к. там нет объектов и масивов JS мы присваиваем эти
  // данные переменной response и применяем к ней метод json()
  // Метод json() также возвращает промис
  .then(json =&gt; console.log(json))
  // При возвращении промиса от метода json(), нужен еще один метод
  // then, который выводит его в консоль
  .catch(error =&gt; console.error(error))
  // Если один из промисов отклонен, выводим ошибку в консоль

  </pre>
  <pre id="Urua" data-lang="javascript">fetch(&#x27;https://jsonplaceholder.typicode.com/todos&#x27;)
  .then(response =&gt; {
    console.log(response)
    return response.json()
  }) 
// Такая функция позволит нам увидеть ответ от сервера в его исходном виде
  .then(json =&gt; console.log(json))
  .catch(error =&gt; console.error(error))</pre>
  <p id="pIHj">Чтобы избежать двойного использования .then мы можем создать собственный промис, который будет получать данные, обрабатывать через JSON и вызвращать результат в виде переменной getData. Такой вариант кода можно поместить в модуль и обращаться к нему по мере необходимости:</p>
  <pre id="SLYy" data-lang="javascript">// Файл module1.js
const getData = (url) =&gt; 
  new Promise((resolve, reject) =&gt; 
    fetch(url)
      .then(response =&gt; response.json())
      .then(json =&gt; resolve(json))
      .catch(error =&gt; reject(error))
  )

// Файл module2.js
getData(&#x27;https://jsonplaceholder.typicode.com/todos&#x27;)
  .then(data =&gt; console.log(data))
  .catch(error =&gt; console.log(error.massage))</pre>
  <blockquote id="tMKU" data-align="center"><a href="#TSUX">Наверх</a></blockquote>
  <p id="0MHO"></p>
  <p id="iOc1"></p>
  <h2 id="dEYM">22. Асинхронные функции</h2>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="mkMK">ASYNC/AWAIT - Специальный синтаксис для упрощения работы с промисами</p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="kbWm">Асинхронная функция - это функция которая всегда возвращает <strong>промис</strong></p>
  </section>
  <p id="EK9a">Для создания асинхронной функции используется слово async:</p>
  <pre id="hXah" data-lang="javascript">async function asyncFn() {
  // Стандартный синтаксис функции
}
const asyncFn = async () =&gt; {
  // Стрелочная функция
}</pre>
  <p id="hXah">!!!</p>
  <pre id="hXah" data-lang="javascript">const asyncFn = async () =&gt; {
  throw new Error(&#x27;There was an error!&#x27;)
}

asyncFn()
  .then(value =&gt; console.log(value))
  .catch(error =&gt; console.log(error.massage))</pre>
  <p id="lDdb">С помощью ключевого слова <em>await</em> можно внутри асинхронной функции ожидать результата другого промиса:</p>
  <pre id="EhUi" data-lang="javascript">const timerPromise = () =&gt;
  new Promise( (resolve, reject) =&gt; setTimeout(() =&gt; resolve(), 5000) )
  
const asyncFn = async () =&gt; {
  console.log(&#x27;Start&#x27;)
  const start = performance.now()
  await timerPromise()
  const end = performance.now()
  console.log(&#x27;Finish&#x27;, end - start)
}

asyncFn()</pre>
  <p id="oCD7"></p>
  <h3 id="tdzj">22.1. Переход с промисов на ASYNC/AWAIT</h3>
  <p id="u2EL">Возьмем предыдущий пример использования промисов:</p>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <pre id="WqvX" data-lang="javascript">// Первая часть
const getData = (url) =&gt; 
  new Promise((resolve, reject) =&gt; 
    fetch(url)
      .then(response =&gt; response.json())
      .then(json =&gt; resolve(json))
      .catch(error =&gt; reject(error))
  )

// Вторая часть
getData(&#x27;https://jsonplaceholder.typicode.com/todos&#x27;)
  .then(data =&gt; console.log(data))
  .catch(error =&gt; console.log(error.massage))</pre>
  </section>
  <p id="KeXQ">Заменим первую часть:</p>
  <pre id="1DCh" data-lang="javascript">const getData = async (url) =&gt; {
  const res = await fetch(url)
  const json = await res.json()
  return json
}</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="3DNa">Если на каком этапе асинхронной функции возникнет ошибка промис будет <strong>автоматически</strong> отклонен</p>
  </section>
  <p id="bpKf">Заменим вторую часть:</p>
  <pre id="TMK7" data-lang="javascript">const url = &#x27;https://jsonplaceholder.typicode.com/todos&#x27;)
const data = await getData(url)</pre>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="mW6i">Ключевое слово <em>await</em> можно использовать в JS только внутри асинхронных функций, но браузеры позволяют обрабатывать такой код в консоли</p>
  </section>
  <p id="U0vj">Добавим во вторую часть обработчик ошибок с помощью <a href="#hVjL">try/catch</a> блоков:</p>
  <pre id="Sgqm" data-lang="javascript">const url = &#x27;https://jsonplaceholder.typicode.com/todos&#x27;)

try {
  const data = await getData(url)
  console.log(data)
} catch (error) {
  console.log(error.message)
}</pre>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="jyuE">ASYNC/AWAIT - это синтаксическая надстройка над промисами</p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="J9RV"><em><strong>await</strong></em> синтаксис возможен только внутри <em><strong>async</strong></em> функций</p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="dnz0"><em><strong>async</strong></em> функция всегда возвращает <em>Promise</em></p>
  </section>
  <section style="background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="S2S3"><em><strong>async</strong></em> функция ожидает результата инструкции <em><strong>await</strong></em> и не выполняет последующие инструкции</p>
  </section>
  <blockquote id="53Cr" data-align="center"><a href="#TSUX">Наверх</a></blockquote>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.buninman.ru/dLty8uA9oLZ</guid><link>https://blog.buninman.ru/dLty8uA9oLZ?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman</link><comments>https://blog.buninman.ru/dLty8uA9oLZ?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=buninman#comments</comments><dc:creator>buninman</dc:creator><title>Решил я, значит, стать программистом</title><pubDate>Sat, 20 Aug 2022 07:35:17 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/d0/17/d017fc60-141b-4e38-9aa4-a1910901ff33.png"></media:content><category>Личный блог</category><description><![CDATA[<img src="https://img1.teletype.in/files/8d/42/8d42066b-977f-4e67-9ad2-2520a7556464.gif"></img>11 лет проработал дизайнером и вот так вот раз, и захотелось чего-то другого. Чего-то более математичного🙂]]></description><content:encoded><![CDATA[
  <p id="LpIT">11 лет проработал дизайнером и вот так вот раз, и захотелось чего-то другого. Чего-то более математичного🙂</p>
  <figure id="Vgn8" class="m_column">
    <img src="https://img1.teletype.in/files/8d/42/8d42066b-977f-4e67-9ad2-2520a7556464.gif" width="700" />
  </figure>
  <h3 id="sgnG">Итак,</h3>
  <p id="E9ft">Меня зовут Алексей и сейчас я занят изучением программирования на JavaScript. Выбор был сделан в пользу этого языка программирования просто потому, что я начал делать свой собственный сайт-портфолио по дизайну, частично используя чужой код. И чтоб разобраться в этом коде, мне нужно было знать HTML/CSS и JavaScript.</p>
  <p id="jDPX">В процессе я заметил, что делать сайт мне намного интереснее, чем заниматься его наполнением и оформлять дизайн-проекты. В голову сразу залезли новые, но вполне достижимые цели. Например, у меня есть идея для приложения по учету финансов, а также есть желание повторить сайт-игру с машинкой от Бруно Саймона. Все это позволяет сделать JavaScript.</p>
  <p id="7bwr">Если честно, я понятия не имею какую сферу программирования и какой язык стоит выбрать, чтоб быть программистом. Я просто захотел попробовать себя в какой-то новой сфере. Наверное, начался кризис среднего возраста😐 или что-то вроде того.</p>
  <p id="pVet">Не уверен, что этот выбор правильный и даже не уверен, что меня на долго хватит, но пока все идет ништяк. Я уже немного подтянул знания HTML/CSS и начал проходить курс по JavaScript, который на удивление дается мне легко. Даже появилось какое-то неловкое чувство🤷‍♂️ будто изучаешь какую-то никому ненужную фигню.</p>
  <p id="uYmZ">Если все получится, то буду пробовать устраиваться джуном и начинать карьеру программиста.</p>
  <hr />
  <hr />
  <h3 id="upR4">Зачем мне блог?</h3>
  <p id="4Zdd">Изначально я завел его чтоб складывать полезные ссылки и конспекты, которые появляются по мере прохождения различных курсов. Я уже старый, поэтому конспектирую новые знания.. так меня научили еще более старые люди🙄</p>
  <p id="hbdg">Но в этот раз я решил не тратить бумагу, а печать все в интернетах.. вдруг кому пригодится. Но нужно понимать, что это не истина, а всего-лишь моя интерпретация полученной информации, поэтому кроме конспектов тут будут и обычные человеческие посты.</p>
  <p id="RFB0">Единственное, когда пишешь &quot;в стол&quot; приходит уныние, поэтому очень хочется чтоб кто-то этот блог читал, поддерживал или чморил в комментах, использовал конспекты. В общем, если вы тоже изучаете программирование, если вам интересно чем все закончится или есть еще какие-то неведомые причины, то подписывайтесь на меня тут или в <a href="https://t.me/buninman" target="_blank">Телеграме</a></p>
  <hr />
  <h3 id="Tu9x">Что дальше?</h3>
  <p id="8eTR">На сегодняшний день, план изучения новых тем у меня такой:</p>
  <ul id="faqz">
    <li id="bin9">HTML/CSS - без них никуда;</li>
    <li id="klOv">JavaScript - основа моего план;</li>
    <li id="c5s9">Vue или React - чтоб попробовать создать работающее приложение;</li>
    <li id="q3XD">Blender - потому что хочу делать иллюстрации и модели для сайтов и приложений;</li>
    <li id="Kr12">Three.js - чтоб сделать сайт как у Бруно Саймона;</li>
    <li id="Lfh9">SQL - эта штука тоже много где всплывает, надо знать.</li>
  </ul>
  <p id="Ybh3">Этот план будет корректироваться по мере необходимости или по наличию интересного курса. Так, например, у меня уже есть курс по React, ждущий своей очереди.</p>
  <p id="AuJk">Также надо не забывать следить за фондовым рынком, заниматься спортом, учить английский и между делом зарабатывать деньги🙂</p>
  <hr />
  <p id="dHhI">А вот <a href="https://bruno-simon.com/" target="_blank">сайт Бруно Саймана</a>, о котором я говорил выше.</p>
  <figure id="HvD8" class="m_column">
    <img src="https://img1.teletype.in/files/86/90/8690629d-8217-4adc-9eaf-255ad2216f15.png" width="1920" />
  </figure>

]]></content:encoded></item></channel></rss>