--
Большинство объектно-ориентированных языков содержат в себе специальную структуру, которая позволяет создавать новые объекты и такая структура называется class.
Генерация объектов
Вспомним, как мы делали генерацию объектов в обычном стиле используя функции:
У нас была просто функция, которая внутри себя создаёт объект, как тип данных и возвращает его наружу.
Видно, что по сути это некий генератор объектов. При этом есть специальные механизмы для того, чтобы делать это встроенными средствами языка, а не самостоятельно. Кода будет больше, но этот способ обладает определёнными преимуществами. Такой способ называется – класс.
Класс
Во-первых, имя класса – это наш тип данных, который мы хотим создать и он записывается в camelCase, причём первая буква у него тоже большая, до этого момента мы так не делали.
Во-вторых, обычно принято (это не только в JS), что у вас один файл – это определение одного типа. Будет крайне странно определять несколько типов в одном файле и по этой причине нет смысла никогда экспортировать класс по имени, его экспортируют по умолчанию. То есть у вас один класс сразу представляет из себя какой-то конкретный тип, который вы импортируете к себе и получаете возможность создавать объекты данного типа.
Для классов в JS существует много возможностей, поэтому сейчас мы разберём только базовые вещи, которые повторяют уже то, что мы делали.
Синтаксис
После того, как идёт специальное имя class и название класса открываются фигурные скобки и дальше мы внутри перечисляем разные механизмы.
Внутри нашего класса определено 2 функции и делается это следующим образом: мы просто пишем имя, после этого скобки, в скобках какие-то аргументы (если они есть) и дальше идёт тело в блоке инструкций. Как видите никаких дополнительных запятых и каких-то знаков нет.
Синтаксис немножко похож на то, как создаются объекты в том смысле, что используются фигурные скобки, но внутри мы не используем ключ: значение.
Забегая немножко вперёд скажу, что здесь можно определять не только функции, но в том числе и определённые свойства, но мы не будем сейчас этого касаться.
Первая функция, которая у нас определяется в классе называется конструктор:
Мы уже знаем, что такое конструктор и зачем он нужен.
Он принимает два параметра, которые мы обычно использовали для карты этого типа: name и percent.
Внутри они записываются, как this.name = name и this.percent = percent.
Здесь мы видим нечто новое. Исходя из того, что здесь есть точка, можно сделать вывод, что this – это объект, а исходя из названия, что этот объект указывает на себя, то есть на экземпляр текущего класса (на самом деле так и есть). Мы чуть позже посмотрим, что такое this и как он работает.
Ну и кроме этого мы определяем функцию damage, которая принимает на вход здоровье и внутри делает всё то же самое, что и обычно, но единственное, процент мы уже получаем через this.
this сюда никак не передаётся, он каким-то образом здесь появляется, как и в конструкторе.
this как пара
Давайте вспомним, как работали в таком же стиле, но с парами, когда у нас был конструктор и какие-то функции:
Если мы говорим про конструктор make, то здесь конечно не пахнет никаким this, мы просто делаем пару и возвращаем её наружу. Но, если мы посмотрим все остальные функции, которые мы использовали для данного типа данных, то мы вспомним, что первым параметром всегда шёл текущий экземпляр с которым мы работаем. И в последних уроках мы начали его называть self.
Почему не this?
Во-первых, мы бы просто смешали это с тем, как это используется в классах и могла бы возникнуть путаница.
Во-вторых, this существует внутри каждой функции, поэтому так просто это делать нельзя. Об этом нам сейчас не принципиально знать, но потом мы обязательно с этим разберёмся.
Получается, что на самом деле первым параметром всегда передаётся сам объект, с которым мы работаем. Это очень важно запомнить, поскольку позже это пригодится.
this как объект
Изменим предыдущий пример с пар на объект:
Внутри make мы возвращаем объект в стиле { name: name, percent: percent }.
Обратите внимание, что при использовании функций мы внутри берём этот self и обращаемся к нему, как self.name и self.percent. И это уже очень сильно похоже на то, как происходит работа внутри класса. И на самом деле можно сказать, что это одно и тоже.
this в классе
Давайте вообразим себе, что класс устроен так и примерно так и происходит на более низком уровне.
На самом деле первым параметром неявно (обычно даже говорят не первым параметром, а нулевой параметр) в класс всегда передаётся this.
Если точнее, то this передаётся не в класс, а в конкретный объект, который представляет собой вот этот тип данных (не забываем, что класс – это про типы данных, а не про объекты).
Когда мы работаем с конкретным объектом, то передаётся this, который мы наполняем и после этого он также попадает в каждую функцию (опять же нулевым аргументом, это происходит неявно, за нас это делает движок JS) и внутри мы можем им пользоваться.
При этом когда он попадает в конструктор, то можно представить себе, что this – это пустой объект. Это не совсем так, но для начала это совершенно нормально, если мы себе это будем представлять именно так.
Поэтому мы сюда спокойно можем установить всё, что мы хотим. Во всех функциях, которые определены внутри класса мы пользуемся this так, как будто он у нас есть, потому что на самом деле это так и происходит, просто это происходит неявно. И естественно конструктор вызывается всегда первым, поэтому всё что здесь установлено будет доступно во всех остальных функциях.
Экземпляр класса
Теперь разберёмся, как работать с классами и как создать объект.
Важно отметить, что создание класса не накладывает на вас никаких обязанностей. Он может быть совершенно пустым:
Синтаксис здесь чуть-чуть новый, добавляется одно ключевое слово new и есть один маленький нюанс касающийся него.
На самом деле new в JS работает совсем не так, как в большинстве других языков программирования, то есть тут уже врывается специфика. На самом деле классы в JS, как синтаксическая конструкция, появились только с ES6 (это один из последних стандартов JS), но до этого их не было. И классы, по сути, не привнесли в язык ничего нового с точки зрения внутреннего устройства.
Класс – это просто синтаксический сахар над системой прототипов, о которой мы пока не говорим. Сейчас мы просто попытаемся понять, что означает new и как это работает.
Когда мы создаём класс у нас имя PercentCard становится занятым. С этим идентификатором мы можем что-то сделать, потому что всё является объектом первого рода.
Если мы вызовем функцию typeof на имени нашего класса, то для многих станет неожиданностью, что его тип будет function. То есть на самом деле наш класс – это функция.
Классов в реальности в JS не существует, но их добавили по той простой причине, что так проще программировать. Большинство людей привыкло именно к такому подходу и в большинстве языков существует именно понятие класс.
И на деле эта обертка делает так, что вы можете и не знать, как оно внутри устроено, хотя на самом деле для хорошего программирования на JS это знать обязательно и мы будем постепенно к этому приходить, но опять же не перегружая себя лишней информацией. Сейчас можно запомнить просто, что любой класс в конце концов создаёт функцию.
Если мы попытаемся создать инстанс класса или экземпляр класса (какой-то объект из этого класса), то мы вызываем эту функцию и тогда становится понято почему это функция, (это именно вызов функции) в которую мы передаём параметры.
Возникает вопрос. Что это за функция?
На самом деле эта функция — это наш конструктор, который здесь закомментирован:
Тогда зачем нужен new, если это просто функция, которая могла бы это сделать?
Если обратить внимание на устройство конструктора, то можно заметить, что этот this мы не возвращаем наружу, мы вообщем не делаем никаких возвратов и в том числе по этой причине нужен new. То есть это такая операция, которая делает дополнительную работу за нас.
Сейчас мы не будем говорить, как она устроена, потому что происходит несколько шагов, которые опять же связаны с прототипами. Самое главное, что в конце концов она прокидывает в конструктор специальный this, который возвращается нам наружу и поэтому получается, что внутри card оказывается тот же самый объект, с которым мы работали раньше (уже как тип данных объект в языке).
Теперь работа никак не отличается от предыдущих примеров:
Например, мы вызываем card.name и получаем имя карты.
Как мы видим внутри класса для этого ничего не нужно специально определять, потому что это просто обычный объект когда мы делаем его инстанс и поэтому this.name достаточно для того, чтобы потом снаружи получить этот name. То же самое касается процента, теперь он у нас доступен и мы можем обращаться ко всему, что внутри было установлено, как this.something. Аналогично с функцией damage ничего в плане синтаксиса не изменилось. Также не нарушается правило связанное с несуществующими ключами: мы вызываем wrongKey и получаем undefined. Ну и конечно здесь будет работать тот же самый синтаксис обращения через квадратные скобки вместо точечной нотации.
<!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:11:47 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="RQhSSqNiSiPd9a9VyyNwS3JKfpIvUNoQOtuMa30xQyaq2Zl9URznQ2u2i83HLIA8skNTOCdnJLKHOxY_LzakSA";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: Программирование, управляемое данными: Познакомиться с классом — структурой, которая позволяет создавать объекты. Рассмотреть механизм создания новых объектов с помощью 'new'.">
<link rel="canonical" href="https://ru.hexlet.io/courses/ddp/lessons/classes/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Классы">
<meta property="og:title" content="JS: Программирование, управляемое данными">
<meta property="og:description" content="Классы / JS: Программирование, управляемое данными: Познакомиться с классом — структурой, которая позволяет создавать объекты. Рассмотреть механизм создания новых объектов с помощью 'new'.">
<meta property="og:url" content="https://ru.hexlet.io/courses/ddp/lessons/classes/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="0JH1v6f6QAJEEY7Mxm5q0JtgwJSWVpFGeuf3d1Z45nU_QD6IVYTtYvJSqlTKYZqnW2ntPp5hb-THB20jBH8BGw" />
<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:11:47.527Z","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":"k2_Kctd9SpJhN-qZh6NngLBlMdpkR_YpYn81FUWRy5F8vgFFJQPn8td0zgGLrJf3cGwccGxwCIvfn69BF5Ys_w","topics":[{"id":27940,"title":"Предпоследний вопрос в тесте вызвал небольшой дискомфорт. Мы же, вроде, негде не говорили про ссылки на объекты, и в предыдущих курсах вообще понятие **\"ссылки на объект\"** не использовалось. Но чтобы правильно ответить на тест, надо знать, что это такое. ","plain_title":"Предпоследний вопрос в тесте вызвал небольшой дискомфорт. Мы же, вроде, негде не говорили про ссылки на объекты, и в предыдущих курсах вообще понятие \"ссылки на объект\" не использовалось. Но чтобы правильно ответить на тест, надо знать, что это такое. ","creator":{"public_name":"Вячеслав Быков","id":210863,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":60210,"body":"Оно было в курсе коллекций, но без акцента. В целом да, скоро я возьмусь за переделку, учту там этот момент.","topic_id":27940}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":60677,"title":"Ошибка в вопросе 9. Поправьте пожалуйста","plain_title":"Ошибка в вопросе 9. Поправьте пожалуйста ","creator":{"public_name":"Max shymko","id":415104,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":128140,"body":"Вы правы, вызов `console.log()` выводит на экран значение свойства до его изменения, а вопрос стоит не про вывод на экран, а про результат вызова. Я перефразировал этот вопрос, чтобы не было неоднозначности","topic_id":60677},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":128019,"body":"Добрый день! Скажите, пожалуйста, какой ответ в тесте вы выбираете и почему считаете, что именно он верный?","topic_id":60677},{"creator":{"public_name":"Max shymko","id":415104,"is_tutor":false},"id":128054,"body":"Если скопировать весь код из вопроса и вставить в консоль результатом console.log будет javascript, т.к. мутация объекта происходит после вызова course.name(). Как я сейчас понял, это не ошибка, а скорее подсказака, что в данный момент находится в объекте. Но меня это ввело в заблуждение, т.к. кажется, что спрашивается что выведет console.log. Я думаю можно в конце кода добавить еще один console.log: `console.log(course.name()); // ?`, и спросить что выведет последний console.log.\n","topic_id":60677}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":17440,"title":"Не получается пройти тест. Ошибка в импорте:\n```\nTest suite failed to run\n\n/usr/src/app/__tests__/test.js:1\nimport { l, length, get } from 'hexlet-pairs-data';\n^^^^^^\n \nSyntaxError: Unexpected token import\n```\n\nВ предыдущих задачах нужные зависимости были поставлены, в этой - нет. Может быть в этом ошибка?","plain_title":"Не получается пройти тест. Ошибка в импорте: ``` Test suite failed to run /usr/src/app/tests/test.js:1 import { l, length, get } from 'hexlet-pairs-data'; ^^^^^^ SyntaxError: Unexpected token import ``` В предыдущих задачах нужные зависимости были поставлены, в этой - нет. Может быть в этом ошибка? ","creator":{"public_name":"behemoth oz","id":103476,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр-C Вагин","id":6409,"is_tutor":false},"id":36813,"body":"Исправил. Но нужно нажать кнопку Сброс (ваш код потеряется, скопируйте его куда-нибудь) и все починится!","topic_id":17440},{"creator":{"public_name":"Александр-C Вагин","id":6409,"is_tutor":false},"id":36812,"body":"Здравствуйте. Исправляю\n","topic_id":17440}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":30984,"title":"Для чего нужны классы?","plain_title":"Для чего нужны классы? ","creator":{"public_name":"Алексей Королёв","id":188357,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikita Mikhaylov","id":186965,"is_tutor":true},"id":67285,"body":"Здравствуйте\n\nКлассы позволяют нам создавать «сущности», у которых будут свои наборы инструментов для взаимодействия. Например создав класс «автомобиль», можно внутри него создать функции для работы с этим автомобилем (проверка колёс, пробег и так далее). Это позволит описать всё в одном месте и в последствии наследовать в другие сущности (например сущность «Audi» будет наследоваться от автомобиля и туда добавятся новые свойства, если они нужны)","topic_id":30984}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":13078,"title":"В тесте в 10 вопросе \"неправильный\" должно быть слитно","plain_title":"В тесте в 10 вопросе \"неправильный\" должно быть слитно ","creator":{"public_name":"Vita Chistyakova","id":163107,"is_tutor":false},"comments":[{"creator":{"public_name":"Дмитрий Храпонов","id":145876,"is_tutor":false},"id":27403,"body":"Спасибо, пофиксил :)","topic_id":13078}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":28937,"title":"А если в этом задании сделать [вот так, как сделал я](https://ru.hexlet.io/code_reviews/119838)?\n\nВроде и покороче выглядит смысл ясен. Что скажете?","plain_title":"А если в этом задании сделать вот так, как сделал я (https://ru.hexlet.io/code_reviews/119838)? Вроде и покороче выглядит смысл ясен. Что скажете? ","creator":{"public_name":"Valery Kovalev","id":226793,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikita Mikhaylov","id":186965,"is_tutor":true},"id":62487,"body":"Здравствуйте\n\nОбратите внимание на то, что при изменении реализации метода `damage` придётся или выносить функцию за пределы конструктора, или добавлять ненужную сложность в сам конструктор, который по своему замыслу должен просто установить поля нашего класса","topic_id":28937}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":15385,"title":" Какое преимущество дает синтаксис доп. функций в описании класса, озвученный в уроке, перед способом записи тех же функций инстанса, указанных через обычное свойство в формате `this[someFunction] = () =>`, \nили, на конкретном примере: \n```\nexport default class SimpleCard {\n constructor(name, dmg) {\n this.name = name;\n this.damage = () => dmg;\n }\n}\n``` \n?","plain_title":" Какое преимущество дает синтаксис доп. функций в описании класса, озвученный в уроке, перед способом записи тех же функций инстанса, указанных через обычное свойство в формате this[someFunction] = () =>, или, на конкретном примере: export default class SimpleCard { constructor(name, dmg) { this.name = name; this.damage = () => dmg; } } ? ","creator":{"public_name":"","id":50614,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":32373,"body":"1. Такой способ определения будет создавать новые функции при создании нового объекта каждый раз. Это очень расточительно.\n1. Можно забыть про автокомплит\n1. Интерфейс не очевиден, нужно копаться в кишках конструктора.\n1. Невозможна генерация автоматической документации.","topic_id":15385},{"creator":{"public_name":"","id":50614,"is_tutor":false},"id":32383,"body":" Четко и содержательно. \nБольшое спасибо !","topic_id":15385}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":30820,"title":"10 вопрос теста вводит в заблуждение. В видео стиль наименования был назван `CamelCase`, в вариантах ответа указан `PascalCase`.\n","plain_title":"10 вопрос теста вводит в заблуждение. В видео стиль наименования был назван CamelCase, в вариантах ответа указан PascalCase. ","creator":{"public_name":"Geo Lashukov","id":237090,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":66913,"body":"PascalCase разновидность CamelCase, а именно UpperCamelCase","topic_id":30820}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":28494,"title":"ИМХО стоит в тесте `имя класса должно быть в camel case` написать как `CamelCase`. Ибо само имя отражает стиль именования","plain_title":"ИМХО стоит в тесте имя класса должно быть в camel case написать как CamelCase. Ибо само имя отражает стиль именования ","creator":{"public_name":"Nikolai Gagarinov","id":104929,"is_tutor":true},"comments":[{"creator":{"public_name":"Сергей К.","id":5174,"is_tutor":false},"id":61456,"body":"Николай, спасибо! Поправил.","topic_id":28494}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}},{"id":29490,"title":"Здравствуйте! Не могу понять почему нужно использовать именование отличное от __damage__, т.е. например __damagePoints__.\nhttps://ru.hexlet.io/code_reviews/124433","plain_title":"Здравствуйте! Не могу понять почему нужно использовать именование отличное от damage, т.е. например damagePoints. https://ru.hexlet.io/code_reviews/124433 ","creator":{"public_name":"Nikita Kostichev","id":179554,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр Чепайкин","id":224384,"is_tutor":true},"id":63851,"body":"**Nikita Kostichev**, Чтобы это до конца понять вам нужно пройти курс прототипы. Это очень важная тема.","topic_id":29490},{"creator":{"public_name":"Александр Чепайкин","id":224384,"is_tutor":true},"id":64261,"body":"**Nikita Kostichev**, Ну видимо автор курсов не предугадал это) Для меня тоже было бы логичнее классы изучать сразу с прототипами. Я могу вам раскрыть карты просто опасаюсь отнять у вас возможность изучить это самостоятельно) ","topic_id":29490},{"creator":{"public_name":"Александр Чепайкин","id":224384,"is_tutor":true},"id":64202,"body":"**Nikita Kostichev**, Вы изучили прототипное наследование? Суть именно в этом. \nКуда записывается свойство this.damagePoints ? Где находится функция damage() ? ","topic_id":29490},{"creator":{"public_name":"Nikita Kostichev","id":179554,"is_tutor":false},"id":64246,"body":"**Александр Чепайкин**, прототипное наследование вроде как дальше по курсу. Странно, что мне нужно его изучить, чтоб понять то, что изучаем ранее) скоро до него доберусь, спасибо)","topic_id":29490},{"creator":{"public_name":"Сергей К.","id":5174,"is_tutor":false},"id":63904,"body":"Никита, добрый день! Уточните вопрос. Как вы хотите использовать имя `damage`, и с какой ошибкой сталкиваетесь?","topic_id":29490},{"creator":{"public_name":"Nikita Kostichev","id":179554,"is_tutor":false},"id":64201,"body":"**Сергей К.**, при таком варианте:\n\n```\nexport default class SimpleCard {\n constructor(name, damage) {\n this.name = name;\n this.damage = damage;\n }\n\n damage() {\n return this.damagePoints;\n }\n}\n```\n\nТесты падают с ошибкой:\n\n```\nmake: Entering directory '/usr/src/app'\nnpm test -s\n FAIL __tests__/test.js\n CardGame\n ✕ #flow 1 (18ms)\n ✕ #flow 2 (1ms)\n\n ● CardGame › #flow 1\n\n TypeError: card.damage is not a function\n\n 14 | const card = customRandom(cards);\n 15 | const cardName = card.name;\n > 16 | const points = card.damage(health2);\n | ^\n 17 | const newHealth = health2 - points;\n 18 | \n 19 | const message = `Игрок '${name1}' применил '${cardName}'\n\n at damage (solution.js:16:25)\n at iter (solution.js:39:18)\n at run (solution.js:43:3)\n at Object.game (__tests__/test.js:10:17)\n\n ● CardGame › #flow 2\n\n TypeError: card.damage is not a function\n\n 14 | const card = customRandom(cards);\n 15 | const cardName = card.name;\n > 16 | const points = card.damage(health2);\n | ^\n 17 | const newHealth = health2 - points;\n 18 | \n 19 | const message = `Игрок '${name1}' применил '${cardName}'\n\n at damage (solution.js:16:25)\n at iter (solution.js:39:18)\n at run (solution.js:43:3)\n at Object.game (__tests__/test.js:45:17)\n\nTest Suites: 1 failed, 1 total\nTests: 2 failed, 2 total\nSnapshots: 0 total\nTime: 1.11s, estimated 2s\nRan all test suites.\nMakefile:2: recipe for target 'test' failed\nmake: Leaving directory '/usr/src/app'\nmake: *** [test] Error 1\n```\nПри использовании любого другого имени тесты проходят:\n\n```\nexport default class SimpleCard {\n constructor(name, damagePoints) {\n this.name = name;\n this.damagePoints = damagePoints;\n }\n\n damage() {\n return this.damagePoints;\n }\n}\n```\n\nЯ так понимаю проблема в использовании имени _damage_, при написании _constructor_.\nНо не понимаю в чём суть проблемы.","topic_id":29490}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Классы","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":471,"slug":"js_ddp_classes_exercise","name":null,"state":"active","kind":"exercise","language":"javascript","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## SimpleCard.js\n\nРеализуйте и экспортируйте по умолчанию класс `SimpleCard` описывающий одноимённый тип карты по аналогии с `PercentCard`.\n","prepared_readme":"## SimpleCard.js\n\nРеализуйте и экспортируйте по умолчанию класс `SimpleCard` описывающий одноимённый тип карты по аналогии с `PercentCard`.\n","has_solution":true,"entity_name":"Классы"},"units":[{"id":1396,"name":"theory","url":"/courses/ddp/lessons/classes/theory_unit"},{"id":1398,"name":"quiz","url":"/courses/ddp/lessons/classes/quiz_unit"},{"id":1397,"name":"exercise","url":"/courses/ddp/lessons/classes/exercise_unit"}],"links":[],"ordered_units":[{"id":1396,"name":"theory","url":"/courses/ddp/lessons/classes/theory_unit"},{"id":1398,"name":"quiz","url":"/courses/ddp/lessons/classes/quiz_unit"},{"id":1397,"name":"exercise","url":"/courses/ddp/lessons/classes/exercise_unit"}],"id":705,"slug":"classes","state":"approved","name":"Классы","course_order":800,"goal":"Познакомиться с классом — структурой, которая позволяет создавать объекты. Рассмотреть механизм создания новых объектов с помощью 'new'.","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"172395947","theory":"--\n\nБольшинство объектно-ориентированных языков содержат в себе специальную структуру, которая позволяет создавать новые объекты и такая структура называется **class**.\n\n## Генерация объектов\n\nВспомним, как мы делали генерацию объектов в обычном стиле используя функции:\n\n```javascript\nexport const make = (name, percent) => {\n return {\n name: name,\n damage: health =>\n Math.round(health * (percent / 100)),\n }\n}\n```\n\nУ нас была просто функция, которая внутри себя создаёт объект, как тип данных и возвращает его наружу.\n\nВидно, что по сути это некий генератор объектов. При этом есть специальные механизмы для того, чтобы делать это встроенными средствами языка, а не самостоятельно. Кода будет больше, но этот способ обладает определёнными преимуществами. Такой способ называется – класс.\n\n## Класс\n\nВо-первых, имя класса – это наш тип данных, который мы хотим создать и он записывается в camelCase, причём первая буква у него тоже большая, до этого момента мы так не делали.\n\nВо-вторых, обычно принято (это не только в JS), что у вас один файл – это определение одного типа. Будет крайне странно определять несколько типов в одном файле и по этой причине нет смысла никогда экспортировать класс по имени, его экспортируют по умолчанию. То есть у вас один класс сразу представляет из себя какой-то конкретный тип, который вы импортируете к себе и получаете возможность создавать объекты данного типа.\n\nДля классов в JS существует много возможностей, поэтому сейчас мы разберём только базовые вещи, которые повторяют уже то, что мы делали.\n\n## Синтаксис\n\n```javascript\n// CamelCase\nexport default class PercentCard {\n constructor(name, percent) {\n this.name = name\n this.percent = percent\n }\n\n damage(health) {\n return Math.round(health * (this.percent / 100))\n }\n}\n```\n\nПосле того, как идёт специальное имя class и название класса открываются фигурные скобки и дальше мы внутри перечисляем разные механизмы.\n\nВнутри нашего класса определено 2 функции и делается это следующим образом: мы просто пишем имя, после этого скобки, в скобках какие-то аргументы (если они есть) и дальше идёт тело в блоке инструкций. Как видите никаких дополнительных запятых и каких-то знаков нет.\nСинтаксис немножко похож на то, как создаются объекты в том смысле, что используются фигурные скобки, но внутри мы не используем `ключ: значение`.\n\nЗабегая немножко вперёд скажу, что здесь можно определять не только функции, но в том числе и определённые свойства, но мы не будем сейчас этого касаться.\n\nПервая функция, которая у нас определяется в классе называется **конструктор**:\n\n```javascript\nconstructor(name, percent) {\n this.name = name;\n this.percent = percent;\n}\n```\n\nМы уже знаем, что такое конструктор и зачем он нужен.\nОн принимает два параметра, которые мы обычно использовали для карты этого типа: `name` и `percent`.\nВнутри они записываются, как `this.name = name` и `this.percent = percent`.\n\nЗдесь мы видим нечто новое. Исходя из того, что здесь есть точка, можно сделать вывод, что `this` – это объект, а исходя из названия, что этот объект указывает на себя, то есть на экземпляр текущего класса (на самом деле так и есть). Мы чуть позже посмотрим, что такое `this` и как он работает.\n\nНу и кроме этого мы определяем функцию `damage`, которая принимает на вход здоровье и внутри делает всё то же самое, что и обычно, но единственное, процент мы уже получаем через `this`.\n\n>this сюда никак не передаётся, он каким-то образом здесь появляется, как и в конструкторе.\n\n### this как пара\n\nДавайте вспомним, как работали в таком же стиле, но с парами, когда у нас был конструктор и какие-то функции:\n\n```javascript\nimport { cons, car, cdr } from 'hexlet-pairs'\n\nexport const make = (name, percent) =>\n cons(name, percent)\n\nexport const getName = self => car(self)\n\nexport const damage = (self, health) =>\n Math.round(health * (cdr(self) / 100))\n\n// using\n\nconst card = make('name', 80)\ngetName(card) // name\n```\n\nЕсли мы говорим про конструктор `make`, то здесь конечно не пахнет никаким `this`, мы просто делаем пару и возвращаем её наружу. Но, если мы посмотрим все остальные функции, которые мы использовали для данного типа данных, то мы вспомним, что первым параметром всегда шёл текущий экземпляр с которым мы работаем. И в последних уроках мы начали его называть `self`.\n\nПочему не `this`?\n\nВо-первых, мы бы просто смешали это с тем, как это используется в классах и могла бы возникнуть путаница.\nВо-вторых, `this` существует внутри каждой функции, поэтому так просто это делать нельзя. Об этом нам сейчас не принципиально знать, но потом мы обязательно с этим разберёмся.\n\nПолучается, что на самом деле первым параметром всегда передаётся сам объект, с которым мы работаем. Это очень важно запомнить, поскольку позже это пригодится.\n\n### this как объект\n\nИзменим предыдущий пример с пар на объект:\n\n```javascript\nexport const make = (name, percent) => {\n return { name: name, percent: percent }\n}\n\nexport const getName = self => self.name\n\nexport const damage = (self, health) =>\n Math.round(health * (self.percent / 100))\n\n// using\n\nconst card = make('name', 80)\ngetName(card) // name\n```\n\nВнутри `make` мы возвращаем объект в стиле `{ name: name, percent: percent }`.\nОбратите внимание, что при использовании функций мы внутри берём этот `self` и обращаемся к нему, как `self.name` и `self.percent.` И это уже очень сильно похоже на то, как происходит работа внутри класса. И на самом деле можно сказать, что это одно и тоже.\n\n### this в классе\n\n```javascript\nexport default class PercentCard {\n constructor(<this>, name, percent) { // this === {}\n this.name = name;\n this.percent = percent;\n }\n\n damage(<this>, health) {\n return Math.round(health * (this.percent / 100));\n }\n}\n```\n\nДавайте вообразим себе, что класс устроен так и примерно так и происходит на более низком уровне.\nНа самом деле первым параметром неявно (обычно даже говорят не первым параметром, а нулевой параметр) в класс всегда передаётся `this`.\n\n>Если точнее, то this передаётся не в класс, а в конкретный объект, который представляет собой вот этот тип данных (не забываем, что класс – это про типы данных, а не про объекты).\n\nКогда мы работаем с конкретным объектом, то передаётся `this`, который мы наполняем и после этого он также попадает в каждую функцию (опять же нулевым аргументом, это происходит неявно, за нас это делает движок JS) и внутри мы можем им пользоваться.\n\nПри этом когда он попадает в конструктор, то можно представить себе, что `this` – это пустой объект. Это не совсем так, но для начала это совершенно нормально, если мы себе это будем представлять именно так.\n\nПоэтому мы сюда спокойно можем установить всё, что мы хотим. Во всех функциях, которые определены внутри класса мы пользуемся `this` так, как будто он у нас есть, потому что на самом деле это так и происходит, просто это происходит неявно. И естественно конструктор вызывается всегда первым, поэтому всё что здесь установлено будет доступно во всех остальных функциях.\n\n## Экземпляр класса\n\nТеперь разберёмся, как работать с классами и как создать объект.\n\n```javascript\ntypeof PercentCard // function\n\nconst card = new PercentCard('card name', 90)\n// constructor(name, percent) {\n// this.name = name;\n// this.percent = percent;\n// }\n\ncard.name // card name\ncard.percent // 90\ncard.damage(10) // 9\n\ncard.wrongKey // undefined\n\ncard['name'] // card name\n```\n\nВажно отметить, что создание класса не накладывает на вас никаких обязанностей. Он может быть совершенно пустым:\n\n```javascript\nclass Fake {\n}\n\nconst obj = new Fake()\n```\n\nСинтаксис здесь чуть-чуть новый, добавляется одно ключевое слово `new` и есть один маленький нюанс касающийся него.\n\nНа самом деле `new` в JS работает совсем не так, как в большинстве других языков программирования, то есть тут уже врывается специфика. На самом деле классы в JS, как синтаксическая конструкция, появились только с ES6 (это один из последних стандартов JS), но до этого их не было. И классы, по сути, не привнесли в язык ничего нового с точки зрения внутреннего устройства.\n\nКласс – это просто синтаксический сахар над системой прототипов, о которой мы пока не говорим. Сейчас мы просто попытаемся понять, что означает `new` и как это работает.\n\nКогда мы создаём класс у нас имя `PercentCard` становится занятым. С этим идентификатором мы можем что-то сделать, потому что всё является объектом первого рода.\nЕсли мы вызовем функцию `typeof` на имени нашего класса, то для многих станет неожиданностью, что его тип будет `function`. То есть на самом деле наш класс – это функция.\n\n>Классов в реальности в JS не существует, но их добавили по той простой причине, что так проще программировать. Большинство людей привыкло именно к такому подходу и в большинстве языков существует именно понятие **класс**.\n\nИ на деле эта обертка делает так, что вы можете и не знать, как оно внутри устроено, хотя на самом деле для хорошего программирования на JS это знать обязательно и мы будем постепенно к этому приходить, но опять же не перегружая себя лишней информацией. Сейчас можно запомнить просто, что любой класс в конце концов создаёт функцию.\n\nЕсли мы попытаемся создать инстанс класса или экземпляр класса (какой-то объект из этого класса), то мы вызываем эту функцию и тогда становится понято почему это функция, (это именно вызов функции) в которую мы передаём параметры.\n\nВозникает вопрос. Что это за функция?\n\nНа самом деле эта функция — это наш конструктор, который здесь закомментирован:\n\n```javascript\nconst card = new PercentCard('card name', 90)\n// constructor(name, percent) {\n// this.name = name;\n// this.percent = percent;\n// }\n```\n\nТогда зачем нужен `new`, если это просто функция, которая могла бы это сделать?\n\nЕсли обратить внимание на устройство конструктора, то можно заметить, что этот `this` мы не возвращаем наружу, мы вообщем не делаем никаких возвратов и в том числе по этой причине нужен `new`. То есть это такая операция, которая делает дополнительную работу за нас.\nСейчас мы не будем говорить, как она устроена, потому что происходит несколько шагов, которые опять же связаны с прототипами. Самое главное, что в конце концов она прокидывает в конструктор специальный `this`, который возвращается нам наружу и поэтому получается, что внутри `card` оказывается тот же самый объект, с которым мы работали раньше (уже как тип данных объект в языке).\n\nТеперь работа никак не отличается от предыдущих примеров:\n\n```javascript\ntypeof PercentCard // function\n\nconst card = new PercentCard('card name', 90)\n// constructor(name, percent) {\n// this.name = name;\n// this.percent = percent;\n// }\n\ncard.name // card name\ncard.percent // 90\ncard.damage(10) // 9\n\ncard.wrongKey // undefined\n\ncard['name'] // card name\n```\n\nНапример, мы вызываем `card.name` и получаем имя карты.\n\nКак мы видим внутри класса для этого ничего не нужно специально определять, потому что это просто обычный объект когда мы делаем его инстанс и поэтому `this.name` достаточно для того, чтобы потом снаружи получить этот `name`. То же самое касается процента, теперь он у нас доступен и мы можем обращаться ко всему, что внутри было установлено, как `this.something`. Аналогично с функцией `damage` ничего в плане синтаксиса не изменилось. Также не нарушается правило связанное с несуществующими ключами: мы вызываем `wrongKey` и получаем `undefined`. Ну и конечно здесь будет работать тот же самый синтаксис обращения через квадратные скобки вместо точечной нотации.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":1378,"name":"theory","url":"/courses/ddp/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":1378,"name":"theory","url":"/courses/ddp/lessons/intro/theory_unit"}],"id":698,"slug":"intro","state":"approved","name":"Введение","course_order":100,"goal":"Познакомиться с курсом и с проектом карточной игры, над которым мы будем работать в течение всего курса, по ходу знакомясь с важными концепциями программирования.","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"172391980","theory":"<figure>\n <blockquote class=\"blockquote\">\n <p>Я изобрел понятие «объектно-ориентированный», но могу заявить, что не имел в виду C++ при этом.</p>\n </blockquote>\n <figcaption class=\"blockquote-footer\">\n Alan Kay\n </figcaption>\n</figure>\n\nНа текущий момент мы уже достаточно много знаем, как строить модульные программы и теперь пришло время поговорить о том, как строить расширяемые программы. В программировании существует понятие аддитивность, и эта тема будет центральной в нашем курсе.\n**Аддитивность** – это расширение возможности вашей программы без переписывания исходного кода.\n\n## Какие темы будут разобраны?\n\n- Пользовательские типы\n- Динамическая диспетчеризация (по типу)\n- Полиморфизм\n- Передача сообщений\n- Объектно-ориентированное программирование\n\nВо-первых, это пользовательские типы (типы данных, которые мы можем создавать самостоятельно).\nМы поговорим про такое важное понятие, как диспетчеризация и конкретно о динамической диспетчеризации (по типу). Разберём понятие полиморфизма. О нём многие слышали, но очень не многие понимают, что оно по-настоящему обозначает. Эту интересную тему мы разберём в одном из последних уроков. Также мы поговорим про передачу сообщений. И, наконец, придём к пониманию объектно-ориентированного программирования: что это такое, как с ним работать и почему, например, JavaScript считается объектно-ориентированным языком.\n\n## Новый синтаксис\n\n- Свойства\n- Тип Данных: Объект\n- Классы\n\nВ этом курсе появится новый синтаксис, который включает в себя несколько понятий.\nПервое – это **свойства**, которые используются в JavaScript даже для самых базовых вещей. Мы избегали этой темы, чтобы не отвлекаться от модульности. Теперь пришло время начать с ними работать.\n\nВо-вторых, мы разберём тип данных, который называется **объект**. Это не то же самое, что объект в объектно-ориентированном программировании, но иногда названия, которые используются в языках программирования несут свой смысл, и к этому нужно будет просто привыкнуть.\n\nИ последнее, с чем мы познакомимся – это **классы**. Ниже приведён небольшой пример класса, который будет использоваться в нашей программе. Мы подробно разберем его в нашем курсе.\n\n```javascript\nexport default class PercentCard {\n constructor(name, percent) {\n this.name = name\n this.percent = percent\n }\n\n damage(health) {\n return Math.round(health * (this.percent / 100))\n }\n}\n```\n\n## Проект: Карточная игра\n\n\n\nВ этом курсе мы будем работать над своей **карточной игрой**. Она будет без графического интерфейса. Мы будем выводит процесс игры в виде текста.\n\nЭта карточная игра необычная и не такая, как все привыкли видеть, играя в обычные карты. Игра будет про волшебников, в процессе которой происходит нападение друг на друга. Каждая карта описывает атаку, урон юнита и так далее. В нашем случае она будет простая, но на ней будет хорошо видна проблематика, которую мы собираемся отрабатывать.\n\n## Процесс игры\n\nДавайте посмотрим на процесс без кода. Как будет происходить вывод нашей игры:\n\n```\nНачинаем бой!\n\nИгрок 'John' применил 'Прохладный чыонг-бонг рыка'\nпротив 'Ada' и нанес урон '3'\n\nИгрок 'Ada' применил 'Воздушный змей клеветы'\nпротив 'John' и нанес урон '1'\n\nИгрок 'John' применил 'Проказливый рубитель крови'\nпротив 'Ada' и нанес урон '2'\n\nAda был убит\n```\n\nДля карт, которые представляют собой оружие, используются названия позаимствованные из Diablo. В сети существует множество генераторов названий предметов из этой игры. Они довольно прикольные, поэтому будет здорово здесь ими воспользоваться.\n\nГлядя на процесс, очевидно, что игра рассчитана на двух игроков, которые по очереди делают ходы. Сначала один игрок наносит урон, потом второй, затем снова первый и так по кругу. Урон зависит от текущей карты. И она не всегда наносит конкретный урон. Он может зависеть от текущего здоровья (например, снимать 50% здоровья) и других характеристик.\n\nПредположим, что стартовый уровень жизни у каждого игрока 10. А тот, у кого уровень жизни дойдёт до нуля или станет отрицательным, считается убитым. В примере видно, что игрок с именем Ada был убит первым.\n\n## Разработка через тесты\n\nНаш курс будет примером разработки через так называемый Test Driven Development. Это означает, что тесты пишутся до кода.\n\n```\nДизайн кода определяется тестами.\n\nВместо того, чтобы сосредотачиваться на\nвнутреннем представлении, сначала прорабатываются\nварианты использования этого кода.\n```\n\nЗачем это нужно?\n\n **Test Driven Development** – это такая методология, которая существует уже достаточно много лет. Она входит в концепцию экстремального программирования. Её главная фишка, что мы не фокусируемся на том, как будет устроен код внутри. Гораздо важнее, как им пользоваться. Именно дизайн кода (так называемый API), интерфейс, который вы отдаёте другим для использования и является определяющим. Если вы делаете хорошую абстракцию, то внутреннее содержимое не так важно. Вы всегда можете его переписать.\n\nОчень часто бывает так (это касается не только программирования), что люди делают что-то, но пользоваться этим невозможно. Это касается всего в нашей жизни: любого производства, продукта, процесса и так далее. Test Driven Development в первую очередь, заставляет нас думать о дизайне кода, о том, как его использовать, а затем строить внутреннюю структуру.\n\n## Обучение на Хекслете\n\nПовторим аспекты, которые мы используем для того, чтобы обучение было лучше и была проработка правильных вещей.\n\n- Неизменяемость (функциональный стиль)\n- СИКП\n- Обучение через рефакторинг\n- Разработка через тесты\n- Знания, которые не устаревают\n\nМы продолжаем использовать функциональный стиль. При этом постепенно начинаем вводить изменяемость.\n\nКурс также продолжает традицию основ на СИКП.\n\nМы делаем обучение через рефакторинг, то есть мы не рассказываем сразу, как правильно что-то сделать. Чтобы понять достаточно сложные темы по-настоящему, нужно пережить опыт и самостоятельно упереться в сложности. Поэтому уроки будут построены следующим образом: мы делаем какое-то решение, которое у вас (по нашей задумке) будет вызывать вопросы «А почему так?» и будете видеть, что здесь есть какие-то недоработки. В свою очередь следующий урок будет говорить об этом подробно и уже показывать следующий шаг для решения возникших проблем, и так далее. Постепенно мы будем приходить к общему решению, которое исправляет все недостатки предыдущих. Это так называемое обучение через рефакторинг.\n\nТакже напоминаю, что в этом курсе разработка будет происходить через тесты (о чём мы уже с вами говорили).\n\nЗнания текущего курса являются универсальными. Будет совсем чуть-чуть JavaScript специфики, но в основном всё, что здесь рассказывается — это ключ к пониманию, как устроены все современные языки, и какие проблемы они решают.\n"},"id":121,"slug":"ddp","challenges_count":3,"name":"JS: Программирование, управляемое данными","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы познакомитесь с программированием, управляемым данными. Вы узнаете, как использовать данные для управления логикой программы. Вы научитесь создавать динамические приложения и работать с классами, узнаете о полиморфизме и его типах, познакомитесь с диспетчеризацией по типу и аддитивностью. Знания из этого курса помогут создавать мощные и гибкие приложения, которые легко адаптируются к изменениям данных.","kind":"additional","updated_at":"2026-01-20T11:53:13.077Z","language":"javascript","duration_cache":39000,"skills":["Делать свое ООП с типами и диспетчеризацией","Использовать инверсию зависимостей для изменения поведения кода без его переписывания","Создавать классы и использовать их в JavaScript","Применять полиморфизм для уменьшения количества условных конструкций и дублирования"],"keywords":["ООП","Динамическая диспетчеризация","Типы данных","Объекты"],"lessons_count":9,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6ODkwOSwicHVyIjoiYmxvYl9pZCJ9fQ==--8d6ae873c203221995a5f2afe08cdb3b594e095f/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/ddp/lessons/classes/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"><p>--</p>
<p>Большинство объектно-ориентированных языков содержат в себе специальную структуру, которая позволяет создавать новые объекты и такая структура называется <strong>class</strong>.</p>
<h2 id="heading-2-1">Генерация объектов</h2>
<p>Вспомним, как мы делали генерацию объектов в обычном стиле используя функции:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">export const make = (name, percent) => {
return {
name: name,
damage: health =>
Math.round(health * (percent / 100)),
}
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>У нас была просто функция, которая внутри себя создаёт объект, как тип данных и возвращает его наружу.</p>
<p>Видно, что по сути это некий генератор объектов. При этом есть специальные механизмы для того, чтобы делать это встроенными средствами языка, а не самостоятельно. Кода будет больше, но этот способ обладает определёнными преимуществами. Такой способ называется – класс.</p>
<h2 id="heading-2-2">Класс</h2>
<p>Во-первых, имя класса – это наш тип данных, который мы хотим создать и он записывается в camelCase, причём первая буква у него тоже большая, до этого момента мы так не делали.</p>
<p>Во-вторых, обычно принято (это не только в JS), что у вас один файл – это определение одного типа. Будет крайне странно определять несколько типов в одном файле и по этой причине нет смысла никогда экспортировать класс по имени, его экспортируют по умолчанию. То есть у вас один класс сразу представляет из себя какой-то конкретный тип, который вы импортируете к себе и получаете возможность создавать объекты данного типа.</p>
<p>Для классов в JS существует много возможностей, поэтому сейчас мы разберём только базовые вещи, которые повторяют уже то, что мы делали.</p>
<h2 id="heading-2-3">Синтаксис</h2>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">// CamelCase
export default class PercentCard {
constructor(name, percent) {
this.name = name
this.percent = percent
}
damage(health) {
return Math.round(health * (this.percent / 100))
}
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>После того, как идёт специальное имя class и название класса открываются фигурные скобки и дальше мы внутри перечисляем разные механизмы.</p>
<p>Внутри нашего класса определено 2 функции и делается это следующим образом: мы просто пишем имя, после этого скобки, в скобках какие-то аргументы (если они есть) и дальше идёт тело в блоке инструкций. Как видите никаких дополнительных запятых и каких-то знаков нет.
Синтаксис немножко похож на то, как создаются объекты в том смысле, что используются фигурные скобки, но внутри мы не используем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ключ: значение</code>.</p>
<p>Забегая немножко вперёд скажу, что здесь можно определять не только функции, но в том числе и определённые свойства, но мы не будем сейчас этого касаться.</p>
<p>Первая функция, которая у нас определяется в классе называется <strong>конструктор</strong>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">constructor(name, percent) {
this.name = name;
this.percent = percent;
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Мы уже знаем, что такое конструктор и зачем он нужен.
Он принимает два параметра, которые мы обычно использовали для карты этого типа: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">name</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">percent</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">this.name = name</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">this.percent = percent</code>.</p>
<p>Здесь мы видим нечто новое. Исходя из того, что здесь есть точка, можно сделать вывод, что <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">this</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">this</code> и как он работает.</p>
<p>Ну и кроме этого мы определяем функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">damage</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">this</code>.</p>
<blockquote>
<p>this сюда никак не передаётся, он каким-то образом здесь появляется, как и в конструкторе.</p>
</blockquote>
<h3 id="heading-3-4">this как пара</h3>
<p>Давайте вспомним, как работали в таком же стиле, но с парами, когда у нас был конструктор и какие-то функции:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">import { cons, car, cdr } from 'hexlet-pairs'
export const make = (name, percent) =>
cons(name, percent)
export const getName = self => car(self)
export const damage = (self, health) =>
Math.round(health * (cdr(self) / 100))
// using
const card = make('name', 80)
getName(card) // name</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Если мы говорим про конструктор <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">make</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">this</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">self</code>.</p>
<p>Почему не <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">this</code>?</p>
<p>Во-первых, мы бы просто смешали это с тем, как это используется в классах и могла бы возникнуть путаница.
Во-вторых, <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">this</code> существует внутри каждой функции, поэтому так просто это делать нельзя. Об этом нам сейчас не принципиально знать, но потом мы обязательно с этим разберёмся.</p>
<p>Получается, что на самом деле первым параметром всегда передаётся сам объект, с которым мы работаем. Это очень важно запомнить, поскольку позже это пригодится.</p>
<h3 id="heading-3-5">this как объект</h3>
<p>Изменим предыдущий пример с пар на объект:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">export const make = (name, percent) => {
return { name: name, percent: percent }
}
export const getName = self => self.name
export const damage = (self, health) =>
Math.round(health * (self.percent / 100))
// using
const card = make('name', 80)
getName(card) // name</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Внутри <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">make</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">{ name: name, percent: percent }</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">self</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">self.name</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">self.percent.</code> И это уже очень сильно похоже на то, как происходит работа внутри класса. И на самом деле можно сказать, что это одно и тоже.</p>
<h3 id="heading-3-6">this в классе</h3>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">export default class PercentCard {
constructor(<this>, name, percent) { // this === {}
this.name = name;
this.percent = percent;
}
damage(<this>, health) {
return Math.round(health * (this.percent / 100));
}
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Давайте вообразим себе, что класс устроен так и примерно так и происходит на более низком уровне.
На самом деле первым параметром неявно (обычно даже говорят не первым параметром, а нулевой параметр) в класс всегда передаётся <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">this</code>.</p>
<blockquote>
<p>Если точнее, то this передаётся не в класс, а в конкретный объект, который представляет собой вот этот тип данных (не забываем, что класс – это про типы данных, а не про объекты).</p>
</blockquote>
<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">this</code>, который мы наполняем и после этого он также попадает в каждую функцию (опять же нулевым аргументом, это происходит неявно, за нас это делает движок JS) и внутри мы можем им пользоваться.</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">this</code> – это пустой объект. Это не совсем так, но для начала это совершенно нормально, если мы себе это будем представлять именно так.</p>
<p>Поэтому мы сюда спокойно можем установить всё, что мы хотим. Во всех функциях, которые определены внутри класса мы пользуемся <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">this</code> так, как будто он у нас есть, потому что на самом деле это так и происходит, просто это происходит неявно. И естественно конструктор вызывается всегда первым, поэтому всё что здесь установлено будет доступно во всех остальных функциях.</p>
<h2 id="heading-2-7">Экземпляр класса</h2>
<p>Теперь разберёмся, как работать с классами и как создать объект.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">typeof PercentCard // function
const card = new PercentCard('card name', 90)
// constructor(name, percent) {
// this.name = name;
// this.percent = percent;
// }
card.name // card name
card.percent // 90
card.damage(10) // 9
card.wrongKey // undefined
card['name'] // card name</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Важно отметить, что создание класса не накладывает на вас никаких обязанностей. Он может быть совершенно пустым:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">class Fake {
}
const obj = new Fake()</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Синтаксис здесь чуть-чуть новый, добавляется одно ключевое слово <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">new</code> и есть один маленький нюанс касающийся него.</p>
<p>На самом деле <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">new</code> в JS работает совсем не так, как в большинстве других языков программирования, то есть тут уже врывается специфика. На самом деле классы в JS, как синтаксическая конструкция, появились только с ES6 (это один из последних стандартов JS), но до этого их не было. И классы, по сути, не привнесли в язык ничего нового с точки зрения внутреннего устройства.</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">new</code> и как это работает.</p>
<p>Когда мы создаём класс у нас имя <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">PercentCard</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">typeof</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">function</code>. То есть на самом деле наш класс – это функция.</p>
<blockquote>
<p>Классов в реальности в JS не существует, но их добавили по той простой причине, что так проще программировать. Большинство людей привыкло именно к такому подходу и в большинстве языков существует именно понятие <strong>класс</strong>.</p>
</blockquote>
<p>И на деле эта обертка делает так, что вы можете и не знать, как оно внутри устроено, хотя на самом деле для хорошего программирования на JS это знать обязательно и мы будем постепенно к этому приходить, но опять же не перегружая себя лишней информацией. Сейчас можно запомнить просто, что любой класс в конце концов создаёт функцию.</p>
<p>Если мы попытаемся создать инстанс класса или экземпляр класса (какой-то объект из этого класса), то мы вызываем эту функцию и тогда становится понято почему это функция, (это именно вызов функции) в которую мы передаём параметры.</p>
<p>Возникает вопрос. Что это за функция?</p>
<p>На самом деле эта функция — это наш конструктор, который здесь закомментирован:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">const card = new PercentCard('card name', 90)
// constructor(name, percent) {
// this.name = name;
// this.percent = percent;
// }</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Тогда зачем нужен <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">new</code>, если это просто функция, которая могла бы это сделать?</p>
<p>Если обратить внимание на устройство конструктора, то можно заметить, что этот <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">this</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">new</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">this</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">card</code> оказывается тот же самый объект, с которым мы работали раньше (уже как тип данных объект в языке).</p>
<p>Теперь работа никак не отличается от предыдущих примеров:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">typeof PercentCard // function
const card = new PercentCard('card name', 90)
// constructor(name, percent) {
// this.name = name;
// this.percent = percent;
// }
card.name // card name
card.percent // 90
card.damage(10) // 9
card.wrongKey // undefined
card['name'] // card name</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Например, мы вызываем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">card.name</code> и получаем имя карты.</p>
<p>Как мы видим внутри класса для этого ничего не нужно специально определять, потому что это просто обычный объект когда мы делаем его инстанс и поэтому <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">this.name</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">name</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">this.something</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">damage</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">wrongKey</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">undefined</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/ddp/lessons/classes/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 9</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/ddp/lessons/classes/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>