<!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 17:17:05 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="BrQ_r0_yMMrbkPLk4EtWJzEOHMOz6GRezRH0LFJz6WHpZfSYvYydqm3T1nzsRKZQ8Qcxabvfmvxw8W54AHQODw";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: Составные данные: Знакомимся с понятием барьеров абстракции, чтобы лучше понять цель разделения на уровни абстракции и повышения уровня абстракции">
<link rel="canonical" href="https://ru.hexlet.io/courses/js-compound-data/lessons/barriers-of-abstraction/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Барьеры абстракции">
<meta property="og:title" content="JS: Составные данные">
<meta property="og:description" content="Барьеры абстракции / JS: Составные данные: Знакомимся с понятием барьеров абстракции, чтобы лучше понять цель разделения на уровни абстракции и повышения уровня абстракции">
<meta property="og:url" content="https://ru.hexlet.io/courses/js-compound-data/lessons/barriers-of-abstraction/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="3Ud1pa3aGl8Oct6qveGm_-mZY6qM9zXbnqyA2oXfu-kylr6SX6S3P7gx-jKx7laIKZBOAITAy3kjTBqO19hchw" />
<script src="/vite/assets/inertia-INZxX8jp.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-nkZBEvfU.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-6pOtQ3OW.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-26T17:17:05.675Z","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":"2bXosuBIm1hwZeU7fhY2tqNnCVnfEUN5dlU_mIwNkPA2ZCOFEjY2OMYmwaNyGcbBY24k89cmvdvLtaXM3gp3ng","topics":[{"id":42602,"title":"Здравствуйте! Решил повторить пройденный материал и вот какой вопрос: не протекает ли абстракция в [решении ](https://ru.hexlet.io/code_reviews/181740)учителя?","plain_title":"Здравствуйте! Решил повторить пройденный материал и вот какой вопрос: не протекает ли абстракция в решении (https://ru.hexlet.io/code_reviews/181740)учителя? ","creator":{"public_name":"Александр Щербаков","id":253191,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей К.","id":5174,"is_tutor":false},"id":92640,"body":"Александр, приветствую! А напишите, пожалуйста, подробнее.","topic_id":42602},{"creator":{"public_name":"Сергей К.","id":5174,"is_tutor":false},"id":93198,"body":"В решении учителя в конструктор передаётся 3 параметра: точка, длина и высота прямоугольника. Этих параметров достаточно для построения прямоугольника. И когда нам нужны координаты точки, то мы получаем их с помощью селекторов `getX` и `getY`.","topic_id":42602},{"creator":{"public_name":"Александр Щербаков","id":253191,"is_tutor":false},"id":92879,"body":"В решении учителя пары используются для создания интерфейса прямоугольника, т.е. получается мы перепрыгиваем через уровень, минуя точки, сразу к парам.\n\nВозможно, тут подразумевается, что точки и прямоугольники это абстракции одного уровня. Собственно, судя по коду, так и есть.","topic_id":42602}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":29814,"title":"Здравствуйте!\nЗастрял на реализации функции containsTheOrigin\n[ревью ](https://ru.hexlet.io/code_reviews/127903)\nподскажите что делаю не так?","plain_title":"Здравствуйте! Застрял на реализации функции containsTheOrigin ревью (https://ru.hexlet.io/code_reviews/127903) подскажите что делаю не так? ","creator":{"public_name":"Bator Eshizhamsoev","id":232615,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей К.","id":5174,"is_tutor":false},"id":64677,"body":"Поздравляю!","topic_id":29814},{"creator":{"public_name":"Bator Eshizhamsoev","id":232615,"is_tutor":false},"id":64634,"body":"решил!","topic_id":29814}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":4963,"title":"А зачем возня с высчитыванием каждой точки и квадрантами? Тогда уж и прямоугольник надо было собирать не коротенько из входных параметров, а по честному высчитывать точки и группировать парами. \nПо мне проще показалось взять исходную точку и от нее плясать:\n1) Проверка , что она лежит во II квадранте if (x>=0 || y<=0) return false\n2) Проверить, что левая нижняя в III (вычтя из y высоту) и правая верхняя в I (добавив к х ширину)\n```\n if (x>=0 || y<=0) return false\n if (x+w>0 && y-h<0) {\n return true\n } else {\n return false\n }\n``` \n\nИ уж точно не надо проверять четвертую точку. \n\n\nНу и хочется попенять на формулировку задания. Не сказано, в каком виде нужен прямоугольник (я выбрал, как проще - оказалось, что в офф. решении также, а можно было подробнее по точкам группировать). Не сказано, что он без наклона (мое скудное воображение не подсказало мне такого возможного развития вопроса, но нескольких людей из каментов этим озадачились). \nБольшой простор для творчества - это хорошо, но не всегда. Проходил я один курс на Степике, замечательный по материалу, но в заданиях была куча мутных формулировок, ох как народ колбасило...\n","plain_title":"А зачем возня с высчитыванием каждой точки и квадрантами? Тогда уж и прямоугольник надо было собирать не коротенько из входных параметров, а по честному высчитывать точки и группировать парами. По мне проще показалось взять исходную точку и от нее плясать: 1) Проверка , что она лежит во II квадранте if (x>=0 || y<=0) return false 2) Проверить, что левая нижняя в III (вычтя из y высоту) и правая верхняя в I (добавив к х ширину) if (x>=0 || y<=0) return false if (x+w>0 && y-h<0) { return true } else { return false } И уж точно не надо проверять четвертую точку. Ну и хочется попенять на формулировку задания. Не сказано, в каком виде нужен прямоугольник (я выбрал, как проще - оказалось, что в офф. решении также, а можно было подробнее по точкам группировать). Не сказано, что он без наклона (мое скудное воображение не подсказало мне такого возможного развития вопроса, но нескольких людей из каментов этим озадачились). Большой простор для творчества - это хорошо, но не всегда. Проходил я один курс на Степике, замечательный по материалу, но в заданиях была куча мутных формулировок, ох как народ колбасило... ","creator":{"public_name":"Andy","id":117329,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":8704,"body":"Мы не удаляем чужие топики если они не нарушают правил сайта) Вы можете сделать это самостоятельно, нажав на маленький синий треугольник справа вверху.","topic_id":4963},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":8702,"body":"Кажется это дубль","topic_id":4963},{"creator":{"public_name":"Andy","id":117329,"is_tutor":false},"id":8703,"body":"Да, удалите пожалуйста. Я его почему-то не видел, и даже жаловался в чате на это.","topic_id":4963}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":17295,"title":"Присоединяюсь к вопросу. Что в данной ситуации подразумевается под прямоугольником? Вычисленные точки прямоугольника? В моём понимании, прямоугольник, это геометрическая фигура из 4 замкнутых линий на двухмерной поверхности. В данной ситуации это просто код. Что он должен возвращать после получения начальной точки и длины с шириной? Ответ \"прямоугольник\" ни коим образом не вносит ясность в этот вопрос при данных условиях.\n\nГлядя на ошибки, которые мне выдают тесты, могу сказать, что без чёткого понимания ответа на этот вопрос бесполезно пытаться реализовать любую часть кода - всё \"держится\" на первой функции `makeRectangle`. По крайней мере, я так понимаю. Я, на данный момент, не могу понять как вообще реализовывать всё остальное, потому что, как мне кажется, весь остальной код строится на базе этого самого начала - с определения этой функции, которая на вход принимает первые три параметра. \n`const makeRectangle = (p, b, h) => ...` а вот дальше я где-то в ступоре. Пробовал свои мысли, с последующим извлечением из этих мыслей данных, которые подавались на вход, но каждый раз ошибки.\n\nЧего-то я глобально не понимаю, а может и всего сразу.","plain_title":"Присоединяюсь к вопросу. Что в данной ситуации подразумевается под прямоугольником? Вычисленные точки прямоугольника? В моём понимании, прямоугольник, это геометрическая фигура из 4 замкнутых линий на двухмерной поверхности. В данной ситуации это просто код. Что он должен возвращать после получения начальной точки и длины с шириной? Ответ \"прямоугольник\" ни коим образом не вносит ясность в этот вопрос при данных условиях. Глядя на ошибки, которые мне выдают тесты, могу сказать, что без чёткого понимания ответа на этот вопрос бесполезно пытаться реализовать любую часть кода - всё \"держится\" на первой функции makeRectangle. По крайней мере, я так понимаю. Я, на данный момент, не могу понять как вообще реализовывать всё остальное, потому что, как мне кажется, весь остальной код строится на базе этого самого начала - с определения этой функции, которая на вход принимает первые три параметра. const makeRectangle = (p, b, h) => ... а вот дальше я где-то в ступоре. Пробовал свои мысли, с последующим извлечением из этих мыслей данных, которые подавались на вход, но каждый раз ошибки. Чего-то я глобально не понимаю, а может и всего сразу. ","creator":{"public_name":"Рабигаль Сагитов","id":175750,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":36951,"body":"Функции проходили в этом уроке: https://ru.hexlet.io/courses/introduction_to_programming/lessons/functions/theory_unit\n\nА также более подробно изучали в [курсе JS: Фукнции](https://ru.hexlet.io/courses/js-functions)","topic_id":17295},{"creator":{"public_name":"Рабигаль Сагитов","id":175750,"is_tutor":false},"id":36503,"body":"1. Я правильно понимаю, что вспомогательные функции тоже надо реализовать? \n\n2. А что тогда по части селекторов? С ними я не до конца понял - это уже реализованный функционал и нужен для помощи в построении всего этого или их тоже надо реализовывать, прежде чем пользоваться? В импорте их нет, потому мне не совсем понятно.\n\n3. И как пользоваться \"квадрантом\", который есть в импорте? По какому принципу он работает? Без понимания этого не понятно как его использовать.\n\np/s: у меня складывается впечатление, что моего собственного абстрактного мышления пока не хватает для понимания этой абстракции.","topic_id":17295},{"creator":{"public_name":"Рабигаль Сагитов","id":175750,"is_tutor":false},"id":36512,"body":"Посмотрел в решение и снова увидел то, что никак не укладывается в моё понимание. В прошлом уроке решил сам, но тот же вопрос у меня возникал - по части того, откуда на входе взялся `rectangle`, когда перед этим он нигде не определялся. В прошлом уроке я вроде бы понял суть ответа, но тут я ещё больше запутался по этой причине. Конструкция конструктора мне ясна - сам такой вариант пробовал. А вот логику `getStartPoint = (rectangle) =>...` в край не понимаю. Данные у нас поступили в конструктор. Чтобы их оттуда взять, надо к ним обратиться. То есть, точка, ширина и высота у нас туда поступили и оттуда их можно взять. А мы каким-то образом достаём их из какого-то левого `rectangle`, который в первый раз появляется там, где мы из него что-то хотим достать. Для меня это оторванные друг от друга вещи, которые никак не связаны друг с другом.\nКак работает всё остальное, мне понятно. В том числе и квадрант. Много раз писал что-то рядом с правильным решением - логику улавливал. Но именно вот этот момент с входящим не обозначенным нигде ранее `rectangle` выбивает из колеи капитально и не даёт, пока что, мне возможности понять как решать такие задачи. Он остаётся для меня камнем преткновения уже не первое упражнение подряд. Не могу уловить суть между ним и между такими вот призрачными входящими данными и тем, что в самом начале даётся как входящие данные. При условии что вокруг этого всё и крутится в задаче, как-то это всё уже печалить меня начинает. Если принять это как аксиому без понимания сути, то когда-нибудь это сыграет злую шутку. Потому хотелось бы всё же как-то понять.\n\nКогда мы создаём селекторы, мы же берём для них данные из определения функции `makeRectangle`. Так откуда берётся `rectangle`? Если убрать похожесть, то вместо `rectangle` можно поставить `water`(я проверил) - программа будет работать также.\n\nДля меня это выглядит как если бы мы говорили о Молдавии, описали бы что вина таких-то сортов производятся в Молдавии, а следом, продолжая беседу о Молдавии, вместо Молдавии начали бы вставлять Грецию, продолжая при этом описывать вина Молдавии, но никак не связанные с Грецией, которая при этом ещё и выглядит опечаткой в тексте о Молдавии. Примерно так это мне видится.","topic_id":17295},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":37087,"body":"И да, разные люди по разному представляют прямоугольники, кто-то делает это с помощью четырех точек.","topic_id":17295},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":37085,"body":"Если говорить про \"что такое прямоугольник\", то правильного ответа нет, в этом суть абстракции. Чем вы его сделаете тем он и будет. Исходные данные такие, \n\n1. У вас есть пары для представления составных данных\n2. Прямоугольник, по очевидным причинам, относится к составным данным. Его нельзя представить одним значением. В задаче даже сказано какими значениями его надо представить.\n3. Соответственно задача сводится к тому чтобы упаковать исходные данные описывающие прямоугольник в единственный известный вам способ создания составных данных, а именно в пару (возможно в пару пар и так далее, здесь нет никаких ограничений).\n\nТо что вы тратите время на понимание этого вопроса абсолютно НОРМАЛЬНО. Так задумано. Это и есть процесс обучения. Невозможно начать мыслить абстракциями действуя по инструкции. Нужно думать и думать много.\n\nТо что я здесь пишу, на 100% повторяет теорию урока https://ru.hexlet.io/courses/compound_data/lessons/modeling/theory_unit\n\n> А то в последнее время очень много донимаю своими вопросами, как мне кажется, довольно глупыми.\n\nВопросы это именно то что требуется. Невозможно представить себе обучение без вопросов, вы же учились в университете и знаете что это такое.\n\n> В конце концов я заплатил за этот курс не с той целью, что-бы несколько часов, с прочтением многих комментариев пытаться догнать, а чего же здесь мне необходимо сделать? \n\nВы недооцениваете процесс обучения, и что понять \"а что такое прямоугольник\" и есть главная фишка задания, в этом вся суть, а не в коде.\n\n","topic_id":17295},{"creator":{"public_name":"Рабигаль Сагитов","id":175750,"is_tutor":false},"id":36858,"body":"> Далее, возможно, у вас не полное понимание того, как работает определение функции и формальные параметры.\n\nМожете указать на урок, в котором есть описание этих моментов? Бегло бробежался по некоторым - то ли не нашёл, то ли не понял, что это он.","topic_id":17295},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":36494,"body":"> Присоединяюсь к вопросу. Что в данной ситуации подразумевается под прямоугольником? Вычисленные точки прямоугольника? В моём понимании, прямоугольник, это геометрическая фигура из 4 замкнутых линий на двухмерной поверхности. В данной ситуации это просто код. Что он должен возвращать после получения начальной точки и длины с шириной? Ответ \"прямоугольник\" ни коим образом не вносит ясность в этот вопрос при данных условиях.\n\nПосмотрите, пожлауйста, этот топик: https://ru.hexlet.io/topics/8093 Здесь (как и в ряде других топиков) уже обсуждался этот вопрос.","topic_id":17295},{"creator":{"public_name":"Anatol Meshalkin","id":167557,"is_tutor":false},"id":37079,"body":"А все равно непонятно, чего надо делать? Зачастую самое сложное в ваших заданиях это понять вводную. Абстракция преподнесения задания намного абстрактнее дальнейшей ее реализации. После прочтения этого топика, и того что открывается по ссылке, так и не понял, что подразумевается под прямоугольником? Чем являются длина и ширина: это сегмент от начальной точки? или это две другие точки на координатах? Понятие прямоугольника как-то слишком абстрактно.","topic_id":17295},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":36741,"body":"> Я правильно понимаю, что вспомогательные функции тоже надо реализовать?\n\nДа, об этом прямо написано в задании. Мы делаем библиотеку по работе с прямоугольниками, хотелось бы, чтобы в ней были такие функции как вычисление площади, периметра и проч.\n\n> А что тогда по части селекторов?\n\nИх тоже надо реализовывать. Ведь надо же как-то доставать информацию про прямоугольник. Иначе мы не сможем ничего о нём узнать.\n\n> И как пользоваться \"квадрантом\", который есть в импорте?\n\nДля начала посмотрите документацию по функциям из библиотеки по работе с точками - она находится в файле `docs/hexlet-points.md`.\n\n> По какому принципу он работает? Без понимания этого не понятно как его использовать.\n\nМы эту функцию реализовывали в практике второго шага, вернитесь туда и посмотрите.","topic_id":17295},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":36743,"body":"> Данные у нас поступили в конструктор. Чтобы их оттуда взять, надо к ним обратиться.\n\nДа. Для этого и существуют функции-селекторы - это просто \"ключики\" к нашей структуре. Позволяют достать оттуда ровно то, что туда положили. Мы же данные ложим в структуру не только для хранения, но ещё и для последующего использования. И любую функцию, которая позволяет извлечь эти данные мы называет \"селектор\". Вот и всё. Не надо более усложнять.\n\n> А мы каким-то образом достаём их из какого-то левого rectangle, который в первый раз появляется там, где мы из него что-то хотим достать.\n\nПро формальные параметры я написал вам в сообщении выше.\n\nДалее, это не какой-то \"левый\" rectangle. Когда происходит **вызов** функции, ей на вход в качестве аргумента передаётся реальный `rectangle` (сконструированный с помощью конструктора). Но это происходит потом, после того как мы опредлелили фунцию - по-моему, вы путаете **определение** функции и **вызов** функции. Это разные вещи.","topic_id":17295},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":36740,"body":"> у меня складывается впечатление, что моего собственного абстрактного мышления пока не хватает для понимания этой абстракции.\n\n1. Мне кажется, что одна из проблем, это то, что вы думаете слишком **много лишнего**, то есть \"накручиваете\" и излишне усложняете, чем есть на самом деле.\n2. Далее, возможно, у вас не полное понимание того, как работает определение функции и формальные параметры.\n","topic_id":17295},{"creator":{"public_name":"Anatol Meshalkin","id":167557,"is_tutor":false},"id":37097,"body":"Ок! Ваш посыл понятен.","topic_id":17295},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":36742,"body":"> но тот же вопрос у меня возникал - по части того, откуда на входе взялся `rectangle`, когда перед этим он нигде не определялся.\n\n`rectangle` - это имя формального параметра, задаваемое при определении функции. Это имя задаётся произвольным образом (с учётом некоторых ограничений языка), т.е. оно могло бы звучить и по другому. Но так как мы определяем функции, которые **ожидают** получить на вход именно прямоугольник, то поэтому и даём их формальному праметру соответствующее имя. Глупо было бы называть как-то по другому, ведь правда?","topic_id":17295},{"creator":{"public_name":"Рабигаль Сагитов","id":175750,"is_tutor":false},"id":37080,"body":"Насчёт вводной поддерживаю. Прежде чем решить следующее задание, пришлось задать очень много вопросов по этому поводу - и то, окончательное понимание того, что от меня хотели и что и каким образом поступало на вход пришло только в процессе реализации. Думаю, имеет место быть такой феномен, как то, что создатель того или иного задания, формулы, проекта и прочих вариаций чего угодно понимает, что имеется в виду, потому что у него уже сформировано это в голове - вся картина изнутри. Передать суть и идею этого во вне - задача не простая, но, в данном случае, первостепенная. Для создающего задачу есть такие моменты в задаче, которые для него очевидны - ибо они у него в голове уже есть. Думаю, это одна из причин недостающих подробностей в виде текста для понимания задачи теми, кто находится по другую сторону экрана - по другую сторону мозга, создающего задачу - у нас в мозгу это может стать \"по-умолчанию\" лишь после описания этих умолчаний)))\n\nПри этом это касается не конкретных методов реализаций, а чаще, банально, того же вида входных данных и прочих \"мелочей\". \n\nКак пример. В следующей задаче мне долго пришлось переписываться, прежде чем понять, что на самом деле поступает на вход и в каком виде, чтобы я мог правильно выстроить алгоритм. Банально, как я понял, нужно было высчитать количество множителей (степень), не учитывая неделимый на множитель остаток. В классической арифметике надо было бы добавить ту же 7ку, например, ко множителям. Но нас интересовали только 2 и 3. В задаче не было указано, что нам можно пренебречь подобным остатком. Думаю, это входит в понимание абстракции, в данной ситуации. Но в реальном коде по той же бухгалтерии или в реальной жизни этот пренебрегаемый остаток - вполне ощутимая материальная часть нашего мира. Потому, по-умолчанию, ею редко пренебрегают и, не обозначив этот момент в задании, не получается выстроить верный алгоритм для решения поставленной задачи, ибо по-умолчанию это вопрос, который надо тоже решить.\n\nТаково моё видение, появившееся к середине курса и сформированное благодаря тому, что наконец-то понял, что не один в этом (думал, что \"сам дурак\", а почитав обсуждение, начал понимать, что проблема шире).\n\nЯ ещё не попал в чёрные списки своими мыслями?))))) А то в последнее время очень много донимаю своими вопросами, как мне кажется, довольно глупыми. Но понимать задачу так стало легче - после ответов на все мои вопросы по заданию.","topic_id":17295},{"creator":{"public_name":"Anatol Meshalkin","id":167557,"is_tutor":false},"id":37084,"body":"И если этот вопрос обсуждался уже в нескольких топиках, но по прежнему остается актуальным, то может на него надо обратить внимание создателям курса. В конце концов я заплатил за этот курс не с той целью, что-бы несколько часов, с прочтением многих комментариев пытаться догнать, а чего же здесь мне необходимо сделать? Всецело согласен с вашим утверждением: **Передать суть и идею - задача не простая, но, в данном случае, первостепенная.**","topic_id":17295}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":29721,"title":"Решил задание, но остался вопрос.В решении учителя был код. Что он означает?\n\n`// removed`","plain_title":"Решил задание, но остался вопрос.В решении учителя был код. Что он означает? // removed ","creator":{"public_name":"","id":174919,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей К.","id":5174,"is_tutor":false},"id":64417,"body":"Михаил, посмотрите ответ в соседнем [топике](https://ru.hexlet.io/topics/29681).","topic_id":29721}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":3562,"title":"В решении автора, по моему мнению, могут получиться отрицательные площади и периметры, если прямогольник наколонен на осях координат так, что `getLeftTop` окажется ниже `getRightBottom`. Может стоит обернуть вычисление сторон в `Math.abs()`, чтобы получать абсолютные значения? И тесты заодно расширить. Как вы думаете?","plain_title":"В решении автора, по моему мнению, могут получиться отрицательные площади и периметры, если прямогольник наколонен на осях координат так, что getLeftTop окажется ниже getRightBottom. Может стоит обернуть вычисление сторон в Math.abs(), чтобы получать абсолютные значения? И тесты заодно расширить. Как вы думаете? ","creator":{"public_name":"Anton Shvab","id":46270,"is_tutor":false},"comments":[{"creator":{"public_name":"Anton Shvab","id":46270,"is_tutor":false},"id":5916,"body":"Разумно. ","topic_id":3562},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":5910,"body":"Немного поправил практику. Но в целом, для простоты, в этом задании подразумевается что прямугольник появляется на канвасе без вращений (как и во всех библиотеках подобных).","topic_id":3562}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":8541,"title":"Здравствуйте, есть пару вопросов по заданию:\n1) что за selectors ? в условии что то не наблюдал :)\n2) не могу понять почему то моя фун. не работает \n```\nexport const makeRectangle = (rectangle) => cons(p, width, height);\nconst rectangle = makeRectangle(p, 4, 5);\nconsole.log(pairToString(rectangle));\n```\nпо тесту вижу что не правильно конструктор работает\n```\nconsole.log rectangles.js:17\n ((0, 1), 4)\n```\nможет нужно создовать конструктор вот так:\n```\ncons(width, height);\n```\n```\ncons(p, cons);\n```\n\n","plain_title":"Здравствуйте, есть пару вопросов по заданию: 1) что за selectors ? в условии что то не наблюдал :) 2) не могу понять почему то моя фун. не работает export const makeRectangle = (rectangle) => cons(p, width, height); const rectangle = makeRectangle(p, 4, 5); console.log(pairToString(rectangle)); по тесту вижу что не правильно конструктор работает console.log rectangles.js:17 ((0, 1), 4) может нужно создовать конструктор вот так: cons(width, height); cons(p, cons); ","creator":{"public_name":"Roman Vinogradov","id":132101,"is_tutor":false},"comments":[{"creator":{"public_name":"","id":299,"is_tutor":false},"id":16857,"body":"> export const square = (a, b) => (getY(cdr(rectangle)) * getX(cdr(rectangle)));\n\nФункция square должна принимать на вход прямоугольник и возвращать его площадь. У вас же функция принимает два аргумента и ничего с ними не делает. В теле функции вообще ересь какая-то.\n\nАналогично с функцией perimeter. ","topic_id":8541},{"creator":{"public_name":"","id":299,"is_tutor":false},"id":16804,"body":"2. описание функции cons есть в файле hexlet-pairs.md.","topic_id":8541},{"creator":{"public_name":"","id":299,"is_tutor":false},"id":16864,"body":"Вам надо разобраться в том, что такое функция. Здесь хорошо объясняется эта тема, посмотрите: [https://ru.hexlet.io/courses/introduction_to_programming/lessons/functions/theory_unit](https://ru.hexlet.io/courses/introduction_to_programming/lessons/functions/theory_unit)\n\n> export const square = rectangle => (sideW * sideH);\n\nЛучше не стало. Функция square должна принимать на вход прямоугольник, извлекать из него значения широты/высоты, перемножать их и возвращать результат.\n\n> quadrant проверяет квадрант точки\n\nquadrant **находит** квадрант точки.\n \n```quadrant(rectangle) !== null``` - квадрант какой точки ищется в этом коде?\n","topic_id":8541},{"creator":{"public_name":"Roman Vinogradov","id":132101,"is_tutor":false},"id":16855,"body":"Помогите, не получается никак пройти boole проверку в последней фун вот код;","topic_id":8541},{"creator":{"public_name":"Roman Vinogradov","id":132101,"is_tutor":false},"id":16859,"body":"quadrant проверяет квадрант точки","topic_id":8541},{"creator":{"public_name":"","id":299,"is_tutor":false},"id":16856,"body":"> if (quadrant(rectangle) !== null ) {\n\nА что делает функция quadrant?","topic_id":8541},{"creator":{"public_name":"Roman Vinogradov","id":132101,"is_tutor":false},"id":16860,"body":"Отредоктировал код\n```\nexport const makeRectangle = (p, width, height) => cons(p, (cons(height, width)));\n\nexport const startPoint = rectangle => car(rectangle);\n\nexport const width = rectangle => getY(cdr(rectangle));\nexport const height = rectangle => getX(cdr(rectangle));\n\nconst sideW = 5;\nconst sideH = 4;\n\nexport const square = rectangle => (sideW * sideH);\nexport const perimeter = rectangle => (2 * (sideW + sideH));\n\nexport const containsTheOrigin\n```\nКак проверить что центр координат находится внутри не получается(","topic_id":8541},{"creator":{"public_name":"","id":299,"is_tutor":false},"id":16805,"body":"> cons(p, cons);\n\ncons это функция, при вызове в нее всегда должны передаваться параметры (если они есть, если нет то должны быть просто скобки)","topic_id":8541},{"creator":{"public_name":"","id":299,"is_tutor":false},"id":16865,"body":"Мне кажется перед тем как проходить этот курс, вам лучше пройти весь курс Рахима [https://ru.hexlet.io/courses/introduction_to_programming](https://ru.hexlet.io/courses/introduction_to_programming)","topic_id":8541},{"creator":{"public_name":"","id":299,"is_tutor":false},"id":16806,"body":"в общем, в данном случае селекторы это функции startPoint, width и height. их назначение извлекать нужные значения из объекта типа \"прямоугольник\"","topic_id":8541},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":16817,"body":"Добрый день!\n\n> что за selectors ? в условии что то не наблюдал :)\n\nУточните свой вопрос, пожалуйста. Вам неясен смысл термина \"селектор\"?\n\n> не могу понять почему то моя фун. не работает\n\nа что по этому поводу вам говорит вывод тестов?\n\n> по тесту вижу что не правильно конструктор работает\n\nКакой тест имеется в виду? Тот, что написали вы, или от системы тестирования? Если речь про ваш тест, то какой конкретный результат вы ожидали от него? По какому критерию определили, что конструктор работает неправильно? Если второй вариант, то см. коммент выше.","topic_id":8541}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":31415,"title":"Не могу понять как так получилось, что аргумент равен 0.\n[Ссылка на ревью](https://ru.hexlet.io/code_reviews/143470#)","plain_title":"Не могу понять как так получилось, что аргумент равен 0. [ссылка на ревью](https://ru.hexlet.io/code_reviews/143470#) ","creator":{"public_name":"fumufu86","id":61956,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikita Mikhaylov","id":186965,"is_tutor":true},"id":68351,"body":"Здравствуйте\n\nВижу, что вы смогли разобраться с ошибкой. У вас есть ещё вопросы по данному заданию?","topic_id":31415},{"creator":{"public_name":"Nikita Mikhaylov","id":186965,"is_tutor":true},"id":68454,"body":"> Почему началось вычисление значения константы b, если первый запрос на неё тремя строчками ниже?\n\nОбратите внимание на синтаксис, который вы применили. Так как он получился в рамках данной задачи не совсем корректный, то привёл к достаточно интересным вычислениям. \n\n const a = rectangle = getQuadrant(getStartPoint(rectangle));\n\nЗдесь вы использовали два знака равно, что равносильно такой записи:\n\n const a = getQuadrant(getStartPoint(rectangle));\n const rectangle = getQuadrant(getStartPoint(rectangle));\n\nПосле этого и пошло самое интересное, ведь вы передаёте уже `rectangle` в функцию `getStartPoint`, в которую уже попадает не нужная нам конструкция, а просто результат вызова `getQuadrant(getStartPoint(rectangle));`","topic_id":31415},{"creator":{"public_name":"fumufu86","id":61956,"is_tutor":false},"id":68398,"body":"Да. Хотелось бы до конца понять, как именно ошибка приводила к этому результату. В 25:53 строке происходит вызов getStartPoint с аргументом (makePoint(0, 1), 5, 4).\n\n\n\n1. Что происходило дальше?\n2. Почему началось вычисление значения константы b, если первый запрос на неё тремя строчками ниже?\n3. Почему вместо (makePoint(0, 1), 5, 4), в getStartPoint передался 0.\n4. Как 0, который был передан в getStartPoint, связан с 17:56(вторая строка в функции getQuadrant)? В функцию попадает значение, которое указано в этом месте.\n\n","topic_id":31415}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":28819,"title":"Подскажите, почему в коде ошибка, из-за переменной start? Вроде по логике должно работать...\n\n`//removed`\n\n**Осмыслил, решил. Вот ревью: https://ru.hexlet.io/code_reviews/118951**","plain_title":"Подскажите, почему в коде ошибка, из-за переменной start? Вроде по логике должно работать... //removed Осмыслил, решил. Вот ревью: https://ru.hexlet.io/code_reviews/118951 ","creator":{"public_name":"Михаил М","id":230054,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikita Mikhaylov","id":186965,"is_tutor":true},"id":62188,"body":"Здравствуйте\n\nХорошо, что вам удалось разобраться. По вашему ревью: постарайтесь отделять схожие функции дополнительным переносом строки. Посмотрите, как это сделано в решении учителя. Так в будущем вам будет проще читать свой код","topic_id":28819}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}},{"id":24202,"title":"никак не могу понять, почему если \n```\n// removed\n```\n то\n```\n// removed\n```\nвернет width ?\n\nесли мы пишем car в самом начале, то обращаемся к левому элементу, т.е. point.\n\nзаранее спасибо за разъяснение.\n","plain_title":"никак не могу понять, почему если // removed то // removed вернет width ? если мы пишем car в самом начале, то обращаемся к левому элементу, т.е. point. заранее спасибо за разъяснение. ","creator":{"public_name":"Сергей Герц","id":117897,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей Герц","id":117897,"is_tutor":false},"id":51668,"body":"очень похоже на правду, спасибо, Антон)","topic_id":24202},{"creator":{"public_name":"","id":199183,"is_tutor":false},"id":51639,"body":"Я могу ошибаться но попробую. Так происходит из-за порядка выполнения функций https://ru.hexlet.io/courses/js-functions/lessons/execution/theory_unit Сначала выполнится `cdr(rectangle)` а затем этот результат будет передан как аргумент для `car`. Подождем ответа более опытных людей)","topic_id":24202},{"creator":{"public_name":"Сергей К.","id":5174,"is_tutor":false},"id":51687,"body":"В JavaScript считается языком с аппликативным порядком вычисления. Это значит, что аргументы будут вычисленны до того, как попадут внутрь функции.","topic_id":24202}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Барьеры абстракции","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":439,"slug":"js_compound_data_abstractions_exercise","name":null,"state":"active","kind":"exercise","language":"javascript","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## rectangles.js\n\nРеализуйте абстракцию (набор функций) для работы с прямоугольником, стороны которого всегда параллельны осям. Прямоугольник может располагаться в любом месте координатной плоскости.\n\nПри такой постановке задачи достаточно знать только три параметра для однозначного задания прямоугольника на плоскости: координаты левой-верхней точки, ширину и высоту. Зная их, мы всегда можем построить прямоугольник одним единственным способом.\n\n |\n 4 | точка ширина\n | *-------------\n 3 | | |\n | | | высота\n 2 | | |\n | --------------\n 1 |\n |\n ------|---------------------------\n 0 | 1 2 3 4 5 6 7\n |\n |\n |\n\nОсновной интерфейс:\n\n* `makeRectangle()` (конструктор) - создаёт прямоугольник. Принимает параметры: левую-верхнюю точку, ширину и высоту.\n* Селекторы `getStartPoint()`, `getWidth()` и `getHeight()`\n\nВспомогательные функции для выполнения расчетов:\n\n* `area()` - возвращает площадь прямоугольника (`a * b`).\n* `perimeter()` - возвращает периметр прямоугольника (`2 * (a + b)`).\n* `containsTheOrigin()` - проверяет, принадлежит ли центр координат прямоугольнику (не лежит на границе прямоугольника, а находится внутри). Чтобы в этом убедиться, достаточно проверить, что все вершины прямоугольника лежат в разных квадрантах (их можно вычислить в момент проверки).\n\nТак как это интерфейсные функции, то они должны быть экспортированы. Если этого не сделать, система модулей js не даст ими воспользоваться.\n\n```javascript\n// Создание прямоугольника:\n// p - левая верхняя точка\n// 5 - ширина\n// 4 - высота\n//\n// p 5\n// -----------\n// | |\n// | | 4\n// | |\n// -----------\n\nconst p = makePoint(0, 1)\nconst rectangle = makeRectangle(p, 5, 4)\n\n// Вычисление площади прямоугольника\narea(rectangle) // 20;\n\nperimeter(rectangle) // 18\ncontainsTheOrigin(rectangle) // false\n\nconst rectangle02 = makeRectangle(makePoint(-4, 3), 5, 4)\ncontainsTheOrigin(rectangle02) // true\n\ncontainsTheOrigin(makeRectangle(makePoint(-4, 4), 5, 2)) // false\ncontainsTheOrigin(makeRectangle(makePoint(-4, 3), 2, 8)) // false\n```\n\n### Подсказки\n\n[Схема расположения квадрантов на плоскости](https://wiki5.ru/wiki/Quadrant_(plane_geometry))\n","prepared_readme":"## rectangles.js\n\nРеализуйте абстракцию (набор функций) для работы с прямоугольником, стороны которого всегда параллельны осям. Прямоугольник может располагаться в любом месте координатной плоскости.\n\nПри такой постановке задачи достаточно знать только три параметра для однозначного задания прямоугольника на плоскости: координаты левой-верхней точки, ширину и высоту. Зная их, мы всегда можем построить прямоугольник одним единственным способом.\n\n |\n 4 | точка ширина\n | *-------------\n 3 | | |\n | | | высота\n 2 | | |\n | --------------\n 1 |\n |\n ------|---------------------------\n 0 | 1 2 3 4 5 6 7\n |\n |\n |\n\nОсновной интерфейс:\n\n* `makeRectangle()` (конструктор) - создаёт прямоугольник. Принимает параметры: левую-верхнюю точку, ширину и высоту.\n* Селекторы `getStartPoint()`, `getWidth()` и `getHeight()`\n\nВспомогательные функции для выполнения расчетов:\n\n* `area()` - возвращает площадь прямоугольника (`a * b`).\n* `perimeter()` - возвращает периметр прямоугольника (`2 * (a + b)`).\n* `containsTheOrigin()` - проверяет, принадлежит ли центр координат прямоугольнику (не лежит на границе прямоугольника, а находится внутри). Чтобы в этом убедиться, достаточно проверить, что все вершины прямоугольника лежат в разных квадрантах (их можно вычислить в момент проверки).\n\nТак как это интерфейсные функции, то они должны быть экспортированы. Если этого не сделать, система модулей js не даст ими воспользоваться.\n\n```javascript\n// Создание прямоугольника:\n// p - левая верхняя точка\n// 5 - ширина\n// 4 - высота\n//\n// p 5\n// -----------\n// | |\n// | | 4\n// | |\n// -----------\n\nconst p = makePoint(0, 1)\nconst rectangle = makeRectangle(p, 5, 4)\n\n// Вычисление площади прямоугольника\narea(rectangle) // 20;\n\nperimeter(rectangle) // 18\ncontainsTheOrigin(rectangle) // false\n\nconst rectangle02 = makeRectangle(makePoint(-4, 3), 5, 4)\ncontainsTheOrigin(rectangle02) // true\n\ncontainsTheOrigin(makeRectangle(makePoint(-4, 4), 5, 2)) // false\ncontainsTheOrigin(makeRectangle(makePoint(-4, 3), 2, 8)) // false\n```\n\n### Подсказки\n\n[Схема расположения квадрантов на плоскости](https://wiki5.ru/wiki/Quadrant_(plane_geometry))\n","has_solution":true,"entity_name":"Барьеры абстракции"},"units":[{"id":1326,"name":"theory","url":"/courses/js-compound-data/lessons/barriers-of-abstraction/theory_unit"},{"id":1328,"name":"quiz","url":"/courses/js-compound-data/lessons/barriers-of-abstraction/quiz_unit"},{"id":1327,"name":"exercise","url":"/courses/js-compound-data/lessons/barriers-of-abstraction/exercise_unit"}],"links":[],"ordered_units":[{"id":1326,"name":"theory","url":"/courses/js-compound-data/lessons/barriers-of-abstraction/theory_unit"},{"id":1328,"name":"quiz","url":"/courses/js-compound-data/lessons/barriers-of-abstraction/quiz_unit"},{"id":1327,"name":"exercise","url":"/courses/js-compound-data/lessons/barriers-of-abstraction/exercise_unit"}],"id":677,"slug":"barriers-of-abstraction","state":"approved","name":"Барьеры абстракции","course_order":40,"goal":"Знакомимся с понятием барьеров абстракции, чтобы лучше понять цель разделения на уровни абстракции и повышения уровня абстракции","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"165146989","theory":"## Конспект урока\n\nВо время разработки нашей библиотеки для работы с графическими примитивами мы двигались от более простого и более низкого уровня (создания простых примитивов, например, точки) к более высокому, то есть повышали так называемый уровень абстракции. В этом нам помогали **барьеры абстракции**.\n\nИдея абстракции данных состоит в том, чтобы определить для каждого типа объектов данных набор базовых операций, через которые будут выражаться все действия с объектами этого типа. Если представить эту идею графически, то мы можем увидеть следующую картину:\n\n\n\nНа самом верхнем уровне находятся программы, которые используют наши графические примитивы и делают это на максимально высоком уровне. Далее мы определяем графические примитивы уже в терминах функций для доступа к более низкому уровню. Например, у нас есть функции `distance` и `makeSegment`, которые работают с отрезками и обращаются к точкам. А точки, в свою очередь, построены на парах. При этом любой части программы на любом уровне все равно, как устроены пары, от этого ничего не должно поменяться. При этом, конечно, абстракция может \"протекать\". Это означает, что более высоким уровням приходится напрямую обращаться к более низким, минуя расположенные между ними уровни. Например, программа пытается пользоваться точками как парами. В таких случаях принято считать, что абстракция не очень хороша, и это приводит к проблемам в дальнейшем при модификации программы.\n\n**Барьеры абстракции** в современной литературе часто именуют другим термином — **принцип одного уровня абстракции**. Это означает, что, работая в одной предметной области на определенном срезе, оперируют объектами только этого среза, избегая объектов, к нему не относящихся.\n\nИтак, какие же преимущества нам дает такой подход, когда мы строим все независимыми слоями или ярусами? Во-первых, нам проще рассуждать о программе, потому что на том уровне абстракции, на котором мы работаем, мы оперируем небольшим ограниченным набором сущностей, которые к тому же соответствуют одному уровню мышления о них. Во-вторых, нам проще комбинировать разные части программы, склеивая их через определенные нами интерфейсы для получения нового более сложного поведения. И, наконец, нам гораздо проще поддерживать и изменять наши программы, потому что код, отделенный барьером абстракции на определенном уровне, не зависит от реализации более низких уровней. Это позволяет в любой момент переписывать отдельные уровни, например, для большей производительности.\n\nКогда мы говорили о данных, мы говорили о том, что они реализуются некоторым интерфейсом, набором конструкторов и селекторов. Но, строго говоря, этого определения недостаточно, потому что структура данных реализуется не любым набором конструкторов и селекторов, они должны быть связаны между собой определенным образом. Формально можно сказать так: для любых `x` и `y`, если `p` есть точка `makePoint(x, y)`, `(getX(p), getY(p))` является точкой `(x, y)`. Такое определение оказывается крайне простым, если просто подумать о нем логически: положенное в конструктор должно быть получено селекторами. Если это правило выполняется, то можно сказать, что мы имеем некоторые данные, с которыми мы можем работать, и они будут вести себя предсказуемым образом. По сути, у нас есть правило (может быть даже не одно), которое описывает связи между данными.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":1318,"name":"theory","url":"/courses/js-compound-data/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":1318,"name":"theory","url":"/courses/js-compound-data/lessons/intro/theory_unit"}],"id":674,"slug":"intro","state":"approved","name":"Введение","course_order":10,"goal":"Знакомимся с целями и задачами курса","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"165146990","theory":"<figure>\n <blockquote class=\"blockquote\">\n <p>Абстракция — один из главных способов борьбы со сложностью реального мира.</p>\n </blockquote>\n <figcaption class=\"blockquote-footer\">\n Стив Макконнелл - <cite title=\"Source Title\">Совершенный код</cite>\n </figcaption>\n</figure>\n\nДо сих пор мы работали только с примитивными типами данных, такими как строки и числа. В этом курсе произойдет переход на новый уровень, и большая часть работы сосредоточится вокруг **составных данных**.\n\nВы могли подумать, что на этот раз мы будем осваивать массивы. И если бы мы не были Хекслетом, то все было бы именно так. Но у нас немного другие планы на ближайшие курсы. Есть вещи важнее массивов, и с них мы и начнем. К массивам же обратимся позже.\n\nНачиная с этого курса, мы начнем погружаться в **парадигму декларативного программирования**, без которой в JavaScript никуда. К сожалению, массивы при стандартном использовании плохо сочетаются с этими концепциями, а также позволяют срезать углы там, где не надо. Это сводит на нет весь эффект от обучения. Поэтому основой для составных данных станет так называемая **пара** — структура данных, некогда популярная во многих языках программирования. В отличие от массивов, ее нельзя изменять. В любой ситуации потребуется создать новую пару на основе предыдущей, и обойти этот механизм невозможно. Подобное ограничение оставляет только один способ работы — функциональный.\n\n*Как вы увидите позже, точно так же можно и зачастую нужно работать с массивами. К тому моменту, когда мы начнем их использовать, вы уже будете готовы к такому способу работы.*\n\nОдна из самых важных тем в программировании — **абстракция**. Чем больше кодовая база, тем больше абстракций используется, либо создается в ней. Значительная часть времени разработчика тратится на моделирование предметной области и реализации ее в коде, а также в ее дальнейшей поддержке и развитии. Как правило, этому вопросу совсем не уделяют времени, но именно от умения моделировать зависит качество вашего кода, насколько просто будет работать с ним, понимать и модифицировать его.\n\nПредставьте, что вам необходимо автоматизировать работу отдела продаж (создать CRM). С чего вы начнете? А начать стоит с [онтологии](https://ru.wikipedia.org/wiki/%D0%9E%D0%BD%D1%82%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)).\n\nВ следующих курсах вы увидите, как, благодаря некоторым особым свойствам, пара может стать основой для создания более сложных структур данных, таких как списки, множества и даже деревья. Другими словами, мы заочно познакомимся с разными структурами данных и поработаем с ними.\n\nНе забывайте, что эти курсы направлены на развитие ваших навыков кодирования, и перед тем, как мы познакомимся с платформами (фреймворками, библиотеками), нам нужно разобраться с самим JavaScript.\n\n---\n\n## Конспект урока\n\nПо названию этого курса можно подумать, что мы будем говорить о таких типах данных в JavaScript, как массив или объект. Это совсем не так. В этом курсе мы используем JavaScript как язык, на котором пишем код, но сам курс относится в целом к программированию. Мы изучим такое понятие, как абстракция данных. Это общая концепция никак не связана конкретно с JavaScript, поскольку это крайне важный механизм абсолютно во всех языках программирования. Более того, в отличие от курса [Основы программирования](https://ru.hexlet.io/courses/programming-basics) мы вообще не введем ни одной новой синтаксической конструкции. В этом и есть наше отличие. Мы учим вас мыслить, писать код и программировать, что сильно отличается от того, что существует сейчас на рынке.\n\nИтак, давайте начнем.\n\n## Что такое составные данные?\n\n**Составные данные** используются в следующих случаях. Когда мы пишем программы,\nто чаще всего пытаемся моделировать достаточно сложные процессы и явления,\nпротекающие часто в реальной жизни. Для этого мы можем использовать составные\nвычислительные объекты, которые включают в себя несколько различных частей.\nЭто позволяет лучше моделировать все явления реальной среды, того, с чем мы\nвзаимодействуем. И язык программирования должен предоставлять возможность создавать механизмы для создания так называемых составных данных, т.е. из разных кусков собирать отдельный вычислительный объект.\n\n## Зачем это нужно?\n\nПри работе со сложными вычислительными объектами благодаря составным данным,\nкак я уже говорил, мы можем поднимать так называемый понятийный уровень. Т.е. работать на более высоком уровне абстракции и создавать нашу программу, и производить вычисления в терминах наших вычислительных объектов. Давайте рассмотрим это на конкретном примере, который известен всем со школы — это точки на плоскости.\n\nУ нас есть координатная плоскость, и мы можем строить на ней точки. При этом любая точка обладает двумя характеристиками — это ее координаты `x` и `y`, т.е. абсцисса и ордината. В данном примере я использую то, что уже известно — простые числовые значения, и описываю две точки. Одна из них `x1/y1`, вторая `x2/y2`. После этого, вычисляю точку посередине.\n\n```javascript\nconst x1 = 3\nconst y1 = 5\n\nconst x2 = -2\nconst y2 = 10\n\n// Точка посередине\nconst x3 = middleX(x1, x2)\nconst y3 = middleY(y1, y2)\n```\n\nМы считаем, что мы строим отрезок между двумя этими точками и находим точку, которая лежит посередине. То, как это работает, не имеет значения. Главное, что вы можете себе это представить. Мы оперируем понятием *точка*. При этом понимаем, что она состоит из двух простых типов, т.е. двух чисел. По сути появляется такая вещь, как пара. И, в общем-то, нам удобно оперировать такими терминами.\n\nНо при написании кода, если у нас не существует понятия составные данные, то, как видите, нам приходится производить вычисления независимо. Т.е. сначала вычислить `х3`, после этого нам надо вычислить `y3`. Это происходит потому, что мы не оперируем понятием точка. У нас нет такого составного вычислительного объекта. И все, что мы можем в наших функциях, это принимать простые параметры и возвращать точно такие же простые параметры. Получается, что одно действие технически разбивается на два. Согласитесь, это крайне неудобно. Особенно, когда ваша программа становится достаточно большой и начинает оперировать большим количеством сложных объектов.\n\nТочки, кстати говоря — это еще достаточно простые объекты, которые включают в себя буквально 2 параметра. Представьте, что будет, когда мы начнем использовать более сложные объекты: хотя бы про фигуры, например, квадрат. Квадрат включает в себя 4 точки — это уже 8 параметров, потому что в каждой точке 2 параметра. Работать на таком уровне абстракции, используя только примитивные типы и примитивные данные, мы просто бы не смогли. Наше мышление очень сильно бы этому сопротивлялось. Код получался бы очень громоздкий и неинтуитивный.\n\nВ идеале нам бы хотелось работать так, как показано в этой строчке:\n\n```javascript\n// Compound Data\nconst middlePoint = middle(point1, point2)\n```\n\nМы отдаем в функцию две точки (`point1` и `point2`), и нам возвращается точка посередине. Как она устроена и что там внутри — это отдельный вопрос. Главное, что мы оперируем этим понятием, как единым целым. Возможность строить составные объекты данных позволяет нам использовать технику, которая называется абстракция данных.\n\n**Абстракция данных** — это метод отделения частей программы, которые имеют дело\nс представлением объектов данных, от тех частей, где эти объекты используются.\n\nПри использовании точки в предыдущем примере это обозначает, что нас не очень волнует, как внутри устроена точка. Мы можем просто оперировать только этим понятием. Соответственно, определять то, как непосредственно внутри она выглядит, мы можем совершенно в другом месте. Это делает код модульным и дает огромные возможности по его дальнейшей модификации и поддержке. Например, мы можем изменить представление данных на более эффективное, более удобное в данный момент и не переписывать всю программу. Нужно будет переписать только ту часть, где именно происходит определение того, как данные структурированы внутри.\n\nАбстракция данных приводит нас к такому понятию, как **барьеры абстракции**, когда мы можем строить многоуровневые слои, позволяющие изолировать разные части и разные уровни системы друг от друга. Об этом отдельный будет урок, где мы подробно поговорим, как работает абстракция данных, и как строятся барьеры абстракции.\n\nКроме этого в любом языке программирования нужен некий клей, который позволит из простых данных строить более сложные. И как мы увидим в дальнейшем, для этого даже не нужны специальные операции. Строить составные объекты можно используя только возможности функций, что еще больше стирает разницу между функциями и данными. Это можно было уже заметить, потому что функции определяются точно так же, как данные. Функции являются полноправными данными, так называемыми объектами первого рода. Этому будет посвящен целый отдельный урок.\n\n## Графические примитивы\n\nНа протяжении всего курса мы будем строить графические примитивы и небольшую библиотеку для работы с примитивами на плоскости. Начнем с точек, поработаем с кругами, научимся делать различные отрезки и фигуры.\n\n## Рациональные числа\n\nКроме этого попробуем создать простую библиотеку для работы с рациональными числами. Это числа, у которых есть числитель и знаменатель. Определение, конечно, не строго математическое, но вы на интуитивном уровне все с ними знакомы. Рациональное число — достаточно простой, но составной объект, который включает в себя пару чисел.\n\n## Преимущества Hexlet\n\n- Осмысление\n- СИКП\n- Не используем существующие структуры данных\n- Функции высшего порядка\n- Неизменяемость (Функциональный стиль)\n\nПо опыту предыдущих курсов очень часто у людей возникают вопросы: \"А почему именно так?\", \"Как мне это поможет в практике?\" и \"Я хочу быстрее начать писать продакшн-код во фреймворках\". Да, мы не просто учим синтаксису. Мы хотим изменить ваше мышление, хотим сделать его правильным, чтобы у вас развилось критическое мышление, чтобы вы понимали причинно-следственные связи и, в первую очередь, мыслили не синтаксисом языка, а смыслом того, что вы делаете с точки зрения построения абстракций. Это позволит писать вам модульные программы, которые легко читать, поддерживать, развивать, и они будут выполнять то, что нам нужно. Это наша основная идея, то, что хочет дать и несет Хекслет. Возможно, вы уже заметили это по каким-то вещам, которые мы рассказываем или делаем.\n\nНаш курс во многом построены на так называемом СИКПе — курсе, который велся и ведется до сих пор в большом количестве университетов мира. Расшифровывается, как структура и интерпретация компьютерных программ. Курс был придуман в Массачусетском технологическом институте (MIT). Это университет номер один в мире IT-технологий, который выпускал и выпускает лучших специалистов в этой области. СИКП — достаточно большой, сложный, но крайне важный курс. По нему выпущена книга, которая уже десятки лет считается книгой номер один среди обучающей литературы по программированию. В любом случае, всем ее рекомендуем. При этом здесь мы ее активно используем, потому что она дала нам много материала и пищи для размышлений. Книга была выпущена в 1985 году, а сам курс велся еще раньше (с начала 80-х). Но, как вы увидите в дальнейшем, несмотря на то, что меняются технологии, меняются фреймворки, меняется все вокруг (JavaScript меняется особенно сильно), базовые вещи, так же как во многом и математика, в общем-то не меняется и не поменяется. Не гонитесь за новым модным, изучайте то, на чем все это базируется.\n\nКурс не использует существующие структуры данных. Я сказал об этом в самом начале. Здесь не будет ничего про массивы, объекты или какие-то другие возможные способы комбинирования данных. Это отвлечет нас от основной идеи. От понимания сути вопроса. Наши функции в этом курсе приобретут совершенно новый оттенок и заиграют новыми красками. Мы познакомимся с функциями высшего порядка и увидим, что функции — это точно такие же данные, которые можно передавать в другие функции, возвращать из функций и делать с ними совершенные чудеса.\n\nВ курсе мы будем работать с неизменямыми данными, использовать функциональный стиль программирования. Этот достаточно важный аспект позволяет нам сосредоточиться только на самом вопросе, который разбирается в курсе, и оставить вопрос изменения состояния и связанных с этим проблем в стороне. В Основах программирования мы уже немного говорили о том, какие проблемы привносят переменные и возможность изменять состояние. В дальнейшем будет отдельный большой курс, посвященный работе с состояниями и всеми особенностями, которые привносят возможность использовать переменные.\n"},"id":114,"slug":"js-compound-data","challenges_count":3,"name":"JS: Составные данные","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы изучите идею составных данных. Вы узнаете больше о концепциях создания сложных типов данных из простых и о парадигме декларативного программирования. В итоге научитесь создавать абстракции и изолировать разные части программы. Знания из этого курса помогают программистам моделировать необходимую предметную область, писать более читаемый и модульный код.","kind":"additional","updated_at":"2026-01-20T11:50:53.368Z","language":"javascript","duration_cache":29880,"skills":["Создавать код, который легко читать и понимать что он делает","Научиться создавать удобные абстракции и скрывать внутреннюю реализацию данных","Определять границу между слоями приложения так, чтобы поддерживать высокий уровень модульности (независимости разных частей) кода"],"keywords":["моделирование данных","барьеры абстракции","замыкание"],"lessons_count":7,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6ODIzNCwicHVyIjoiYmxvYl9pZCJ9fQ==--d7865c47fdac3be18bfc41cd41e2b544aa491efb/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/js-compound-data/lessons/barriers-of-abstraction/theory_unit","version":"0b0c6d4ebbd40fd58630a0dd89cc25544ccdf24e","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"><h2 id="heading-2-1">Конспект урока</h2>
<p>Во время разработки нашей библиотеки для работы с графическими примитивами мы двигались от более простого и более низкого уровня (создания простых примитивов, например, точки) к более высокому, то есть повышали так называемый уровень абстракции. В этом нам помогали <strong>барьеры абстракции</strong>.</p>
<p>Идея абстракции данных состоит в том, чтобы определить для каждого типа объектов данных набор базовых операций, через которые будут выражаться все действия с объектами этого типа. Если представить эту идею графически, то мы можем увидеть следующую картину:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6ODI0NiwicHVyIjoiYmxvYl9pZCJ9fQ==--f423081376e685f1153c2dfaa935965d164f226e/barriers.png" alt="Барьеры абстракции" loading="lazy"/></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">distance</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">makeSegment</code>, которые работают с отрезками и обращаются к точкам. А точки, в свою очередь, построены на парах. При этом любой части программы на любом уровне все равно, как устроены пары, от этого ничего не должно поменяться. При этом, конечно, абстракция может "протекать". Это означает, что более высоким уровням приходится напрямую обращаться к более низким, минуя расположенные между ними уровни. Например, программа пытается пользоваться точками как парами. В таких случаях принято считать, что абстракция не очень хороша, и это приводит к проблемам в дальнейшем при модификации программы.</p>
<p><strong>Барьеры абстракции</strong> в современной литературе часто именуют другим термином — <strong>принцип одного уровня абстракции</strong>. Это означает, что, работая в одной предметной области на определенном срезе, оперируют объектами только этого среза, избегая объектов, к нему не относящихся.</p>
<p>Итак, какие же преимущества нам дает такой подход, когда мы строим все независимыми слоями или ярусами? Во-первых, нам проще рассуждать о программе, потому что на том уровне абстракции, на котором мы работаем, мы оперируем небольшим ограниченным набором сущностей, которые к тому же соответствуют одному уровню мышления о них. Во-вторых, нам проще комбинировать разные части программы, склеивая их через определенные нами интерфейсы для получения нового более сложного поведения. И, наконец, нам гораздо проще поддерживать и изменять наши программы, потому что код, отделенный барьером абстракции на определенном уровне, не зависит от реализации более низких уровней. Это позволяет в любой момент переписывать отдельные уровни, например, для большей производительности.</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">x</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">y</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">p</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">makePoint(x, y)</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">(getX(p), getY(p))</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">(x, y)</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/js-compound-data/lessons/barriers-of-abstraction/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 / 7</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/js-compound-data/lessons/barriers-of-abstraction/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-CdBlNCiQ.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-nkZBEvfU.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>