<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 22:45:01 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="E0tR9W7XXoag7OF1UV3aci-0u1jh1dQ8v5RNLxnpiOf8mprCnKnz5havxe1dUioF772W8uniKp4CdNd7S-5viQ";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Разметка | JS: Последовательности</title>
<meta name="description" content="Разметка / JS: Последовательности: Начинаем разрабатывать библиотеку для работы с HTML, знакомимся с Markdown и изучаем общие принципы языков разметки на примере HTML">
<link rel="canonical" href="https://ru.hexlet.io/courses/sequences/lessons/html/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Разметка">
<meta property="og:title" content="JS: Последовательности">
<meta property="og:description" content="Разметка / JS: Последовательности: Начинаем разрабатывать библиотеку для работы с HTML, знакомимся с Markdown и изучаем общие принципы языков разметки на примере HTML">
<meta property="og:url" content="https://ru.hexlet.io/courses/sequences/lessons/html/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="LgXedhXZG5PWRs8bo0IoSw7HjXBcFsXoWG5PB9aqtEDB1BVB56e282AF64OvTdg8zs6g2lQhO0rljtVThK1TLg" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc2MCwicHVyIjoiYmxvYl9pZCJ9fQ==--9348098e4053d798b6f34bee4ef66947540261e4/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-rafiki.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T22:45:00.937Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"wtH2c6p3GBtWazQOXtDaY2B_3Bfn2AdBPiITC-XM7BYtAD1EWAm1e-AoEJZS3yoUoHbxve_v-eODwolft8sLeA","topics":[{"id":47418,"title":"если надо добавить в начало, то лучше функцию назвать prepend вместо append","plain_title":"если надо добавить в начало, то лучше функцию назвать prepend вместо append ","creator":{"public_name":"Daniyar Zhanakhmetov","id":239134,"is_tutor":false},"comments":[{"creator":{"public_name":"Николай Ларионов","id":274677,"is_tutor":false},"id":103623,"body":"**Роман Ашиков**, тоже понять не могу, почему не prepend , даже после вашего разъяснения не ясно , потому что после того, как мы запустили функцию append, из названия функции ясно, что нода добавилась в конец списка, но при вызове метода head мы почему то должны получить пару которая находиться в конце списка.. а не головного, где в этом логика, т.е чтобы пройти данную задачу функцию append нужно реализовать чтобы она добавляла в начало списка, а функция toString выводила список начиная с конца.. то что это пройдет тесты, это понятно, но почему такое задание, не ясно","topic_id":47418},{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":102153,"body":"Приветствую!\n\nДавайте рассмотрим пример из условий задачи:\n\n // Создаем новый html-список\n const dom1 = make();\n // Создаем тег и сразу добавляем его в html\n const dom2 = append(dom1, node('h1', 'hello, world'));\n // Еще раз\n const dom3 = append(dom2, node('h2', 'header2'));\n // Создаем новый тег\n const tag = node('h3', 'header3');\n // Добавляем созданный тег в html-список\n const dom = append(dom3, tag);\n // Преобразуем html-список в строчку\n toString(dom);\n // <h1>hello, world</h1><h2>header2</h2><h3>header3</h3>\n\nКак видно в выводе `append` добавил ноды в конец. Так как наш html построен на односвязном списке, понятие \"добавить в голову\" относится к внутренней реализации. Для пользователя кода происходит добавление в конец.","topic_id":47418},{"creator":{"public_name":"Николай Ларионов","id":274677,"is_tutor":false},"id":103624,"body":"**Николай Ларионов**, [мое решение](https://ru.hexlet.io/code_reviews/325227), оно уродливо, потому что не особо знал о том что, cons и consList две разные функции, я почему то думал это одна , переименованная в импорте, но сути дела это не меняет, в решении учителя также append добавляет элемент в начало, а toString выводит с конца, why ?","topic_id":47418}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":10749,"title":"Мда, в сравнении с решением учителя `toString` моя функция похожа больше на один большой велосипед и если тесты немного изменить, то уже не будут проходить..\n\n```\n// removed\n```\nP.S. Все равно нет полного понимания этих функций. Мне кажется что сейчас я просто все усложняю используя ФП..Потому что на задачи я трачу большое кол-во времени: пару часов минимум и больше.","plain_title":"Мда, в сравнении с решением учителя toString моя функция похожа больше на один большой велосипед и если тесты немного изменить, то уже не будут проходить.. // removed P.S. Все равно нет полного понимания этих функций. Мне кажется что сейчас я просто все усложняю используя ФП..Потому что на задачи я трачу большое кол-во времени: пару часов минимум и больше. ","creator":{"public_name":"Дима Матвейчук","id":134792,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":22375,"body":"> Просто меня видимо отталкивает функциональный подход такой\n\nСудя по вашим постам, вас отталкивает то что вам не поддается, а не фп). Но такое при обучении неизбежно, легко не будет это я обещаю.\n\n> Или же это из-за того что я туплю в экран часа 4 прежде чем решить задачу\n\nЭто всего лишь говорит о том что вы не умеете правильно подходить к решению задач и не важно что это за задача и фп там или не фп.\n\n1. Используете ли вы листок с бумагой?\n1. Пробуете ли все запускать локально и повторять весь код из теории?\n1. Пробуете ли сначала описать словесно алгоритм который вы хотите реализовать перед тем как бросаться в код?\n1. Анализируете ли все импорты которые есть в файлах?\n1. Анализируете ли файл с тестами и пытаетесь понять как ваш код будет использоваться?\n\n> Доверюсь конечно учителю\n\nКогда что то непонятно - пишите. Я никогда не дам прямых ответов, но задам правильные вопросы ;)\n\nИ еще, у нас очень много полезного в блоге и на ютубе, посмотрите обязательно.\n\n","topic_id":10749},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":22381,"body":"1. Первые два проекта одинаковые и во фронте и в бекенде.\n1. Вы уже готовы к первому проекту. Проекты немного о другому, если в курсах (особенно первых) все направлено на логическо-абстрактное мышление, то проекты это больше про архитектуру, окружение, прикладные вещи и библиотеки. То есть совсем другой опыт (и очень важный!). Так что не стоит ждать готовности по быстроте решений, в проектах нет сложных алгоритмических задач.","topic_id":10749},{"creator":{"public_name":"Дима Матвейчук","id":134792,"is_tutor":false},"id":22376,"body":"Возможно Вы правы на счет того, что мне не поддается, но столько функций для меня и правда многовато.\nПо поводу решения задач: в пред.курсах с ними особо проблем не возникало, а косвенно понимал что где и что возвращаю, запары пошли когда начал работать со списком. Иногда просто не могу понять что должна вернуть та или иная функция и как вообще такое реализовать, а все еще углубленно тем, что тут везде рекурсии) \nКогда уже вообще ни как не получается, пишу на бумаге примерно что надо (насколько вообще выходит писать такое), локально увы код запустить я не могу, что иногда меня разочаровывает потому что консоль в отладчике не всегда работает. А не могу локально запустить потому что не знаю как устроены некоторые импортовые функции. В частности, так бы работал еще и в редакторе. \nБлог, слак и ютуб читаю конечно, когда есть время)\nСпасибо что дарите немного мотивации своими ответами, надеюсь прям на лучшее)","topic_id":10749},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":22377,"body":"> но столько функций для меня и правда многовато\n\nДальше их будет больше, функции в функциях и функциями погоняют. ЭТо не мы такие, жизнь такая (js).\n\n> а все еще углубленно тем, что тут везде рекурсии\n\nРаз видно где проблема, то и решение простое. нужно взять и все задачи из введения прорешать через рекурсию на своем компьютере. Плюс вы не все практики сделали, а те что сделали, скорее всего были решены не через рекурсию, а значит есть возможность попрактиковаться.\n\n> А не могу локально запустить потому что не знаю как устроены некоторые импортовые функции.\n\nНе совсем понял. Что вы имеете ввиду?\n\n","topic_id":10749},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":22372,"body":"Сейчас по пунктам расскажу что да как.\n\n> пару часов минимум и больше\n\nэто нормально, наши задачи построены таким образом, что нужно думать. У некоторых уходят дни. Со временем, когда у вас появятся в голове правильные модели, алгоритмы будут сами выстраиваться.\n\n> и если тесты немного изменить, то уже не будут проходить..\n\nбыл бы здорово если бы вы подскзали что дописать в тесты\n\n> Все равно нет полного понимания этих функций.\n\nВы только начали и совершенно нормально что нет понимания. Со временем ситуация изменится.\n\n> что сейчас я просто все усложняю используя ФП\n\nВ жизни так устроено все. Если вам дать клавиатуру в которой клавиши расположены по другому, то вы начнете писать медленее. Для того чтобы научиться писать код в неизменяемом стиле, нужна практика. Много практики. И у вас ее будет выше крыши.\n\nБуквально через пару курсов начнется настоящий (прикладной) код на js с массивами, объектами и вот этим всем. К этому времени, если вы будете выполнять практику самостоятельно и учитывая наши рекомендации, ваше отношение к коду и его понимание изменится до неузнаваемости.\n\np.s. когда вы хотите поделиться своим кодом, то отправляйте его на код ревью и вставляйте сюда ссылки, чтобы другие пользователи не видели решений.","topic_id":10749},{"creator":{"public_name":"Дима Матвейчук","id":134792,"is_tutor":false},"id":22378,"body":"> Не совсем понял. Что вы имеете ввиду?\n\nНу, например у вас там в импорте функции ```l(), head(), isEmpty ``` и прочие, но не видно ведь как они реализованы. То есть, нет полностью функции с ее телом. И я не могу скопировать ее и вставить в редактор чтобы там поработать, поэтому приходится тут. Ну или как-то можно но я такой лох что даже не заметил или не знаю как перенести их к себе на комп.\n\n> нужно взять и все задачи из введения прорешать через рекурсию\n\nНе думаю конечно что это поможет, потому что для меня одно дело простые числа, строки и т.д. а другое когда я другие функции использую, или пары и т.д. Примерно понимаю что делаю, но мозг отказывается еще воспринимать. \nХотя прорешаю, то не слишком сложные для меня задачки. \n\n","topic_id":10749},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":22379,"body":"> Ну или как-то можно но я такой лох что даже не заметил или не знаю как перенести их к себе на комп.\n\nУ вас есть в планах прохождение наших проектов? Если да, то после прохождения первого, этот вопрос у вас отпадет). Как собственно и многие другие.","topic_id":10749},{"creator":{"public_name":"Дима Матвейчук","id":134792,"is_tutor":false},"id":22380,"body":"Я вот жду проектов на фронт енд, ведь насколько я знаю у вас только по бекенду. Да и я пока очевидно слабоват для проектов, раз даже задания не могу сделать без подсказок из вопросов и ответов) ","topic_id":10749},{"creator":{"public_name":"Дима Матвейчук","id":134792,"is_tutor":false},"id":22374,"body":"Спасибо за развернутый ответ Кирилл. Просто меня видимо отталкивает функциональный подход такой. Или же это из-за того что я туплю в экран часа 4 прежде чем решить задачу и у меня возникает боязнь что дальше я вообще не смогу решить нечего. \nВот буквально сейчас решаю следующую задачу из этого курса и снова остановился и видимо надолго. \nДоверюсь конечно учителю и надеюсь что проходя практику тут и задачи я смогу прийти к пониманию. \nP.S. Скройте код, если можно.","topic_id":10749}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":39218,"title":"Привет, очень странно ведет себя функция tail(). При создании листа в node через нее. Имя(первый элемент) можно брать при помощи head(), однако при использовании tail() ,к примеру, tail(l('name', 'hello, world!')) почему то возвращается пара вместо 'hello, world', т.е. хвоста. Почему так происходит? Неужели запятая внутри строки считается за разделитель, и поэтому возвращает пару? ","plain_title":"Привет, очень странно ведет себя функция tail(). При создании листа в node через нее имя(первый элемент) можно брать при помощи head(), однако при использовании tail() ,к примеру, tail(l('name', 'hello, world!')) почему то возвращается пара вместо 'hello, world', т.е. хвоста. Почему так происходит? Неужели запятая внутри строки считается за разделитель, и поэтому возвращает пару? ","creator":{"public_name":"Даниил Каминский","id":262229,"is_tutor":false},"comments":[{"creator":{"public_name":"Даниил Каминский","id":262229,"is_tutor":false},"id":85857,"body":"Чего мне не хватает в append? https://ru.hexlet.io/code_reviews/226399","topic_id":39218},{"creator":{"public_name":"Даниил Каминский","id":262229,"is_tutor":false},"id":85893,"body":"https://ru.hexlet.io/code_reviews/226399 как от скобок избавиться?","topic_id":39218},{"creator":{"public_name":"Даниил Каминский","id":262229,"is_tutor":false},"id":85850,"body":"https://ru.hexlet.io/code_reviews/226399 Этот вопрос касается решения: в функцию append передаю два аргумента (тег и пару), затем эта функция возвращает лист, которая передается в селекторы. После происходит проверка при помощи хвостов, которые должны выявить имена тэгов, но этот тест у меня не проходит, т.к. домы в тестах представляют из себя пару, и при проверке на хвост возвращает null, т.к. перед парой ничего нет. Я понимаю, что мне нужно каким то образом преобразовать в список вместо пары, чтобы можно было выделить хвост, но как это сделать? Данный шаг я исполняю в аппенде, но в итоге при присваивании значения в dom снова, каким то образом, вместо листа значение представляется в виде пары. ","topic_id":39218}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":9091,"title":"Помогите с фун toString, а то что-то запутался. Второй день над ней сижу. Вот весь код\n```\nexport const make = () => l();\n\nexport const node = (name, value) => cons(name, value);\nexport const name = (node) => car(node);\nexport const value = (node) => cdr(node);\n\nexport const append = (list, item) => consList(item, list);\n\nexport const toString = (dom) => {\n if(isEmpty(dom)) {\n return dom;\n }\n \n return `${head(dom)}</${name(head(dom))}>`, toString(tail(dom));\n};\n```\n","plain_title":"Помогите с фун toString, а то что-то запутался. Второй день над ней сижу. Вот весь код ``` export const make = () => l(); export const node = (name, value) => cons(name, value); export const name = (node) => car(node); export const value = (node) => cdr(node); export const append = (list, item) => consList(item, list); export const toString = (dom) => { if(isEmpty(dom)) { return dom; } return ${head(dom)}</${name(head(dom))}>, toString(tail(dom)); }; ``` ","creator":{"public_name":"Roman Vinogradov","id":132101,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Vinogradov","id":132101,"is_tutor":false},"id":18349,"body":"Подскажите, пожалуйста вот фун toString переделал\n```\nexport const toString = (html) => {\n const result = '';\n if (isEmpty(html)) {\n return result;\n }\n \n return toString(`<${name(head(html))}>${value(head(html))}</${name(head(html))}>`, tail(html));\n};\n```\nтест падает при проверки на 38 и 47 строке, а в моем коде на 17 и 21, но я не пойму что ему не нравиться?\n\n```\n\n Argument must be list, but it was '<h1>hello, world</h1>'\n \n at checkList (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/dist/index.js:46:11)\n at isEmpty (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/dist/index.js:105:3)\n at Object.<anonymous>.exports.toString.html (html-tags.js:17:36)\n at Object.<anonymous>.exports.toString.html (html-tags.js:21:10)\n at Object.it (__tests__/html-tags.test.js:38:35)\n at Promise.resolve.then.el (../../local/share/.config/yarn/global/node_modules/p-map/index.js:42:16)\n\n ● dom › #toString 3\n\n Argument must be list, but it was '<h2>header2</h2>'\n \n at checkList (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/dist/index.js:46:11)\n at isEmpty (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/dist/index.js:105:3)\n at Object.<anonymous>.exports.toString.html (html-tags.js:17:36)\n at Object.<anonymous>.exports.toString.html (html-tags.js:21:10)\n at Object.it (__tests__/html-tags.test.js:47:35)\n at Promise.resolve.then.el (../../local/share/.config/yarn/global/node_modules/p-map/index.js:42:16)\n```","topic_id":9091},{"creator":{"public_name":"Roman Vinogradov","id":132101,"is_tutor":false},"id":18351,"body":"Сам не смог решить toString, пришлось подсмотреть. Думаю что сам не додумался что можно вызывать фун рекурсивно в интерполяции. Абстракции сложно усваеваются.","topic_id":9091}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":3681,"title":"Подскажите в чём у меня ошибка. При прохождении теста выдаёт ошибку: \n```\n 1) dom #toString:\n TypeError: pair is not a function\n```\nРугается на последнюю строчку в:\n```\nconst pair4tag = head(listMy);\nconst name = car(pair4tag);\n```\nгде `listMy` - список пар для html-разметки\n\n\nДля болеее полной картины выложу весть текст написанной функции:\n```\nexport const toString = (list) => {\n let str = '';\n let listMy = list;\n while (!isEmpty(listMy)) {\n const pair4tag = head(listMy);\n const name = car(pair4tag);\n const value = cdr(pair4tag);\n str += `<${name}><${value}></${name}>`;\n listMy = tail(listMy);\n }\n return str;\n}\n```","plain_title":"Подскажите в чём у меня ошибка. При прохождении теста выдаёт ошибку: 1) dom #toString: TypeError: pair is not a function Ругается на последнюю строчку в: const pair4tag = head(listMy); const name = car(pair4tag); где listMy - список пар для html-разметки Для болеее полной картины выложу весть текст написанной функции: export const toString = (list) => { let str = ''; let listMy = list; while (!isEmpty(listMy)) { const pair4tag = head(listMy); const name = car(pair4tag); const value = cdr(pair4tag); str += `<${name}><${value}></${name}>`; listMy = tail(listMy); } return str; } ","creator":{"public_name":"Дмитрий Филь","id":102846,"is_tutor":false},"comments":[{"creator":{"public_name":"Дмитрий Филь","id":102846,"is_tutor":false},"id":6091,"body":"Да, всё верно. Проблема оказывается в `append`\nУ меня было:\n```\nexport const append = (el1, el2) => cons(el1,el2);\n```\nВсё уже почти заработало с кодом:\n```\nexport const append = (el1, el2) => cons(el2,el1);\n```\nЯ добавлял в список в неправильном порядке. Нужно было новый элемент писать в первый аргумент функции `cons()`, а я вставлял во второй аргумент.\nХотя я думал, что разницы нет и что возможно просто был бы другой порядок следования элементов.","topic_id":3681},{"creator":{"public_name":"Anton Shvab","id":46270,"is_tutor":false},"id":6089,"body":"Давайте посмотрим ваш код.","topic_id":3681},{"creator":{"public_name":"Anton Shvab","id":46270,"is_tutor":false},"id":6088,"body":"Ваш `pair4tag` подставляется на место формального параметра `pair` вот здесь: `const name = car(pair4tag);` В свою очередь функция `car` ждет на вход пару(`pair`), которая должна быть реализована как функция. Ошибка говорит: функция `car` получила на вход не-функцию. Значит ваш `pair4tag` не функция. ","topic_id":3681},{"creator":{"public_name":"Anton Shvab","id":46270,"is_tutor":false},"id":6090,"body":"На мой скромный взгляд функция будет работать, если вход придет правильный список из пар(т.е. нод). Если возникает ошибка значит проблема в реализации функций `node` (то как создается узел) или `append` (то как узел добавляется в список). \nМожет лучше внутри цикла `const` заменить на `let`? Как думаете?","topic_id":3681},{"creator":{"public_name":"Александр Шакун","id":23001,"is_tutor":false},"id":6469,"body":"Оставл]ю коммент чтобы этот вопрос не висел в интерфейсе ментора:)","topic_id":3681},{"creator":{"public_name":"Дмитрий Филь","id":102846,"is_tutor":false},"id":6092,"body":"> Может лучше внутри цикла const заменить на let? \n> Как думаете?\n\nС `const` всё в порядке, т.к эти переменные в дальнейшем не переопределяются","topic_id":3681}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":12576,"title":"Для чего нам make() не понимаю смысл этого конструктора ","plain_title":"Для чего нам make() не понимаю смысл этого конструктора ","creator":{"public_name":"Александр Немиров","id":139429,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":26236,"body":"> Для чего нам make() не понимаю смысл этого конструктора\n\nНужен для того же для чего и любой другой конструктор, он является частью абстракции. В курсе js: составные данные, тема конструкторов и абстракций разбиралась довольно подробно. Вы ее не поняли?\n","topic_id":12576},{"creator":{"public_name":"Александр Немиров","id":139429,"is_tutor":false},"id":26226,"body":"node смог реализовать, через пары. Осталось разобраться с конструктором make, можете объяснить какую функцию он тут несет и для чего он нужен","topic_id":12576},{"creator":{"public_name":"Александр Немиров","id":139429,"is_tutor":false},"id":26225,"body":"node тоже не пойму как работает и логику. В комментария написал как я реализовал изначально, но потом подумал нужно же было сделать абстракцию. Но как ни крути не пойму что от меня требуется и почему не работает \n```\n// removed\n```","topic_id":12576},{"creator":{"public_name":"Александр Немиров","id":139429,"is_tutor":false},"id":26329,"body":"Дело вот в чем. Я не знаю задуманно ли так, чтобы не объяснять всю суть задания. НО!\n\nЯ просто объясню как я решал эту задачу.\nЯ зашел в вопрос - ответ, чтобы понять что от меня требуется, примерно понял, начал решать, ошибка.\n\nВ итоге я долго и упорно искал ответ на вопрос что же мне нужно сделать (я не искал решения на задания, я искал сами задания). \nИ только после того, как я прочитал вопрос - ответ несколько раз, посетив слак и немного магии. Я понял, что от меня требуется абстракция на все эти функции, что в функциях как такого решения быть не должно. Звучит конечно просто \"абстракция\" но наделе, если вы не напишете конкретно, что нужно сделать именно это, то решить очень сложно.\n\nИ вот в след задании уже, опять такая проблема ну не понимаю я и все, что от меня нужно. Ладно когда задача поставлена и ты ищешь решение, а тут желания искать решения нету, потому что методом тыка и читанием тестов искать смысл задачи и подбирать решение уже надоело.","topic_id":12576},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":26330,"body":"> Я понял, что от меня требуется абстракция на все эти функции\n\nВ задании это написано прямым текстом. Есть набор функций которые надо реализовать, есть описание того что они принимают на вход и описание того что они должны вернуть. Даны примеры того как они работают на конкретных данных.\n\nУчитывая что тема абстракций шла весь предыдущий курс, то подразумевается что вы понимаете что такое абстракция и уже научились их создавать, что для вас понятны термины конструктор или селекторы.","topic_id":12576},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":26331,"body":"Я расширил описание, попробуйте сбросить прогресс и посмотреть насколько теперь понятно для вас.","topic_id":12576}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":16057,"title":"Трудноватое задание) но это даже плюс\n\nСмутило что в append список сразу не переворачиваем, почему бы и не перевернуть его сразу?","plain_title":"Трудноватое задание) но это даже плюс Смутило что в append список сразу не переворачиваем, почему бы и не перевернуть его сразу? ","creator":{"public_name":"Руслан Сухарев","id":170412,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":33973,"body":"Потому что это очень дорогая операция со сложностью O(n). С односвязными списками так не работают.","topic_id":16057}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":18882,"title":"Доброго времени суток, подскажите статью по оформлению кода, визуальному, линтер не ругается, но в сравнении с учителем мой код выглядит нагроможденным, в коде учителя иногда, например, строки пропускаются. Когда так лучше делать? и какие есть другие паттерны, для улучшения читабельности, спасибо)","plain_title":"Доброго времени суток, подскажите статью по оформлению кода, визуальному, линтер не ругается, но в сравнении с учителем мой код выглядит нагроможденным, в коде учителя иногда, например, строки пропускаются. Когда так лучше делать? и какие есть другие паттерны, для улучшения читабельности, спасибо) ","creator":{"public_name":"Anton Rehemae","id":172038,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":39849,"body":"Со временем и опытом вы будете писать более качественный код (если будете стараться, конечно :). Если интересует литература, то поищите подходящую в [рекомендуемых книгах](https://ru.hexlet.io/pages/recommended-books).","topic_id":18882}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":20030,"title":"Добрый день!\nМне кажется что неплохо бы добавить ясности к упражнению, т.к. создается впечатление, что `append(list, node)` должна добавить `node` в конец списка. На эту мысль наталкивает объяснение что должна делать функция `append()`, для иллюстрации работы которой используется `toString()`, которая выводит список так, как будто `node` добавляется в конец. Если бы не посмотрел тесты, не разобрался бы как должна работать `append(list, node)`.\n","plain_title":"Добрый день! Мне кажется что неплохо бы добавить ясности к упражнению, т.к. создается впечатление, что append(list, node) должна добавить node в конец списка. На эту мысль наталкивает объяснение что должна делать функция append(), для иллюстрации работы которой используется toString(), которая выводит список так, как будто node добавляется в конец. Если бы не посмотрел тесты, не разобрался бы как должна работать append(list, node). ","creator":{"public_name":"Сергей Синицын","id":105179,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей Синицын","id":105179,"is_tutor":false},"id":42419,"body":"> первым делом при начале работе с задачей вы должны анализировать тесты (а в реальной работе - писать тесты перед реализацией задачи)\n\nС этим согласен на все 100%. \n>И работа toString не может никак влиять на внутреннюю реализацию append\n\nЭто тоже не оспорить, видимо у меня просто уже рефлекс выработался от `java.lang.StringBuilder`, нужно избавляться от таких рефлексов.\nВ любом случае, спасибо за ответ.","topic_id":20030},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":42373,"body":"Здравствуйте!\n\n> На эту мысль наталкивает объяснение что должна делать функция append(), для иллюстрации работы которой используется toString(), которая выводит список так, как будто node добавляется в конец.\n\nУ этих двух функций разные зоны ответственности. Функция toString выводит строковое представление списка - а этих представлений может быть множество вариаций (например, можно вывести и в обратном порядке, если кому-то это понадобится). И работа toString не может никак влиять на внутреннюю реализацию append.\n\n> Если бы не посмотрел тесты, не разобрался бы как должна работать append(list, node).\n\nПервым делом при начале работе с задачей вы должны анализировать тесты (а в реальной работе - писать тесты перед реализацией задачи). Мы к этому призываем на протяжении всех наших курсов.","topic_id":20030}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}},{"id":5871,"title":"Добрый день! \n\nЯ вроде задание решила (глядя в вопросы и ответы), но какой-то осадок непонятости остался.\n\nС функциями > make и > node все понятно. А вот дальше как-то туманно.\n\nНасколько я понимаю, функция > append принимает на вход список (пустой или нет) и пару из тега и текста этого тега, правильно? И, соответственно, \"запихивает\" в список эту пару.\n\nА дальше мы преобразуем этот получившийся список в строку. Так?\n\nКогда я пытаюсь посмотреть, что получилось в списке до его преобразования в строку и пишу: \n\n```\nconst dom1 = make();\nconst dom2 = append(dom1, node('h1', 'hello, world'));\nconst dom = append(dom2, node('h2', 'header2'));\nconsole.log(dom);\n```\n\nто мне приходит вот такой ответ:\n\n```\nconsole.log solution.js:25\n[Function]\n```\n\nкак мне понять, что оказалось в dom?\n\nИ еще совсем не понимаю вот эту часть:\n\n```\nconst element = head(elements);\nconst tag = name(element);\nreturn `${toString(tail(elements))}<${tag}>${value(element)}</${tag}>`;\n```\n\nЗачем делать > ${toString(tail(elements))} что это дает?\n\nЗаранее спасибо за помощь!\n\nP.S. Я вроде и дополнительные задания проходила после пройденных курсов, и вроде даже какое-то понимание ко мне приходило. А потом снова - раз! - и ничего не понятно =(и ничего не понятно =(","plain_title":"Добрый день! Я вроде задание решила (глядя в вопросы и ответы), но какой-то осадок непонятости остался. С функциями > make и > node все понятно. А вот дальше как-то туманно. Насколько я понимаю, функция > append принимает на вход список (пустой или нет) и пару из тега и текста этого тега, правильно? И, соответственно, \"запихивает\" в список эту пару. А дальше мы преобразуем этот получившийся список в строку. Так? Когда я пытаюсь посмотреть, что получилось в списке до его преобразования в строку и пишу: const dom1 = make(); const dom2 = append(dom1, node('h1', 'hello, world')); const dom = append(dom2, node('h2', 'header2')); console.log(dom); то мне приходит вот такой ответ: console.log solution.js:25 [Function] как мне понять, что оказалось в dom? И еще совсем не понимаю вот эту часть: const element = head(elements); const tag = name(element); return `${toString(tail(elements))}<${tag}>${value(element)}</${tag}>`; Зачем делать > ${toString(tail(elements))} что это дает? Заранее спасибо за помощь! P.S. Я вроде и дополнительные задания проходила после пройденных курсов, и вроде даже какое-то понимание ко мне приходило. А потом снова - раз! - и ничего не понятно =(и ничего не понятно =( ","creator":{"public_name":"Anna Kuzina","id":116694,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikita Antonenkov","id":49435,"is_tutor":false},"id":10624,"body":"Правильно. Не получилось решить? Можете показать вывод тестов?","topic_id":5871},{"creator":{"public_name":"Anna Kuzina","id":116694,"is_tutor":false},"id":10607,"body":"```name``` и ```value``` - это ```car``` и ```cdr```, созданные за пределами функции ```toString```.\n\n> Рекурсивный вызов возвращает tail(elements), преобразованный в строку, и мы конкатенируем его с head(elements).\n\nМы это делаем, потому что нам в начало надо поставить те элементы из списка, которые попали туда первыми, правильно? ","topic_id":5871},{"creator":{"public_name":"Nikita Antonenkov","id":49435,"is_tutor":false},"id":10595,"body":"Смотрите, `dom` представлен в виде списка, и мы можем работать с ним, как с парой, выдёргивая из него первый элемент (`head(dom)`) и передавая в рекурсивный вызов оставшиеся (`tail(dom)`), пока он не останется пустым (`isEmpty(dom) === true`). Тогда образовавшаяся в памяти цепочка вычислений вычислится (простите за тавтологию) и вернёт значение.\n\nВ редакторе используется синтаксис Markdown, подробн он описан [тут](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet), а вообще для выделения кода можно нажать на кнопку `</>` :)","topic_id":5871},{"creator":{"public_name":"Nikita Antonenkov","id":49435,"is_tutor":false},"id":10603,"body":"Что за функции `name` и `value`?\n\nРекурсивный вызов возвращает `tail(elements)`, преобразованный в строку, и мы конкатенируем его с `head(elements)`.","topic_id":5871},{"creator":{"public_name":"Anna Kuzina","id":116694,"is_tutor":false},"id":10591,"body":"Никита, здравствуйте! Спасибо за помощь!\n\nКажется, я поняла! \n\nТо есть, мы сначала достаем первый элемент из списка, который был туда добавлен последним (**dom**), а потом, с помощью рекурсии, достаем оставшуюся часть из списка. И эту рекурсию ставим вперед, чтобы остальные части списка появлялись перед **dom**, правильно?\n\nА можно еще вопрос не по теме: как Вы выделяете красным цветом и серым фоном отдельные слова/предложения в тексте? =)","topic_id":5871},{"creator":{"public_name":"Nikita Antonenkov","id":49435,"is_tutor":false},"id":10590,"body":"Анна, добрый день!\n\nПо поводу функции `append`: да, `append` принимает список элементов и новый тег, который мы хотим добавить. Преобразование в строку должно происходить в функции `toString`. Небольшой лайфхак: чтобы вывести код функции, используйте строковую интерполяцию.\n\nНасчёт второго вопроса: в `toString` вы берёте первый элемент списка, преобразуете его в строку и конкатенируете с результатом рекурсивного вызова `toString`, в который передаёте оставшиеся элементы `dom`. Здесь полезно вспомнить, как работает [линейно рекурсивный процесс](https://ru.hexlet.io/courses/programming-basics/lessons/recursive_process/theory_unit).\n\nЧто понимание приходит не сразу - это нормально. Главное не прекращать заниматься :)","topic_id":5871},{"creator":{"public_name":"Anna Kuzina","id":116694,"is_tutor":false},"id":10641,"body":"Никита, решить получилось! Спасибо Вам большое за помощь! \n\nПросто всё равно не до конца всё понятно. То проскакивает луч понимания, то снова туман =(","topic_id":5871},{"creator":{"public_name":"Anna Kuzina","id":116694,"is_tutor":false},"id":10596,"body":"Никита, смотрите, вот такое решение \n\n```\nexport const toString = elements => {\n if (isEmpty(elements)) {\n return '';\n }\n const element = head(elements);\n const tag = name(element);\n const text = value(element);\n\n return `${toString(tail(elements))}<${tag}>${text}</${tag}>`;\n};\n```\n\nЯ пытаюсь посмотреть, что же находится в ```tail(elements)``` и ```head(elements)``` вот таким образом прямо внутри функции toString:\n\n```\nconsole.log('this is head elements '+head(elements));\n console.log('this is tail elements '+tail(elements));\n console.log('this is tag '+tag);\n console.log('this is text '+text);\n```\n\nи мне дается вот такой вывод:\n\n```\nthis is head elements function (message) {\n switch (message) {\n case 'car':\n return a;\n case 'cdr':\n return b;\n default:\n throw new Error('Unknown message \\'' + message + '\\'');\n }\n }\n\n console.log solution.js:20\n this is tail elements null\n```\n\nА вот с этим возвратом ```return `${toString(tail(elements))}<${tag}>${text}</${tag}>`;``` я не понимаю только, почему рекурсия ставится в один ряд с последовательностью тег-текст-тег?","topic_id":5871},{"creator":{"public_name":"Nikita Antonenkov","id":49435,"is_tutor":false},"id":10645,"body":"Отлично! \n\nПонимание придёт с практикой. Ещё крайне рекомендую почитать [книжки](https://map.hexlet.io/pages/books), в частности, \"Структура и интерпретация компьютерных программ\".","topic_id":5871}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Разметка","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":456,"slug":"js_sequences_html_exercise","name":null,"state":"active","kind":"exercise","language":"javascript","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## html-tags.js\n\nРеализуйте абстракцию для создания html. Она включает в себя следующие функции:\n\n* `make()` — конструктор. Уже реализован. Не принимает параметров, и создает HTML-список.\n* `node()` — создает новый тег. Содержит два элемента, имя тега и его содержимое. Дополнительно реализуйте селекторы тега: `getName()` и `getValue()`.\n\n ```javascript\n const tag = node('div', 'what is love?');\n getName(tag); // div\n getValue(tag); // what is love?\n ```\n\n* `append()` — добавляет элемент (тег), созданный с помощью `node()`, в HTML-список. Возвращает новый HTML-список. Новый элемент должен добавляться в начало (\"голову\") списка.\n* `toString()` — возвращает текстовое представление HTML на основании HTML-списка.\n\nПример использования этого интерфейса:\n\n```javascript\nimport { make, append, toString, node } from './html-tags.js';\n\n// Создаем новый html-список\nconst dom1 = make();\n\n// Создаем тег и сразу добавляем его в html\nconst dom2 = append(dom1, node('h1', 'hello, world'));\n// Еще раз\nconst dom3 = append(dom2, node('h2', 'header2'));\n\n// Создаем новый тег\nconst tag = node('h3', 'header3');\n// Добавляем созданный тег в html-список\nconst dom = append(dom3, tag);\n\n// Преобразуем html-список в строчку\ntoString(dom);\n// <h1>hello, world</h1><h2>header2</h2><h3>header3</h3>\n\n// Пример без создания промежуточных переменных\ntoString(append(make(), node('p', 'this is Sparta!')));\n// <p>this is Sparta!</p>\n```\n\nЭкспортируйте все созданные функции.\n","prepared_readme":"## html-tags.js\n\nРеализуйте абстракцию для создания html. Она включает в себя следующие функции:\n\n* `make()` — конструктор. Уже реализован. Не принимает параметров, и создает HTML-список.\n* `node()` — создает новый тег. Содержит два элемента, имя тега и его содержимое. Дополнительно реализуйте селекторы тега: `getName()` и `getValue()`.\n\n ```javascript\n const tag = node('div', 'what is love?');\n getName(tag); // div\n getValue(tag); // what is love?\n ```\n\n* `append()` — добавляет элемент (тег), созданный с помощью `node()`, в HTML-список. Возвращает новый HTML-список. Новый элемент должен добавляться в начало (\"голову\") списка.\n* `toString()` — возвращает текстовое представление HTML на основании HTML-списка.\n\nПример использования этого интерфейса:\n\n```javascript\nimport { make, append, toString, node } from './html-tags.js';\n\n// Создаем новый html-список\nconst dom1 = make();\n\n// Создаем тег и сразу добавляем его в html\nconst dom2 = append(dom1, node('h1', 'hello, world'));\n// Еще раз\nconst dom3 = append(dom2, node('h2', 'header2'));\n\n// Создаем новый тег\nconst tag = node('h3', 'header3');\n// Добавляем созданный тег в html-список\nconst dom = append(dom3, tag);\n\n// Преобразуем html-список в строчку\ntoString(dom);\n// <h1>hello, world</h1><h2>header2</h2><h3>header3</h3>\n\n// Пример без создания промежуточных переменных\ntoString(append(make(), node('p', 'this is Sparta!')));\n// <p>this is Sparta!</p>\n```\n\nЭкспортируйте все созданные функции.\n","has_solution":true,"entity_name":"Разметка"},"units":[{"id":1355,"name":"theory","url":"/courses/sequences/lessons/html/theory_unit"},{"id":1486,"name":"quiz","url":"/courses/sequences/lessons/html/quiz_unit"},{"id":1356,"name":"exercise","url":"/courses/sequences/lessons/html/exercise_unit"}],"links":[],"ordered_units":[{"id":1355,"name":"theory","url":"/courses/sequences/lessons/html/theory_unit"},{"id":1486,"name":"quiz","url":"/courses/sequences/lessons/html/quiz_unit"},{"id":1356,"name":"exercise","url":"/courses/sequences/lessons/html/exercise_unit"}],"id":688,"slug":"html","state":"approved","name":"Разметка","course_order":300,"goal":"Начинаем разрабатывать библиотеку для работы с HTML, знакомимся с Markdown и изучаем общие принципы языков разметки на примере HTML","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"170597335","theory":"На данный момент мы изучили все необходимые строительные блоки, которые позволят нам построить библиотеку по работе с HTML. Поэтому пришло время познакомиться ближе с этим языком разметки, узнать, что он из себя представляет и как с ним правильно работать.\n\n## Языки разметки: Markdown и HTML\n\n> Набор символов или последовательностей, вставляемых в текст для передачи информации о его выводе или строении.\n\nМы не просто пишем какой-то текст, но и хотим его разметить для того, чтобы потом автоматически преобразовать в необходимое нам визуальное представление. Большинство людей пользовалось программой Microsoft Word, в которой как раз этим и занимаются: выделяют текст жирным, делают его подчёркнутым, разбивают на абзацы и т.д. При этом данная программа является примером механизма, где вы не видите те самые символы и последовательности, которые вставляются в текст. Вы видите сразу результат. Эти символы и последовательности существуют где-то внутри.\n\nЕсть и прямой способ, когда вы создаёте настоящий текст, в который вставляются маркеры. Это достаточно важно с точки зрения программных манипуляций. Microsoft Word, в конце концов, скрыто от вас, делает то же самое. Один из самых простых и распространённых языков разметки — это [Markdown](https://en.wikipedia.org/wiki/Markdown). Исходник текста при этом выглядит вот так:\n\n\n```markdown\n## Языки разметки: Markdown и HTML\n\n> Набор символов или последовательностей,\n> вставляемых в текст для передачи информации\n> о его выводе или строении.\n```\n\nЗаголовок, который здесь выводится, обозначается двумя решётками, что соответствует заголовку второго уровня. Символ `>` обозначает цитату. И если она пишется подряд, то превращается в единую цитату, отображается с отступом от левой границы документа и чуть более крупным шрифтом. В Markdown есть много других способов вывести ту или иную информацию заголовками, абзацами, списками и т.д. При этом разметка текста происходит крайне просто. Нужно только запомнить маркеры и использовать их. Markdown встречается повсеместно. Почти все редакторы в интернете используют Markdown. Раздел Обсуждения на Хекслете, где можно что-то спросить, о чём-то поговорить, написать комментарий, тоже поддерживает Markdown. Это очень удобно. Например, если нужно выделить или вставить код. Не все об этом знают, но я надеюсь, что с этого момента, вы будете использовать данные возможности.\n\nMarkdown — не единственный способ размечать текст.\n\nБолее распространённым является HTML. Но это не значит, что они друг друга заменяют, просто их используют в разных ситуациях.\n\n> Следует отметить, что язык разметки неполон по Тьюрингу и обычно не считается языком программирования.\n\n[Полнота по Тьюрингу](https://en.wikipedia.org/wiki/Turing_completeness) — это формальный критерий, который говорит о вычислимости, т.е. можно ли реализовать какую-либо вычислимую функцию на данном языке программирования. И если язык не полон по Тьюрингу, то, строго говоря, его нельзя называть языком программирования. Скорее всего, это так называемый [DSL](https://en.wikipedia.org/wiki/Domain-specific_language), т.е. язык описания. И языки разметки как раз являются языками описания.\n\nЧасто новички и те, кто начинают вникать в эту тему, говорят **программировать на HTML**. Если вы попробуете поискать данную формулировку в интернете, то увидите, что есть статьи, где такое словосочетание встречается. На самом деле оно в корне не верно, потому что невозможно программировать на не Тьюринг полной системе. И HTML — это всего лишь способ оформлять текст, просто набор тегов, которые надо запомнить. И, естественно, с помощью HTML вы не можете описать какие-то программы.\n\nЭто касается и более сложных систем, которые не полны по Тьюрингу.\n\n```html\n<h3>Язык разметки: Markdown, HTML, ...</h3>\n<blockquote>Следует отметить, что язык\nразметки неполон по Тьюрингу и\nобычно не считается языком программирования.</blockquote>\n\n<p>\n <b>HTML</b>\n</p>\n```\n\nВ примере приводится код, оформленный в виде HTML. Можно заметить, что HTML оперирует таким понятием, как теги. Они бывают открывающие и закрывающие и говорят, где начинается и где заканчивается действия тега. Они обрамляются угловыми скобками.\n\nВ отличие от Markdown, HTML сложнее. Он требует больше действий, и поэтому крайне редко используется в тех же редакторах, где нужно описывать разметку руками. Потому что с Markdown это очень просто делать. Но HTML даёт гораздо больше возможностей для того, чтобы выражать сложные структуры. И в реальности чаще всего Markdown в итоге превращается в HTML. Существуют специальные программы, которые анализируют Markdown, транслируют его в HTML и дальше он показывается в браузере или используется где-то ещё.\n\n## Базовые элементы HTML\n\n```html\n<!-- Комментарий -->\n<hr>\n<h1>Заголовок</h1> <!-- h1 может быть только один -->\n<p>Параграф</p>\n<blockquote>Цитата</blockquote>\n<ul>\n <li>Первый элемент</li>\n <li>Второй элемент<li>\n</ul>\n```\n\nУ HTML очень много разных элементов. В примере показаны только некоторые из базовых.\n\n- Например, HTML поддерживает комментарии — одиночный тег, который оформляется `<!-- -->` и не отображается на итоговой странице.\n- Есть одиночный тег `<hr>`. Он проводит горизонтальную черту.\n- Заголовок `<h1>`. Причём заголовок первого уровня может быть только один в документе HTML. Если вы их сделаете несколько, браузер всё равно их отобразит. Парсится и отображается любой, даже не валидный HTML, т.е. семантически у нас не может быть двух заголовков первого уровня, а синтаксически можно просто забыть закрывающий тег и всё равно всё будет работать. Эта особенность, в том числе, позволила HTML стать очень популярным, но и сделала браузеры очень сложными в части реализации работы с не валидным HTML-кодом.\n- Есть тег `<p>` — параграф, достаточно очевидное имя.\n- `<blockquote>` — это цитата.\n- Списки `<ul>`, которые внутри используют элементы `<li>`.\n\n## Особенности\n\n* Логическая (`h1`) / Визуальная разметка (`b`)\n* Форматирование `html` не влияет на внешний вид\n* Иерархическая структура\n\nВо-первых, разметка делится на два типа: логическая и визуальная.\n\n- **Логическая разметка** (можно говорить ещё семантическая) определяет смысловую структуру. Когда мы пишем `h1`, мы имеем в виду именно заголовок первого уровня, не указывая, как он будет выглядеть. Это говорит о том, чем является этот кусок текста. А то, как он выглядит, зависит от многих факторов.\n- **Визуальная разметка** определяет то, как должен выглядеть элемент. Например, есть тег `b`. Расшифровывается как bold (англ. жирный). Он не несёт никакого семантического смысла. Это просто указание, что кусок текста внутри данного тега должен быть отображён жирным.\n\nВажный момент, который надо знать про HTML, это то, что форматирование самого HTML, т.е. как теги оформлены внутри, в какой последовательности описаны, сделаны ли между ними переводы строк, никак не влияет на то, как он будет отображён. Вы можете написать всё в одну непрерывную строчку, и всё будет отображаться в соответствие с правилами тегов в строке, но не с тем, как вы оформили сам HTML. Вы легко можете в этом убедиться, если на любой странице любого сайта откроете исходный код через контекстное меню, вы увидите, что внутри чаще всего какое-то месиво. А закроешь его, и отображается красивый сайт.\n\nHTML — иерархическая структура: элементы могут входить в элементы: `<p>Раз <b>Два</b> Три</p>`.\n\n## Интерфейс\n\nТеперь давайте рассмотрим интерфейс нашей библиотеки, которая позволит нам строить HTML. Пока мы не будем работать с иерархическим способом задания (это следующий этап), будем работать только с плоским.\n\n```javascript\nimport {\n make, append, toString, node,\n} from 'hexlet-html-tags'\n\nconst html1 = make()\nconst html2 = append(html1, node('h1', 'hexlet'))\nconst p1 = node('p', 'hello, world')\nconst html3 = append(html2, p1)\n\n// <h1>hexlet</h1>\n// <p>hello, world</p>\ntoString(html3)\n```\n\nИтак, у нас есть четыре базовых функции.\n\n- `make` — это конструктор, с помощью которого мы создаём html, структуру. По сути вызов `make` возвращает список, в который мы будем постепенно добавлять теги.\n- Давайте посмотрим способ добавления тега. Для этого используется функция `append`, которой мы передаём созданный HTML.\n- Далее используем ещё одну функцию `node`, которая создаёт ноду, элемент нашего html-документа. Она принимает на вход два параметра. Имя тега и body, т.е. контент, который будет внутри этого тега. Как видите, нам не надо никаких закрывающих тегов. При выводе они будут добавляться автоматически.\n- И `toString` — функция позволяющая распечатывать содержимое списка тегов.\n\nИз кода видно, что это неизменяемая структура, так как нам пришлось создать `html2` после вызова `append`, потому что изменения `html1` не происходит. Далее мы создаём ещё один элемент — абзац `p1`, добавляем в него текст `hello, world`, \"аппендим\" его и получаем `html3`.\n\nЕсли с помощью `toString` мы выведем наш `html`, то увидим вполне красивую разметку.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":1351,"name":"theory","url":"/courses/sequences/lessons/intro/theory_unit"}],"links":[{"id":425614,"name":"Документация по функциям для работы с парами js-pairs","url":"https://github.com/hexlet-components/js-pairs/tree/main/docs"}],"ordered_units":[{"id":1351,"name":"theory","url":"/courses/sequences/lessons/intro/theory_unit"}],"id":686,"slug":"intro","state":"approved","name":"Введение","course_order":100,"goal":"Знакомимся с курсом и проектом «Генератор HTML», который будет постепенно разрабатываться в течение всего курса","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"170597336","theory":"Этот курс — логическое продолжение предыдущего курса про [Составные данные](https://ru.hexlet.io/courses/compound_data). Теперь мы будем говорить о составных данных в чуть более сложном и продвинутом их виде. Как это часто бывает на Хекслете, данный курс будет не про JavaScript и не про изучение его возможностей, а про некоторые фундаментальные вещи, которые обязан знать программист, не зависимо от того, на каком языке он пишет код. JavaScript в данном случае всего лишь способ выражения той идеи, которую мы хотим донести.\n\n## Что такое последовательности\n\n\n\nПоследовательность — это упорядоченная совокупность объектов данных.\n\nДавайте разберём это определение. Оно звучит немного страшновато, но в реальности всё очень просто. Под совокупностью подразумевается некая единая сущность, а под объектами данных подразумевается всё, что угодно. В нашем случае, в программировании, это могут быть: числа, строки, составные объекты (например, пары). В математике последовательности представлены очень широко. Этому посвящены целые её разделы, например, матанализ, который изучает различные числовые последовательности: натуральные числа или числа Фибоначчи, с которыми мы так или иначе имеем дело когда учимся программированию. В реальной жизни с ними никто не сталкивается, но их любят использовать при обучении.\n\n- Натуральные числа\n\n```\n1, 2, 3, 4, 5, 6, 7, 8, ...\n```\n\n- Числа Фибоначчи\n\n```\n0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...\n```\n\n## Списки\n\n- список файлов\n- список дел\n- список сотрудников\n- список сайтов\n- список списков :)\n\nВ программировании гораздо чаще, чем с числовыми последовательностями, мы будем иметь дело со списками. Списки являются практически центральной частью в любой системе. Например, в функциональных языках манипуляции со списками являются ключевым моментом при написании кода.\n\nКакие бывают списки? Если на компьютере вы открываете папку, то видите список файлов. У вас где-то записан список дел, более того, вы можете их вести в онлайновых системах. На предприятии есть список сотрудников. А если вы ищете в Яндекс, Google или другом поисковике, то видите список сайтов с пейджингом (переключатель, который позволяет вам ходить по страницам одного большого списка). И даже такой мета-список, который вы видите сейчас перед собой в данный момент — список списков. Это тоже список. Так что списки можно комбинировать, делать из них новые списки и это очень напоминает то, что мы делали в курсе Составные данные.\n\nВ этом курсе мы будем разрабатывать проект, который называется **Генератор HTML**. Это библиотека, генерирующая части HTML-кода. Если на текущий момент вы не знакомы с HTML, то не стоит переживать, потому что в будущих уроках мы будем обязательно разбирать, что это такое, как он работает, для чего нужен и что такое языки разметки вообще. Это крайне простая тема, на освоение которой не уйдет много времени.\n\n## Принцип работы\n\nПринцип работы нашего проекта показан в примере ниже:\n\n```javascript\nimport {\n make, append, addChild, toString, node,\n} from '@hexlet/html-tags'\n\nconst ul1 = node('ul')\nconst ul2 = addChild(ul1, node('li', 'hello'))\nconst ul3 = addChild(ul2, node('li', 'world'))\n\nconst html1 = make()\nconst html2 = append(html1, ul3)\n```\n\nСейчас не обязательно пытаться полностью понять данный код. Он иллюстрирует, что в конечном итоге получится библиотека, в которой есть обычные функции, позволяющие нам строить древовидную структуру. Которая с одной стороны список,а с другой может быть сложней, чем список. Например, она может быть деревом, о чём будет рассказано позже. В конечном итоге после того, как мы создали эту структуру, мы можем её распечатать и увидеть, что генерируется такое представление:\n\n```javascript\ntoString(html2)\n// <ul>\n// <li>hello</li>\n// <li>world</li>\n// </ul>\n```\n\nЭтот кусочек и есть часть HTML-кода.\n\n## Применение\n\n- программная (динамическая) генерация\n- манипуляции\n- анализ (например, проверка корректности)\n- внутреннее представление в браузере\n\n## Зачем такие библиотеки вообще нужны?\n\n1. Программная (динамическая) генерация HTML очень востребованная операция. Существует большое количество сайтов, которые используют, например, технологии [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application), когда не происходит перезагрузки страницы, а всё меняется прямо на самом сайте. HTML в таком случае действительно генерируется динамически и в итоге представление перерисовывается непосредственно на клиенте.\n\n2. Кроме этого такие библиотеки позволяют манипулировать текущим HTML, который уже загружен и используется. Собственно сайты это постоянно и делают, когда вы закрываете какое-то окно, что-то открываете или меняете на странице.\n\n3. Помимо самого использования есть и анализ, например, валидация или проверка корректности. Верификация — когда проверяется правильно ли создана структура, так как HTML обладает определёнными правилами построения и их можно нарушить. То есть когда в браузере идёт работа с HTML, можно создать HTML, который будет невалиден — не пройдет верификацию. Библиотеки позволяют анализировать и давать рекомендации о том, как лучше делать, а как лучше не делать.\n\n4. И самое интересное, что в реальности именно так внутри браузера и представлен HTML. Если мы загрузим какую-то страницу и посмотрим её исходный код, мы увидим, что там есть HTML как текст. Но текст — это всего лишь текстовое представление HTML, а в реальности он загружается в виде некоторой программной сущности. У неё есть специальное название — [DOM](https://en.wikipedia.org/wiki/Document_Object_Model). Модель построения элементов на веб-странице. Она хранится где-то внутри и ею можно манипулировать\n\n## Document Object Model\n\n```javascript\nconst impl = document.implementation\nlet doc = impl.createDocument('', '', null)\nlet peopleElem = doc.createDocument('people')\n\nlet personElem1 = doc.createDocument('personal')\npersonElem1.setAttribute('first-name', 'eric')\n\nlet addressElem = doc.createDocument('address')\naddressElem.setAttribute('street', '321 south st')\npersonElem1.appendChild(addressElem)\n\npeopleElem.apendChild(personElem1)\ndoc.appendChild(peopleElem)\n```\n\nЗдесь как раз показан пример того, как в браузере происходит манипуляция DOM, т.е. реальным представлением HTML, как он представлен в программном коде. Опять же, не нужно рассчитывать на то, что вы сейчас поймёте этот код, тем более, что он содержит некоторые новые аспекты. Важно, что та библиотека, которую мы разрабатываем, является по сути локальным представлением того, что происходит в браузере. Зная принципы её работы, умея самостоятельно её написать, вы будете легко ориентироваться в том, как это происходит в браузере. По сути мы делаем прототип настоящего DOM. Конечно, сложность очень сильно отличается, потому что DOM в браузере очень навороченная вещь. Наша библиотека гораздо проще, но главное это понять общий принцип.\n\n## Темы\n\n- списки, множества, деревья\n- отображение, фильтрация, агрегация\n- стандартные интерфейсы\n- уровневое проектирования\n\n### Какие темы мы рассмотрим в этом курсе?\n\nИх много, и они достаточно серьёзные.\n\n1. Первое — это структуры данных. Мы рассмотрим списки, познакомимся с понятием множеств и с деревьями.\n\n2. Мы научимся с ними работать посредством тройки функций: отображение (`map`), фильтрация (`filter`) и агрегация (`reduce`). Это тройка методов, которые используются для обработки различных списков, множеств, деревьев и других структур данных, которые существуют в JavaScript. Когда в будущем мы будем работать с внутренними структурами языка, вы увидите, что это основные способы манипуляции структурами в JS и, кстати, во всех функциональных языках и даже не в функциональных. Во всех достаточно продвинутых языках программирования, которые поддерживают функции высшего порядка, реализована эта тройка. А код с использованием данных методов является чаще всего каноническим, т.е. так принято и правильно писать, а не использовать, например, циклы, о чём мы позже обязательно поговорим.\n\n3. Мы познакомимся с таким подходом, как стандартные интерфейсы. Ярким примером данного подхода является конструктор для детей Lego.\n\n4. Также мы познакомимся с уровневым проектированием, которое касается не только программирования, а всей инженерной технической области. Именно оттуда оно берет начало и именно оттуда уровневому проектированию можно учиться.\n\n## Превосходство Хекслета\n\n- функциональный стиль и неизменяемость\n- СИКП\n- фокус на программировании, а не синтаксисе языка\n- двигаемся вперед не с помощью изучения новых фич языка, а путем комбинирования изученных инструментов и развития абстрактного мышления\n- нет нового синтаксиса\n\nНапоследок, несколько аспектов, почему курс сделан именно так. Почему и зачем мы освещаем эту тему на Хекслете.\n\nЭтот курс продолжает традицию предыдущего курса. Всё, что мы здесь делаем, будет неизменяемым. Мы используем функциональный стиль. Потому что введение состояния привносит множество сложностей и проблем, и на данном этапе для понимания темы это совершенно не нужно. Поэтому мы оставляем за скобками изменение. Библиотека может показаться вам немного странной, но чуть позже вы поймёте, почему она реализована именно так.\n\nПомимо этого курс продолжает традицию и также основан на СИКПе ([Структура и интерпретация компьютерных программ](https://web.mit.edu/6.001/6.037/sicp.pdf)). В нём отсутствует новый синтаксис. Мы в очередной раз подчёркиваем, что этот курс не про изучение JavaScript, он про изучение программирования. Пройдя его, вы действительно поймёте и, наконец, осознаете разницу между тем, что такое программировать и что такое знать синтаксис языка.\n\nВсё, что происходит в этом курсе — мы просто берём то, что уже изучили, начинаем это комбинировать и получаем какие-то новые возможности нашей системы, более сложное поведение. Данный подход развивает абстрактное мышление. Многие вещи, которые мы делаем в этом курсе, направлены не только на то, чтобы вы понимали, что такое программирование, но и постепенно начинали развивать свой мозг и тренировать его для того, чтобы он мог переваривать всё более и более сложные сущности и концепции, потому что изучение нового синтаксиса не привносит нового развития в части того, как вы размышляете. Оно не позволит вам автоматически строить действительно сложные программы. А вот абстрактное мышление — это как раз та вещь, которая влияет на это больше всего. Некоторые уроки и практические задания могут показаться весьма сложными, потому что в этом курсе будет много кода и он будет заставлять ваш мозг кипеть. В каком-то смысле это лакмусовая бумажка. Если вы пройдёте курс, действительно в нём разберётесь и поймёте его, то скорее всего дальнейшее обучение пройдет для вас достаточно легко и, в целом, в программировании у вас всё будет хорошо получаться.\n\nЖелаю удачи! Вперёд!\n"},"id":120,"slug":"sequences","challenges_count":10,"name":"JS: Последовательности","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы продолжите изучать составные данные на более продвинутом уровне и рассмотрите тип данных «список». Вы узнаете больше о функциях высшего порядка filter, map и reduce и иерархических структурах. В итоге вы научитесь строить сложные структуры данных на базе более простых и проектировать функции так, чтобы их можно было легко соединять друг с другом. Составные данные пригодятся, если вы решите работать над проектами, которые требуют обработку сложных структур данных. Знания из этого курса помогают программистам обрабатывать коллекции, представленные списками с помощью функций высшего порядка.","kind":"additional","updated_at":"2026-01-20T11:55:07.138Z","language":"javascript","duration_cache":36000,"skills":["Строить сложные структуры данных на базе более простых","Проектировать функции так чтобы их можно было легко соединять друг с другом","Обрабатывать коллекции представленные списками с помощью функций высшего порядка (map/filter/reduce)","Разделять код на уровни, выстраивая правильное взаимодействие между слоями"],"keywords":["функции высшего порядка","стандартные интерфейсы","уровневое проектирование"],"lessons_count":9,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6OTE0OCwicHVyIjoiYmxvYl9pZCJ9fQ==--d07901b5af81babe7daa5cc3f1853beb9c936160/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJqcGciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--39ba06fa99226096df9fc6bb31f84e1d29ea98e9/image.png"},"recommendedLandings":[{"stack":{"id":20,"slug":"js-sicp","title":"СИКП на JS","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4050,"duration_in_months":1},"id":28,"slug":"js-sicp","title":"СИКП на JS","subtitle":"Навык понимать программы на фундаментальном уровне, уверенно проходить собеседования и решать сложные задачи","subtitle_for_lists":"Навык фундаментального программирования","locale":"ru","current":true,"duration_in_months_text":"1 месяц","stack_slug":"js-sicp","price_text":"от 3 900 ₽","duration_text":"1 месяц","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc2MCwicHVyIjoiYmxvYl9pZCJ9fQ==--9348098e4053d798b6f34bee4ef66947540261e4/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-rafiki.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/sequences/lessons/html/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">JS: Последовательности</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Разметка</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Разметка","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"JS: Последовательности"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>На данный момент мы изучили все необходимые строительные блоки, которые позволят нам построить библиотеку по работе с HTML. Поэтому пришло время познакомиться ближе с этим языком разметки, узнать, что он из себя представляет и как с ним правильно работать.</p>
<h2 id="heading-2-1">Языки разметки: Markdown и HTML</h2>
<blockquote>
<p>Набор символов или последовательностей, вставляемых в текст для передачи информации о его выводе или строении.</p>
</blockquote>
<p>Мы не просто пишем какой-то текст, но и хотим его разметить для того, чтобы потом автоматически преобразовать в необходимое нам визуальное представление. Большинство людей пользовалось программой Microsoft Word, в которой как раз этим и занимаются: выделяют текст жирным, делают его подчёркнутым, разбивают на абзацы и т.д. При этом данная программа является примером механизма, где вы не видите те самые символы и последовательности, которые вставляются в текст. Вы видите сразу результат. Эти символы и последовательности существуют где-то внутри.</p>
<p>Есть и прямой способ, когда вы создаёте настоящий текст, в который вставляются маркеры. Это достаточно важно с точки зрения программных манипуляций. Microsoft Word, в конце концов, скрыто от вас, делает то же самое. Один из самых простых и распространённых языков разметки — это <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/Markdown" rel="noopener noreferrer" target="_blank">Markdown</a>. Исходник текста при этом выглядит вот так:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">## Языки разметки: Markdown и HTML
> Набор символов или последовательностей,
> вставляемых в текст для передачи информации
> о его выводе или строении.</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Заголовок, который здесь выводится, обозначается двумя решётками, что соответствует заголовку второго уровня. Символ <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">></code> обозначает цитату. И если она пишется подряд, то превращается в единую цитату, отображается с отступом от левой границы документа и чуть более крупным шрифтом. В Markdown есть много других способов вывести ту или иную информацию заголовками, абзацами, списками и т.д. При этом разметка текста происходит крайне просто. Нужно только запомнить маркеры и использовать их. Markdown встречается повсеместно. Почти все редакторы в интернете используют Markdown. Раздел Обсуждения на Хекслете, где можно что-то спросить, о чём-то поговорить, написать комментарий, тоже поддерживает Markdown. Это очень удобно. Например, если нужно выделить или вставить код. Не все об этом знают, но я надеюсь, что с этого момента, вы будете использовать данные возможности.</p>
<p>Markdown — не единственный способ размечать текст.</p>
<p>Более распространённым является HTML. Но это не значит, что они друг друга заменяют, просто их используют в разных ситуациях.</p>
<blockquote>
<p>Следует отметить, что язык разметки неполон по Тьюрингу и обычно не считается языком программирования.</p>
</blockquote>
<p><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/Turing_completeness" rel="noopener noreferrer" target="_blank">Полнота по Тьюрингу</a> — это формальный критерий, который говорит о вычислимости, т.е. можно ли реализовать какую-либо вычислимую функцию на данном языке программирования. И если язык не полон по Тьюрингу, то, строго говоря, его нельзя называть языком программирования. Скорее всего, это так называемый <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/Domain-specific_language" rel="noopener noreferrer" target="_blank">DSL</a>, т.е. язык описания. И языки разметки как раз являются языками описания.</p>
<p>Часто новички и те, кто начинают вникать в эту тему, говорят <strong>программировать на HTML</strong>. Если вы попробуете поискать данную формулировку в интернете, то увидите, что есть статьи, где такое словосочетание встречается. На самом деле оно в корне не верно, потому что невозможно программировать на не Тьюринг полной системе. И HTML — это всего лишь способ оформлять текст, просто набор тегов, которые надо запомнить. И, естественно, с помощью HTML вы не можете описать какие-то программы.</p>
<p>Это касается и более сложных систем, которые не полны по Тьюрингу.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><h3>Язык разметки: Markdown, HTML, ...</h3>
<blockquote>Следует отметить, что язык
разметки неполон по Тьюрингу и
обычно не считается языком программирования.</blockquote>
<p>
<b>HTML</b>
</p></code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>В примере приводится код, оформленный в виде HTML. Можно заметить, что HTML оперирует таким понятием, как теги. Они бывают открывающие и закрывающие и говорят, где начинается и где заканчивается действия тега. Они обрамляются угловыми скобками.</p>
<p>В отличие от Markdown, HTML сложнее. Он требует больше действий, и поэтому крайне редко используется в тех же редакторах, где нужно описывать разметку руками. Потому что с Markdown это очень просто делать. Но HTML даёт гораздо больше возможностей для того, чтобы выражать сложные структуры. И в реальности чаще всего Markdown в итоге превращается в HTML. Существуют специальные программы, которые анализируют Markdown, транслируют его в HTML и дальше он показывается в браузере или используется где-то ещё.</p>
<h2 id="heading-2-2">Базовые элементы HTML</h2>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><!-- Комментарий -->
<hr>
<h1>Заголовок</h1> <!-- h1 может быть только один -->
<p>Параграф</p>
<blockquote>Цитата</blockquote>
<ul>
<li>Первый элемент</li>
<li>Второй элемент<li>
</ul></code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>У HTML очень много разных элементов. В примере показаны только некоторые из базовых.</p>
<ul>
<li>Например, HTML поддерживает комментарии — одиночный тег, который оформляется <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><!-- --></code> и не отображается на итоговой странице.</li>
<li>Есть одиночный тег <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><hr></code>. Он проводит горизонтальную черту.</li>
<li>Заголовок <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><h1></code>. Причём заголовок первого уровня может быть только один в документе HTML. Если вы их сделаете несколько, браузер всё равно их отобразит. Парсится и отображается любой, даже не валидный HTML, т.е. семантически у нас не может быть двух заголовков первого уровня, а синтаксически можно просто забыть закрывающий тег и всё равно всё будет работать. Эта особенность, в том числе, позволила HTML стать очень популярным, но и сделала браузеры очень сложными в части реализации работы с не валидным HTML-кодом.</li>
<li>Есть тег <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><p></code> — параграф, достаточно очевидное имя.</li>
<li><code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><blockquote></code> — это цитата.</li>
<li>Списки <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><ul></code>, которые внутри используют элементы <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><li></code>.</li>
</ul>
<h2 id="heading-2-3">Особенности</h2>
<ul>
<li>Логическая (<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">h1</code>) / Визуальная разметка (<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">b</code>)</li>
<li>Форматирование <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">html</code> не влияет на внешний вид</li>
<li>Иерархическая структура</li>
</ul>
<p>Во-первых, разметка делится на два типа: логическая и визуальная.</p>
<ul>
<li><strong>Логическая разметка</strong> (можно говорить ещё семантическая) определяет смысловую структуру. Когда мы пишем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">h1</code>, мы имеем в виду именно заголовок первого уровня, не указывая, как он будет выглядеть. Это говорит о том, чем является этот кусок текста. А то, как он выглядит, зависит от многих факторов.</li>
<li><strong>Визуальная разметка</strong> определяет то, как должен выглядеть элемент. Например, есть тег <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">b</code>. Расшифровывается как bold (англ. жирный). Он не несёт никакого семантического смысла. Это просто указание, что кусок текста внутри данного тега должен быть отображён жирным.</li>
</ul>
<p>Важный момент, который надо знать про HTML, это то, что форматирование самого HTML, т.е. как теги оформлены внутри, в какой последовательности описаны, сделаны ли между ними переводы строк, никак не влияет на то, как он будет отображён. Вы можете написать всё в одну непрерывную строчку, и всё будет отображаться в соответствие с правилами тегов в строке, но не с тем, как вы оформили сам HTML. Вы легко можете в этом убедиться, если на любой странице любого сайта откроете исходный код через контекстное меню, вы увидите, что внутри чаще всего какое-то месиво. А закроешь его, и отображается красивый сайт.</p>
<p>HTML — иерархическая структура: элементы могут входить в элементы: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><p>Раз <b>Два</b> Три</p></code>.</p>
<h2 id="heading-2-4">Интерфейс</h2>
<p>Теперь давайте рассмотрим интерфейс нашей библиотеки, которая позволит нам строить HTML. Пока мы не будем работать с иерархическим способом задания (это следующий этап), будем работать только с плоским.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">import {
make, append, toString, node,
} from 'hexlet-html-tags'
const html1 = make()
const html2 = append(html1, node('h1', 'hexlet'))
const p1 = node('p', 'hello, world')
const html3 = append(html2, p1)
// <h1>hexlet</h1>
// <p>hello, world</p>
toString(html3)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Итак, у нас есть четыре базовых функции.</p>
<ul>
<li><code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">make</code> — это конструктор, с помощью которого мы создаём html, структуру. По сути вызов <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">make</code> возвращает список, в который мы будем постепенно добавлять теги.</li>
<li>Давайте посмотрим способ добавления тега. Для этого используется функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">append</code>, которой мы передаём созданный HTML.</li>
<li>Далее используем ещё одну функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node</code>, которая создаёт ноду, элемент нашего html-документа. Она принимает на вход два параметра. Имя тега и body, т.е. контент, который будет внутри этого тега. Как видите, нам не надо никаких закрывающих тегов. При выводе они будут добавляться автоматически.</li>
<li>И <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">toString</code> — функция позволяющая распечатывать содержимое списка тегов.</li>
</ul>
<p>Из кода видно, что это неизменяемая структура, так как нам пришлось создать <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">html2</code> после вызова <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">append</code>, потому что изменения <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">html1</code> не происходит. Далее мы создаём ещё один элемент — абзац <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">p1</code>, добавляем в него текст <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">hello, world</code>, "аппендим" его и получаем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">html3</code>.</p>
<p>Если с помощью <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">toString</code> мы выведем наш <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">html</code>, то увидим вполне красивую разметку.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/js-sicp?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">1 месяц</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">СИКП на JS</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Навык фундаментального программирования</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc2MCwicHVyIjoiYmxvYl9pZCJ9fQ==--9348098e4053d798b6f34bee4ef66947540261e4/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-rafiki.png" alt="СИКП на JS" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/sequences/lessons/html/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 9</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/sequences/lessons/html/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>