Czytaj książkę: «JavaScript с нуля: учимся программировать, создавая игры»
Как не потеряться на старте
Самый опасный момент для новичка наступает не тогда, когда код ломается.
До кода многие вообще не доходят.
Человек решает начать программировать и делает вроде бы разумную вещь: идет искать совет. Открывает YouTube, спрашивает ChatGPT, читает дорожные карты, смотрит вакансии, листает комментарии под постами.
Через час у него уже не ясность, а шум.
Один говорит: начинай с Python. Второй: только JavaScript, он сразу работает в браузере. Третий: без алгоритмов ты никто. Четвертый: фронтенд умер. Пятый: программистов заменит ИИ. Шестой: теперь достаточно уметь писать промпты.
И вот человек, который еще не написал ни одной своей программы, уже пытается выбрать не первую задачу, а судьбу.
Он сравнивает языки. Сравнивает зарплаты. Смотрит на вакансии, где от junior-разработчика требуют такой список технологий, будто ищут не новичка, а маленький отдел разработки в одном человеке.
Появляется неприятное ощущение: я уже опоздал.
Но чаще всего новичок опоздал не в профессию. Он опоздал к простому действию.
Программирование начинается не с выбора идеального языка. И не с попытки понять весь рынок. Оно начинается с маленького момента, который нельзя заменить чужим советом: вы пишете несколько строк, запускаете их и видите, что компьютер сделал именно то, что было написано. Не то, что вы имели в виду. Не то, что хотелось. А то, что было написано.
Это очень хорошо отрезвляет, особенно когда появлятся баги. Разрушает иллюзии о коде и программировании. И одновременно это первая настоящая победа.
Пока человек только читает про программирование, код кажется чужой территорией. Там живут странные слова, скобки, ошибки, фреймворки, опытные разработчики и бесконечные споры о правильном пути.
Но когда первая программа запускается, территория меняется. Перед вами уже не мифическая профессия, а конкретное поведение: программа спросила, запомнила, сравнила, ответила, ошиблась, сломалась, снова заработала.
С этого начинается инженерное мышление.
Не с умных терминов. Не с красивых схем. Не с уверенности, что вы выбрали лучший язык на всю жизнь.
С привычки превращать неопределенность в проверяемые шаги.
Хочу сделать игру — значит, нужно понять, где находится игрок. Что он может сделать. Как программа узнает, что нажата клавиша. Когда начислять очки. Когда отнимать жизнь. Где заканчивается попытка. Что считать победой. Так идея теряет магию и становится системой.
ИИ в этом месте может помочь, но может и украсть самый важный опыт. Он легко напишет код за вас. Объяснит ошибку. Предложит решение. Соберет пример, который выглядит убедительно. И именно поэтому с ним опасно начинать как с кнопки «сделай вместо меня».
Работающий код написанный нейросетью не добавит понимания вам.
Если вы не можете объяснить, где в программе хранится состояние, почему условие сработало, откуда пришло значение и что изменится после следующего действия пользователя, вы не управляете программой. Вы наблюдаете за чужим решением. Это как смотреть, как другой человек проходит сложный уровень в игре, и считать это своей победой. На экране результат есть. Но навык появился не у вас.
А программирование начинается там, где появляется управление: осознанное влияние на результат. Сначала совсем маленькое: поменять текст вопроса, сохранить ответ, проверить условие, начислить очко, сдвинуть героя на несколько пикселей, открыть дверь только тогда, когда найден ключ.
Но в этих маленьких действиях уже есть главное. Вы перестаете быть зрителем. Вы начинаете задавать правила.
Именно поэтому старт должен быть коротким, практическим и честным. Не пытаться сразу объять всю профессию. Не спорить с интернетом о лучшем языке. Не копировать большие куски кода, которые невозможно удержать в голове.
Сначала нужно получить первый результат, который вы понимаете настолько, чтобы изменить его самому.
После этого шум становится тише.
Потому что у вас появляется не чужая дорожная карта, а собственная точка опоры.
Не выбирайте путь до первого шага. Сначала заставьте программу работать.
Одна строка — разные смыслы
Первая строка кода может выглядеть обманчиво бедно. Открывается маленькое окно. В нем вопрос. Пользователь что-то вводит. Нажимает кнопку. И всё.
Если смотреть на это как на будущий разработчик, который уже мечтает о сложных играх, интерфейсах, персонажах, серверах и ИИ-помощниках, такая программа кажется почти несерьезной.
Но именно в таких местах часто прячется главный принцип.
Редактировать код можно в любом удобном текстовом редакторе. Для старта я рекомендую Notepad++ или Sublime Text: они легкие, быстро открываются и не требуют сложной настройки.
Создайте новый файл и сохраните его как game.html. Обычно это делается через пункт Сохранить как: в имени файла укажите game.html, а если редактор предлагает тип файла, выберите вариант Все файлы. Важно, чтобы файл назывался именно game.html, а не game.html.txt.
Файл с расширением .html — это HTML-страница. Чтобы встроить в нее JavaScript, нужен специальный тег <script></script>. Браузер выполняет только тот код, который лежит внутри него. Сейчас и далее создавайте этот тег, а весь код пишите внутри него.
Теперь напишите в файле:
<script>
prompt('Сколько будет 2 + 2?');
</script>
Сохраните файл и откройте его в браузере. Браузер покажет окно с вопросом. Вы сможете ввести ответ. Потом окно закроется. Пока программа не проверяет ответ. Не запоминает его. Не начисляет очки. Не говорит, победили вы или проиграли.
Но кое-что уже произошло. Программа впервые обратилась к человеку. До этого она могла быть просто текстом в файле. Теперь между программой и пользователем появился контакт: вопрос, ожидание, действие.
prompt — это встроенная возможность браузера. Она показывает окно с текстом и дает пользователю поле для ввода.
Внутри скобок мы передаем текст вопроса. Сам вопрос написан в кавычках:
prompt('Сколько будет 2 + 2?');
На первый взгляд это просто учебный пример.
Но уже здесь можно почувствовать важную вещь. Уберите одну кавычку или одну скобку — и программа развалится. Браузер больше не поймет, где начинается текст, где он заканчивается и какую команду нужно выполнить.
В программировании есть четкие правила. Компьютер не догадывается, что вы имели в виду. Он выполняет только то, что может разобрать.
Попробуйте заменить вопрос:
prompt('Как зовут твоего героя?');
Вы поменяли только вопрос, но та же самая строка с кодом уже работает иначе. Технически почти ничего не изменилось. Всё тот же prompt. Всё те же скобки. Всё те же кавычки. Всё то же окно в браузере. Изменился смысл текста.
В первом варианте программа задавала математический вопрос. Во втором — просит имя героя. Замените текст еще раз:
prompt('Ты стоишь перед пещерой. Войти внутрь?');
Теперь это похоже на начало текстового приключения.
И снова команда та же. Изменилась не технология. Изменилась роль этой строки в программе. Это важнее, чем кажется.
Новичок часто думает, что программирование — это знание большого количества команд. Чем больше команд знаешь, тем ближе ты к настоящей разработке. А опытный разработчик смотрит иначе. Ему важно не только знать команду, но и понимать, какую роль она играет в поведении программы.
Одна строка может быть учебным вопросом. Может быть диалогом. Может быть выбором. Может быть вводом имени. Может быть первым шагом к загадке, паролю, сцене, форме, боту или игре. Код один. Смысл разный.
Именно поэтому не стоит презрительно относиться к маленьким примерам. В них можно увидеть основу больших программ. Большая игра тоже держится на вопросах, только чаще они спрятаны внутри кода. Нажал ли игрок кнопку? Есть ли у него ключ? Сколько осталось здоровья? Коснулся ли герой стены? Закончился ли таймер? Можно ли открыть дверь?
Компьютер не задает эти вопросы сам. Программист учит программу проверять такие условия и отвечать на них действием.
prompt — самый простой способ увидеть это руками. Он грубый, старомодный и почти никогда не используется в настоящих красивых интерфейсах. Но для первого шага он полезен: он сразу показывает, что программа может обратиться к человеку и ждать его действия.
Сейчас не нужно делать из этого большую игру. Сделайте маленькую вещь. Поменяйте вопрос. Запустите файл. Посмотрите, как меняется ощущение от программы.
Спросите не пример, а имя героя — и строка становится началом создания персонажа. Спросите про дверь — и она становится выбором. Спросите пароль — и она становится проверкой доступа.
Программист работает не только с командами. Он работает со смыслом, который эти команды получают внутри задачи.
В программировании важна не только команда, но и роль, которую программист отводит ей в программе.
Память программы
Если программа не сохранила введенное пользователем значение, для нее ничего не произошло. Человек помнит, что он ввел. Программа — нет.
Компьютер не держит в голове контекст просто потому, что нам так хочется. Ему нужно прямо указать: вот это значение важно, оставь его, мы будем работать с ним дальше.
Для этого нужна переменная.
Напишите:
<script>
const answer = prompt('Как зовут твоего героя?');
alert(answer);
</script>
Откройте файл в браузере. Сначала появится вопрос. Введите любое имя. Потом появится второе окно. В нем будет ваше значение. Это выглядит просто. Даже слишком просто.
Но здесь произошло главное: программа запомнила ответ пользователя и использовала его во второй строке кода.
Разберем эту строку:
const answer = prompt('Как зовут твоего героя?');
Слово const создает переменную.
answer — имя этой переменной.
Знак = говорит программе: возьми результат справа и сохрани его слева.
Справа стоит prompt('Как зовут твоего героя?'). Эта команда задает вопрос и возвращает то, что ввел пользователь.
Слева стоит answer. Это место, куда программа кладет полученное значение.
Если сказать по-человечески: спроси имя героя и сохрани ответ под именем answer.
Следующая строка:
alert(answer);
Она показывает не слово answer, а значение, которое лежит внутри переменной.
Здесь важно не поставить кавычки вокруг answer. Если написать alert(answer), программа покажет значение из переменной. Если написать alert('answer'), программа покажет просто текст answer. Для JavaScript это разные вещи. Текстовые строки пишутся в кавычках. Имена переменных — без кавычек.
Что пользователь ввел, то alert и покажет. Переменная — это не украшение кода. Это точка памяти. Без нее программа похожа на человека, который спросил имя собеседника и тут же забыл ответ.
С переменной появляется продолжение. Имя героя можно показать в приветствии. Выбор двери можно проверить. Пароль можно сравнить. Количество очков можно увеличить. Здоровье можно уменьшить. Наличие ключа можно запомнить.
Игра держится не только на действиях игрока, но и на том, что программа помнит после этих действий. Где находится герой? Сколько у него жизней? Есть ли ключ? Какая дверь выбрана? Какой ответ был введен? Сколько очков набрано? Если эти значения нигде не сохранены, программа каждый раз начинает с пустого места.
Попробуйте заменить вопрос:
<script>
const answer = prompt('Куда пойдешь: в лес или в пещеру?');
alert(answer);
</script>
Теперь переменная answer хранит не имя, а выбор. Имя переменной осталось тем же. Механика изменилась. В этом и сила переменных: они позволяют программе удерживать то, что имеет значение именно в этой задаче. Не всё подряд. А то, без чего следующее действие станет невозможным.
Программа помнит не то, что пользователь ввел, а то, что программист сказал ей сохранить.
Компьютер не понимает намеков
Первый свой код я писал на языке Pascal еще когда был подростком. Это был обычный Hello World: несколько строк, запуск, и на экране появляется текст. Сначала все выглядело великолепно. Я написал английские слова, программа вывела английские слова. Компьютер послушался.
Потом захотелось сделать по-своему. Не Hello World, а что-нибудь на русском. Не чужой учебный пример, а свой текст. Я поменял строку текста с английских слов на русские, а остальной код программы оставил тем же. Запустил программу и вместо нормальных русских букв увидел кашу из странных символов.
Поведение казалось странным. Я же написал текст. Я же хотел, чтобы компьютер его показал. Почему он не понял? Потому что компьютер не понимает «я хотел».
Тогда я еще не знал слов вроде кодировка, таблица символов, настройки вывода, среда выполнения. Я просто столкнулся с фактом: компьютеру мало моего желания увидеть нормальный русский текст. Ему нужно, чтобы текст был записан и прочитан по подходящим правилам. Если эти правила не совпадают, на экране появляются странные символы.
Не потому что компьютер вредничает. Просто у каждого языка программирования, среды выполнения и способа запуска есть свои правила. Код нужно не только написать, но и записать в той форме, которую сможет правильно разобрать и выполнить система, где этот код запускается.
Пока код работает как ожидаешь, кажется, что компьютер просто слушается. Когда появляется ошибка, становится видно другое: компьютер не слушается вас. Он строго выполняет инструкцию. Это неприятное, но важное открытие.
Если программа ведет себя не так, как вы ожидали, первый вопрос не «почему компьютер тупит?». Первый вопрос: «что я не сказал программе достаточно точно?».
В коде мелочи быстро перестают быть мелочами. То, что в обычной речи можно понять по контексту, в программе должно быть записано явно. С людьми иначе. Мы постоянно понимаем друг друга неточно. Друг перепутал слово, но вы уловили смысл. В сообщении ошибка, но вы догадались. Фраза сказана криво, но намерение понятно.
Компьютер не угадывает намерение. Хочешь вывести текст, значит, нужно записать его так, чтобы среда поняла символы. Хочешь сохранить ответ, значит, нужно явно сказать программе, куда его положить. Хочешь проверить выбор, значит, нужно описать условие. Хочешь получить победу, значит, нужно определить, при каком состоянии она наступает. Чем точнее инструкция, тем меньше тумана между идеей и результатом.
Ошибка в этом смысле не враг. Она показывает место, где желание еще не стало точной командой.
Компьютер не исполняет намерения. Он исполняет инструкции.
Результат появляется после проверки
В девятнадцать лет я устроился на подработку программистом. Я немного понимал PHP, а на работе мне дали задачу на JavaScript: сделать форму авторизации для взаимодействия с бэкендом на PHP.
На словах все звучало просто. Пользователь вводит данные. Если данные правильные — пускаем дальше. Если неправильные — показываем ошибку. Но именно в этом «если» и была вся задача.
Я не понимал последовательность проверки. Где взять данные пользователя. Как передать данные из JavaScript в PHP. Как обратиться к базе данных. Как хранится пароль. Почему его нельзя просто сравнить как обычный текст. Что такое хеширование. В каком месте сравнивать значения. Что делать, если совпало. Что делать, если не совпало. Да и сам синтаксис JavaScript тогда меня пугал.
Пока логин и пароль не прошли проверку, это просто введенный текст. У любой более-менее серьезной логики быстро появляется ветвление: если условие совпало — выполнить один код, иначе — выполнить другой.
Результат появляется только после проверки.
Программа должна сравнить введенные данные с правилом и выбрать, что делать дальше. Пустить. Не пустить. Показать ошибку. Дать доступ. Остановить действие. Тогда я еще не умел нормально раскладывать такую проверку на шаги. Поэтому простая форма авторизации превратилась для меня в испытание.
Так устроено очень многое в программах.
Пользователь ввел пароль — программа проверяет, подходит ли он. Игрок подошел к двери — программа проверяет, есть ли ключ. Герой получил урон — программа проверяет, осталось ли здоровье. Покупатель нажал кнопку — программа проверяет, можно ли оформить заказ.
Пока проверки нет, введенное значение просто лежит в программе. Оно еще не стало результатом.
Сделаем маленькую игру на этом принципе.
Программа загадает число. Игрок попробует угадать. Если ответ совпал с правилом — победа. Если нет — попытка не удалась.
Напишите:
<script>
const answer = prompt('Я загадал число от 1 до 5. Попробуй угадать.');
if (answer == 3) {
alert('Ты угадал!');
} else {
alert('Не угадал. Попробуй еще раз.');
}
</script>
Откройте файл в браузере.
Введите число 3 — программа покажет победу. Введите любое другое число — программа покажет другой результат.
Вот здесь появляется первое ветвление поведения.
Одна и та же программа может пойти по двум разным путям. Не потому что она «поняла» игрока. А потому что программист дал ей правило проверки.
Разберем главный кусок:
if (answer == 3) {
alert('Ты угадал!');
} else {
alert('Не угадал. Попробуй еще раз.');
}
if можно прочитать как «если».
Внутри скобок стоит проверка условия. В нашем примере программа сравнивает два значения: одно лежит в переменной answer, а другое мы сами написали как число.
answer == 3
В JavaScript два знака равно означают сравнение.
Программа спрашивает: значение answer равно 3?
Проверки могут быть разными: равно, больше, меньше, не равно. Но смысл один — программа должна получить ответ «да» или «нет».
Если да — выполняется код внутри первого блока. На экране появляется «Ты угадал!».
Если нет — выполняется код после else. На экране появляется «Не угадал. Попробуй еще раз.».
else можно прочитать как «иначе».
Получается простое правило:
если ответ равен 3 — победа;
иначе — неудачная попытка.
Теперь измените правильное число.
Поставьте вместо 3 число 5. Запустите программу снова. Старый ответ больше не победит, потому что изменилось правило.
Игрок может вводить что угодно. Но результат определяет не текст вопроса в prompt, а проверка, которую вы написали в коде.
Если в вопросе написано «от 1 до 5», а в проверке вы случайно поставили 39, программа будет ждать 39. Для нее главный источник правила — не фраза для игрока, а условие внутри if.
Программа уже не просто спрашивает и не просто запоминает. Она начинает различать ситуации. Правильный ответ и неправильный ответ больше не одинаковы. У них разные последствия.
Так появляется основа любой логики: значение встречается с правилом, а программа выбирает действие.
Результат появляется там, где значение встречается с правилом.
Не запоминайте команды, понимайте их роль
Я много лет пишу код и все равно регулярно проверяю синтаксис вещей, которые не использую каждый день. Особенно в языках и форматах, к которым возвращаюсь реже.
Современная разработка давно не живет в одном языке. В одном проекте рядом могут оказаться JavaScript, TypeScript, HTML, CSS, SQL, YAML, TOML, bash-скрипты для автоматизации, конфигурация деплоя или даже Ruby-скрипт, который кто-то написал много лет назад для сборки или доставки кода.
Невозможно держать все это в голове одинаково хорошо.
Иногда приходится вспоминать, как округлить число. Как получить случайное значение в нужном диапазоне. Как работает метод, который давно не открывал. Какой порядок аргументов у функции.
Это нормально. Даже не пытайтесь запомнить все. Программирование — не соревнование по памяти.
Сильный разработчик отличается не тем, что держит в голове все команды языка. Он умеет найти нужный прием, понять его, вставить в задачу и проверить, что результат действительно работает.
Сегодня это можно сделать через документацию, поиск или ИИ. Но опасность остается прежней: если просто скопировать строку и не понять, какую роль она играет, вы снова становитесь зрителем.
Случайное число в JavaScript — хороший пример.
Можно годами писать сайты, формы, личные кабинеты, бэкенд или интерфейсы и почти не использовать генератор случайных чисел. А в играх генераторы случайных чисел встречаются на каждом шагу: случайный урон, выпадение предмета, поведение врага, порядок событий, награда после победы.
В нашей игре правильный ответ пока известен заранее. Если всегда нужно угадать 3, игрок быстро запомнит правило. Первый раз он думает, второй раз уже просто вводит нужное число.
Чтобы игра не повторялась одинаково, программе нужна неизвестность. Для этого добавим случайность.
Измените код так:
<script>
const secretNumber = Math.floor(Math.random() * 5) + 1;
const answer = prompt('Я загадал число от 1 до 5. Попробуй угадать.');
if (answer == secretNumber) {
alert('Ты угадал!');
} else {
alert('Не угадал.');
}
</script>
Теперь правильный ответ больше не написан вручную как 3 или 5. Программа сама выбирает число.
Вот эта строка создает случайное число от 1 до 5:
const secretNumber = Math.floor(Math.random() * 5) + 1;
Выглядит тяжеловато. Особенно если вы видите это впервые. И даже спустя годы разработки на других языках такая запись может удивлять, если вы привыкли к чему-то вроде rand(от, до).
Но ее можно разобрать по частям.
Math.random() создает случайное число от 0 до 1. Не целое число, а дробное. Например, что-то вроде 0.37 или 0.82. Когда мы умножаем его на 5, получаем случайное число от 0 до почти 5.
В нашем случае 5 — это количество возможных значений: 1, 2, 3, 4 и 5. Math.floor убирает дробную часть и оставляет целое число. После этого получается одно из чисел: 0, 1, 2, 3 или 4. Плюс 1 в конце сдвигает диапазон. Было от 0 до 4, стало от 1 до 5.
Если говорить общим правилом: сначала получаем нужное количество вариантов, потом сдвигаем результат к нижней границе диапазона. Итог: secretNumber хранит случайное число от 1 до 5.
Дальше программа делает уже знакомую проверку:
if (answer == secretNumber)
Она сравнивает ответ игрока не с заранее написанным числом, а со значением, которое программа сама выбрала при запуске. Из-за этого каждый запуск становится немного другим.
В первый запуск правильный ответ может быть 2. Следующий раз — 5. Потом — 1. Игрок больше не может просто запомнить один правильный ответ. Ему приходится играть.
Теперь попробуйте изменить диапазон.
Если хотите число от 1 до 10, поменяйте 5 на 10:
const secretNumber = Math.floor(Math.random() * 10) + 1;
И не забудьте изменить текст вопроса, чтобы он не обманывал игрока. Текст для человека и правило для программы должны совпадать по смыслу.
Не нужно помнить все команды наизусть. Нужно понимать, какую роль конкретная строка играет в вашей программе.
Не нужно знать, как все устроено внутри
Когда вы нажимаете кнопку на пульте, телевизор включается. Вы не думаете о том, как сигнал уходит с пульта. Как телевизор его принимает. Какая микросхема внутри обрабатывает команду. Как устроена схема питания. Что происходит в динамиках, экране и процессоре. Вам это не нужно, чтобы переключить канал или сделать звук громче.
Но вам нужно понимать другое. Какая кнопка что делает. Если вы хотите увеличить громкость, вы нажимаете одну кнопку. Если хотите выключить телевизор — другую. Если хотите открыть меню — третью. Пульт скрывает от вас сложность телевизора, но не отменяет необходимость понимать назначение кнопок.
В инженерном мире это называется абстракцией. Абстракция — это когда вы можете пользоваться инструментом, не зная всех деталей его внутреннего устройства.
Вы уже использовали такие инструменты.
prompt показывает окно с вопросом.
alert показывает сообщение.
Math.random() возвращает случайное число.
Вы не писали код браузера, который открывает окно prompt. Не разбирали, как именно JavaScript внутри получает случайное число. Не изучали устройство движка браузера перед тем, как вызвать alert.
И это нормально.
Если бы перед первой строкой кода нужно было понять весь браузер, никто бы не начинал программировать. Сила абстракции в том, что она снимает лишнюю умственную нагрузку.
Darmowy fragment się skończył.