[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "content": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": "Architecture-Design.md",
    "content": "- [Определения](#определения)\n  - [Архитектурный стиль](#архитектурный-стиль)\n  - [Архитектурный паттерн](#архитектурный-паттерн)\n  - [Паттерн проектирования](#паттерн-проектирования)\n  - [Принцип проектирования](#принцип-проектирования)\n- [Архитектурные стили](#архитектурные-стили)\n  - [Монолитная архитектура](#монолитная-архитектура)\n  - [Микросервисная архитектура](#микросервисная-архитектура)\n  - [Многослойная архитектура](#многослойная-архитектура)\n- [Архитектурные паттерны](#архитектурные-паттерны)\n  - [MVC (1979)](#mvc-1979)\n  - [Иерархический MVC (2000), PAC (1987)](#иерархический-mvc-2000-pac-1987)\n  - [MVP (1996)](#mvp-1996)\n  - [MVVM (2005)](#mvvm-2005)\n  - [MVPVM](#mvpvm)\n  - [EBI (1992)](#ebi-1992)\n  - [Трёхуровневая архитектура](#трёхуровневая-архитектура)\n  - [DDD (2003)](#ddd-2003)\n  - [Шестиугольная архитектура, Порты и Адаптеры (2005)](#шестиугольная-архитектура-порты-и-адаптеры-2005)\n  - [Луковая архитектура (2008)](#луковая-архитектура-2008)\n  - [Кричащая архитектура (2011)](#кричащая-архитектура-2011)\n  - [Чистая архитектура (2012)](#чистая-архитектура-2012)\n- [Паттерны проектирования](#паттерны-проектирования)\n  - [Порождающие](#порождающие)\n  - [Структурные](#структурные)\n  - [Поведенческие](#поведенческие)\n- [Принципы проектирования](#принципы-проектирования)\n  - [Поддерживаемый код](#поддерживаемый-код)\n  - [Принципы пакетов](#принципы-пакетов)\n  - [Принцип разделения ответственности (SoC)](#принцип-разделения-ответственности-soc)\n  - [Принцип инверсии управления (IoC)](#принцип-инверсии-управления-ioc)\n  - [Основные принципы ООП](#основные-принципы-ооп)\n  - [Основные термины ООП](#основные-термины-ооп)\n  - [Композиционный принцип повторного использования](#crp-композиционный-принцип-повторного-использования)\n  - [Принципы SOLID](#принципы-solid)\n  - [Принцип DRY](#принцип-dry)\n  - [Принцип KISS](#принцип-kiss)\n  - [Принцип YAGNI](#принцип-yagni)\n\n\n# Определения\n\n**Архитектура программного обеспечения** (Software Architecture) - набор структур, необходимый для рассуждения о системе, содержащий элементы ПО, связи между ними и их свойства.\n\n**Архитектор** — один из опытнейших программистов в команде, умеющий анализировать высокоуровневые проблемы и их решения.  \n\nКаждый программист при написании кода вносит свой вклад в архитектуру приложения, и важно понимать, как это делать.\n\nВажно понимать, что не существует универсальных решений для всех приложений: все они имеют свои сильные и слабые стороны. Подобрать хорошую архитектуру можно только при условии хорошего понимания требований приложения. Иногда не подходит ни одна существующая архитектура. Так появляются новые.\n\n> Some architectural styles are often portrayed as ‘silver bullet’ solutions for all forms of software. However, a good designer should select a style that matches the needs of the particular problem being solved. \n**Roy Fielding, 2000**\n\nПонятия *архитектурный стиль*, *архитектурный паттерн* и *паттерн проектирования не взаимоисключающие* (not mutually exclusive), а *взаимодополняющие* (complementary). Они отличаются своей областью применения (scope).\n\n## Архитектурный стиль\n\n**Архитектурный стиль** (Architectural Style) в общих чертах указывает, как организовать код в проекте. Это наивысший уровень детализации (granularity), абстракции (abstraction), на котором определяются слои, модули высшего порядка приложения и их отношения, взаимодействия друг с другом.\n\nАрхитектурные стили не привязаны к конкретной реализации.\n\n### Примеры архитектурных стилей\n* Компонетный (component-based)\n* Монолитный (monolithic)\n* Многослойный (layered)\n* Событийный (event-driven)\n* Клиент-серверный (client-server)\n* Сервис-ориентированный (service-oriented)\n\n## Архитектурный паттерн\n\n**Паттерн, шаблон** (Pattern) — типичное решение широко распространённой проблемы. Отличается от *алгоритма* тем, что не содержит чёткой последовательности действий, а описывает общую концепцию (идею) решения, реализации которой могут сильно отличаться.\n\nПаттерны следует использовать только при необходимости, в противном случае они могут лишний раз усложнить код.\n\n**Архитектурный паттерн** (Architectural Pattern) решает проблемы, связанные с архитектурными стилями.\n\n### Примеры проблем, которые решают архитектурные паттерны\n* Как много уровней (tiers) будет в наше клиент-серверной архитектуре? Как они будут взаимодействовать?\n* Какие модули высшего уровня будут в нашей сервис-ориентированной архитектуре?\n\n### Примеры архитектурных паттернов\n* MVC (Model-View-Controller)\n* EBI (Entity-Boundary-Interactor)\n* Трёхуровневый (3-tier)\n\n## Паттерн проектирования\n\n**Паттерн проектирования** (Design Pattern) решает какую-то локальную проблему в проекте, затрагивая лишь конкретный блок кода, но не весь код проекта целиком.\n\n### Примеры проблем и решающих их паттернов проектирования\n* Нужно за чем-то проследить в приложении и отреагировать соотвествующем образом (например, показать уведомление при появлении пользователя в сети) — *поведенческий паттерн \"Наблюдатель\"*.\n* Нужно написать простой интерфейс для работы со сложной системой, библиотекой, API — *структурный паттерн \"Фасад\"*.\n\n## Принцип проектирования\n\n**Принцип проектирования** (Design Principle) предоставляет высокоуровневые рекомендации (high level guidelines) для проектирования приложений. Они не предоставляют рекомендации по реализации (implementation guidelines) и не привязаны к какому-либо языку программирования.\n\nСамыми популярными принципами проектирования являются принципы SOLID.\n\n# Архитектурные стили\n\n## Монолитная архитектура\n\n**Монолитная архитектура** означает, что приложение — большой связанный модуль, все компоненты которого спроектированы для совместной работы, используя общую память и ресурсы.\n\n## Микросервисная архитектура\n\nМикросервисная архитектура означает, что приложение состоит из маленьких независимых приложений, использующих собственные ресурсы и развивающихся независимо друг от друга, которые потенциально могут быть размещены на разных машинах.\n\n### Сага\n\n**Сага** (Saga) — архитектурный паттерн для реализации транзакции, охватывающей (span) несколько сервисов. \n\nВнутри сервиса доступны ACID-транзакции, но между несколькими сервисами их использовать нельзя.\n\nSaga — последовательность локальных транзакций. Каждый сервис в саге выполняет свою, локальную транзакцию и публикует событие. Другие сервисы прослушивают это событие и выполняют следующую локальную транзакцию. \n\nЕсли по какой-либо причине одна транзакция завершается неудачно, то нужно откатить (rollback) изменения предыдущих транзакций. В отличае от обыкновенных ACID-транзакций внутри сервисов, которые отменяются автоматически, в случае использования Saga каждая локальная транзакция делает commit и нужно явно (explicitly) отменить предыдущие действия. Каждая транзакция должна иметь компенсирующую (compensating) транзакцию, отменяющую её. В случае неудачи Saga поочерёдно выполняет компенсирующие транзакции.\n\n<!-- Saga - транзакционный механизм сообщений (transactional messaging mechanism): сервисы взаимодействуют преимущественно обменом сообщений. -->\n\n## Многослойная архитектура\n\n**Разделение на слои** (layering) — обычная практика для организации блоков кода по их роли или обязанностям в системе.\n\n# Архитектурные паттерны\n\n## MVC (1979)\nВ 1970-ых обязанности не разделялись: смешивание HTML, CSS и работы с базой данных считалось нормальной практикой. С ростом таких приложений становилось понятно, что они очень запутанны и их невозможно поддерживать (тратится слишком много ресурсов), поэтому нередко разросшиеся приложения приходилось переписывать с нуля.\n\nВ 1979 появился архитектурный шаблон **MVC** (Model-View-Controller), попытавшийся разрешить проблему, продвигая идею **разделения ответственности** (separation of concerns, SoC) между UI и бизнес-логикой.\n\n**Бизнес-логика** (business logic, domain logic) — часть приложения, задающая бизнес-правила, определяющие, как данные (состояние, state) предметной области (domain) приложения создаются, хранятся и изменяются.\n\n![MVC](./assets/MVC.png)\n\nMVC разделяет приложение на 3 концептуальные единицы\n* **Модель** (Model) представляет бизнес-логику (данные и правила, методы работы с ними) приложения; не содержит информации, как отобразить данные.\n* **Представление** (View) отвечает за пользовательский интерфейс и отображает часть данных из Model. Обычно это UI-компонента (кнопка, поле для ввода и прочее); то, что пользователь видит и с чем взаимодействует.  \n* **Контроллер** (Controller) выступает координатором между View и Model (решает, какие Views показывать и с какими данными), переводит действия пользователя (например, клик по кнопке) в бизнес-логику.\n\nМодель может быть представлена объектом или структурой объектов.\n\n### Особенности MVC  \n1) View использует объекты данных напрямую из Model, чтобы эти данные отобразить.  \n2) Когда данные в Model меняются, срабатывает событие, немедленно обновляющее View.  \n3) Один View обычно привязан к одному Controller.  \n4) Каждый экран может иметь несколько пар View-Controller.  \n5) Controller может быть связан с несколькими Views. \n\n### Проблема MVC в клиент-серверных приложениях\n\nКогда MVC появился, протокола HTTP (1992) и клиент-серверных приложений ещё не было.  \n\nПопытка наложить архитектуру MVC на современное клиент-серверное приложение приводит к множеству несоответствий (inconsistencies).\n\n* View отвечает за пользовательский интерфейс, его можно отнести к клиенту.  \n* Model обычно включет бизнес-логику приложения, такое лучше хранить на сервере в целях безопастности, но какая-то часть логики (например, валидация формы), может остаться на фронтенде, а соответственно и часть Model.  \n* Controller реагирует на действия пользователя и манипулирует Model. Нет однозначного ответа о его расположении.  \n* Изменения Модели не вызывают изменения во View напрямую: эта связь проходит через Controller. Это связано с тем, что клиент и сервер функционируют отдельно, как два разных приложения. Единственный способ исправить это — настроить веб-сокеты.\n\nВо многих современных приложениях для того, чтобы разгрузить сервер (сократить количество запросов к нему), часть логики и состояния храяится на фронтенде. Например, взаимодействие React и Redux может можно представить в виде MVC.\n\n## Иерархический MVC (2000), PAC (1987)\n\n**HMVC** (Hierarchical MVC) увеличивает модульность в контексте виджетизации UI-блоков.\n\nСуществует мнение, что авторы HMVC переосмыслили другой паттерн: **PAC** (Presentation-Abstraction-Control).\n\nHMVC разбивает уроверь клиента (client tier) на иерархию из MVC-слоёв. Это называется **проектированием клиентского уровня** (client-tier architecture) и должно увеличивать масштабируемость приложения: каждый MVC-слой независим от других и может работать при отсутствии любого другого.\n\n![HMVC](./assets/HMVC.png)\n\nController, обрабатывающий основной запрос, пересылает подзапросы другим Controllers, чтобы получить рендеринг виджетов и включить их в рендеринг основного View.\n\n## MVP (1996)\n\n*MVC* был хорош для приложений своего времени, но приложения выросли и настало время изменений.\n\n**MVP** (Model-View-Presenter) видоизменяет MVC, разделяя View и Model и осуществляя их коммуникацию только через **Presenter**.  \n\nСнимаем со View обязательство следить за обновлениями Model и наделяем таким обязательством Controller, переименовывая его в **Presenter**.\n\nЕсли UI-логику нужно тестировать, то она размещается в Presenter, в противном случае — в View.\n\n*Presenter* также называют **Supervisor Controller**.\n\n![MVP](./assets/MVP.png)\n\nВ случае, когда два View зависят друг от друга (input — один View, кнопка — другой), нужно ViewModel одного View подписаться на событие, которые излучает другой ViewModel. \n\n### Особенности MVP \n1) View пассивен (passive) и ничего не знает о Model. \n2) Presenter не содержит бизнес-логики, он просто вызывает методы Model, а затем передаёт необходимые данные во View.  \n3) Только один Presenter для каждого View.  \n4) Изменение данных в Model не вызывает немедленное обновление View: событие всегда проходит через Presenter, что позволяет в нём перед обновлением View проделывать дополнительную логику, связанную с представлением.\n\n### Зависимые Views\n\nПусть два Views зависят друг от друга.  \nНапример, первый View содержит поля для ввода (inputs), а второй View — кнопку.  \nКнопка недоступна (disabled) до тех пор, пока все поля не заполнятся.  \n\nЕсли данные из полей записываются в Model, то Presenter, привязанный к полям, записывает данные в Model, а Presenter, привязанный к кнопке, читает данные из Model.  \n\nЕсли данные не записываются в Model, то создаём оборачивающий Presenter, который реализует оба Presentors.\n\n### Пример связи View и Presenter\n\n```js\n// View сообщает Presentor, что готов к обновлению данных \nconst resume = () => {\n  Presenter.load();\n};\n// View сообщает Presentor, что не хочет отображать что-либо ещё\nconst pause = () => {\n   Presenter.stopLoad();\n};\n```\n\n## MVVM (2005)\n  \nСложность приложений продолжала расти и снова появилась необходимость изменений.\n\n**MVVM** (Model-View-ViewModel) призван разделить UI и бизнес-логику таким образом, чтобы за них могли отвечать разные люди, использующие разные технологии для этих целей. \n\nPresenter производит (produce) данные, View потребляет (consume) их. Почему производитель должен беспокоиться о потребителе?\n\nТаким образом, мы снимаем с Presenter обязательство изменять View. Вместо этого используем `Observable`: во View подписываемся на события (subscribe to events), излучаемые (emit) Presenter, который мы переименовываем во ViewModel.\n\nЕсли UI-логику нужно тестировать, то она размещается в ViewModel, в противном случае — в View.\n\n![MVVM](./assets/MVVM.png)\n\nВ случае, когда два View зависят друг от друга (input — один View, кнопка — другой), необходимо, чтобы ViewModel  View подписаться на событие, которые излучает другой ViewModel. \n\n### Особенности MVVM\n1) Один ViewModel соответствует только одному View и наоборот.\n2) Вся логика из View перемещается во ViewModel, чтобы упростить View и позволить ему выполнять свою задачу (визуализация).\n3) Отношение один к одному установлено между данными во View и данными во ViewModel.\n4) Изменение данных во ViewModel вызывает немедленное обновление View.\n\n### Зависимые Views\n\nПусть два Views зависят друг от друга.  \nНапример, первый View содержит поля для ввода (inputs), а второй View — кнопку.  \nКнопка недоступна (disabled) до тех пор, пока все поля не заполнятся.  \n\nЕсли данные из полей записываются в Model, то ViewModel, привязанный к полям, записывает данные в Model, а ViewModel, привязанный к кнопке, читает данные из Model.  \n\nЕсли данные не записываются в Model, то создаём оборачивающий Presenter, который реализует оба Presentors.\n\nЕсли данные не записываются в Model, то подписываем ViewModel, привязанный к кнопке, на событие, излучаемое ViewModel, привязанным к полям ввода.\n\n### Пример связи View и Presenter\n\n```js\n// View сообщает Presentor, что готов к загрузке данных \nconst resume = () => {\n  ViewModel.subscribe(/* ... */);\n};\n// View сообщает Presentor, что не хочет отображать что-либо ещё\nconst pause = () => {\n   ViewModel.unsubscribe(/* ... */);\n};\n```\n\n## MVPVM\n\n**Model** — набор классов, содержащий всю бизнес-логику и случаи использования.\n\n**View** — шаблон (template), на основании которого геренируется HTML при помощи шаблонизатора (template engine).\n\n**Необработанные данные** (raw data) — данные, которые не были обработаны для использования.\n\n**ViewModel** (Presentation Model) получает необработанные данные из query и держит их для использования в шаблоне; инкапсулирует сложную логику представления для упрощения шаблона.\n\nПочему важен ViewModel\n* Изменения в Model могут всплыть и повлиять на ViewModel, но не на View.\n* Сложная логика представления не просочится в предметную область (not leak into the domain), поскольку мы можем инкапсулировать эту логику во ViewModel.\n* Зависимости View становятся явными, поскольку они должны быть установлены в ViewModel.\n\n*View* и *ViewModel* имеют связь \"один к одному\".\n\n**Presenter** получает HTTP-запрос (request), вызывает команду (command) или запрос (query), использует данные результата запроса (query), ViewModel, шаблон и шаблонизатор для генерации HTML и отправляет его клиенту в качестве HTTP-ответа (response).  \n\nВсе взаимодействия Views проходят через Presenter.\n\n![MVPVM](./assets/MVPVM.png)\n\n## EBI (1992)\n\nАрхитектурный шаблон **Entity-Boundary-Interactor** (EBI) был опубликован Иваром Якобсоном в одной из серии книг об объектно-ориентированной разработке ПО.\n\nИзначально автор назвал архитектуру Entity-Interface-Control, но затем переименовал для понимания, что Interface не связан с одноимённой конструкцией в языках программирования, а Control не связан с Controller в MVC.\n\n*Цель EBI*: обычно объектно-ориентированные методологии помещают все обязанности в одну сущность, без разделения, но автор книги считает, что разделение (инкапсуляция) обязанностей позволяет сделать систему более гибкой к изменениям, которые в таком случае становятся локальными (изменяется только один объект системы).\n \n**Механизм доставки** (delivery mechanism) — часть приложения, которую клиент использует для выполнения приложения. Эта часть не должна определять логику приложения.\n\n![EBI](./assets/EBI.png)\n\nСхема работы в обозначениях, введённых автором книги.\n\n![EBI](./assets/EBI_2.png)\n\n### Entity\n\n**Entities** содержат данные системы и всю логику, напрямую связанную с этими данными (такую логику, которая при внесении изменений в Entity тоже должна изменяться; меняется структура — меняются методы работы с ней). Эта логика не зависит от приложения.\n\n*Entities* можно назвать бизнес-объектами приложения.\n\n### Boundary\n\n**Boundaries** содержат в себе функциональность, касающуюся интерфейса системы (системного окружения), то есть отвечают за связь системы с окружающим миром.\n\nЛюбое *взаимодействие с системой* происходит через *Boundary*. Действующее лицо (actor) может быть не только человеком, но и другим устройсвом или сторонним API.\n\n*Boundary принимает запрос* (request) и *выдаёт ответ* (response). Эти абстракции *реализуются* (implement) в *Interactor*.\n\n### Interactor\n\n**Interactors** содержат поведение, не вошедшее в Boundary и Entity (обычно это операции над несколькими Entity, результат  которых возвращается в Boundary; похоже на сервисы).\n\n*Основная задача Interactor*: получить запрос от *Boundary*, манипулировать состоянием приложения (application state) через *Entities*, вернуть ответ *Boundary*. \n\n*Interactors* — слой *бизнес-логики приложения*.  \n\n*Interactor* является конкретной реализацией (concrete implementation) *Boundary*.\n\n*Антипаттерн*: Entity содержит лишь данные, всё поведение выносится в Interactor. \n\n*Interactors* важны, поскольку они содержат специфическую логику для конкретных *Boundaries*, в то время как *Entity* содержит общую (generic) логику для всех *Boundaries*. В случае, если вся логика будет храниться в *Entity*, некоторые *Boundaries* будут иметь доступ к тому, к чему не должны, что увеличивает сложность и может привести к ошибкам.\n\n### Сравнение EBI и MVC\n\nEBI стремится к тому, чтобы бизнес-логика приложения, представленная Entities и Interactors, была независима от механизма доставки. Эта логика не должна знать, что её использует. Это позволяет писать на любом языке для любого случая использования (use case). Требуется лишь умение определять абстракции на подобие интерфейсов. Причём статическая типизация здесь не нужна: нужен только способ создания чётких и проверяемых определений функциональности. Если необхомимой структуры нет, то дотаточно написания документации. Boundary должен быть уточнён (be specified), но не важно, каким образом.\n\nMVC всегда зависит от механизма доставки.\n\nView и Controller в MVC составляют весь уровень представления, Model включает в себя всё остальное в приложении, в том числе и бизнес-логику. Boundary в EBI — одно целостное соединение с окружающим миром, Entities и Interactors в EBI представляют всю бизнес-логику. Таким образом MVC и EBI не заменяют друг друга, но пересекаются. Совместное их использование породило бы паттерн View-Controller-Boundary-Interactor-Entity.\n\n### Пример реализации EBI\n\n```\napi/\n  article.ts\ncore/  \n  entities/  \n    article.ts\n    entity.ts\n  interactors/  \n    article.ts  \nservices/  \n  requests/  \n    article.ts  \n    request.ts  \n  responses/  \n    article.ts  \n    response.ts  \n  index.ts  \n```\n\n#### Entities\n```ts\n/* core/entities/entity.ts */\nexport interface Entity {\n  id: string;\n}\n\n/* core/entities/article.ts */\nimport { Entity } from './entity.ts';\n\nexport interface Article extends Entity {\n  title: string;\n}\n\nconst validate = (request: Article) => {\n  if (request.title.length === 0) {\n    throw new Error('Article\\'s title must not be empty!');\n  }\n  return true;\n};\n```\n\n#### Interactors\n```ts\n/* core/interactors/article.ts */\n\nexport const createArticle (request: CreateArticleRequest): CreateArticleResponse => {\n  \n}\n\nexport const findArticle (request: FindArticleRequest): FindArticleResponse => {\n  \n}\n\n```\n\n#### Boundaries\n\n```ts\n/* services/article.ts */\nexport interface ArticleBoundary {\n  createArticle(request: CreateArticleRequest): CreateArticleResponse;\n  findArticle(request: FindArticleRequest): FindArticleResponse;\n}\n```\n\n#### Модели запросов (Request Models)\n```ts\n/* services/requests/request.ts */\nexport interface Request {}\n\n/* services/requests/article.ts */\nimport { Request } from './request.ts';\n\nexport interface FindArticleRequest extends Request {\n  id: string;\n}\n\nexport interface CreateArticleRequest extends Request {\n  title: string;\n}\n```\n#### Модели ответов (Response Models)\n```ts\n/* services/responses/response.ts */\ninterface Response {}\n\n/* services/responses/article.ts */\nimport { Response } from './response.ts';\n\nexport interface FindArticleResponse extends Response {\n  id: string;\n  title: string;\n}\n\nexport interface CreateArticleResponse extends Response {\n  id: string;\n  title: string;\n}\n```\n\n## Трёхуровневая архитектура\n\n**Трёхуровневая архитектура** (3-Tier) разбивает приложение на 3 логических уровня (слоя). \n\nЧаще всего используется для *клиент-серверных* приложений.\n\n*Трёхуровневая архитектура* позволяет разрабатывать и размещать каждый уровень отдельно от остальных. Это делает приложение более масштабируемым, поскольку каждый уровень развивается и функционирует относительно независимо от других. Разбиение на уровни позволяет также нанимать узкоспециализированных специалистов (дизайнеры, верстальщики, бэкендеры, тестировщики бэкенда, DevOps), которые будут быстро и качественно выполнять свою часть работы.\n\n![3-tier](./assets/3-tier.png)\n\n### Уровни\n* **Уровень представления** (Presentation Tier) содержит пользовательский интерфейс (UI). Соответствует фронтенд (HTML, JS, CSS, фреймворки и прочее).\n* **Уровень приложения** (Application Tier) содержит бизнес-логику приложения. Соответствует бэкенду (Node.js, .NET, Java и прочее).\n* **Уровень данных** (Data Tier) включает в себя базу данных или другую систему хранения данных, а также уровень доступа к данным (data access layer). Примеры: MySQL, MongoDB, DynamoDB и прочие.\n\n*Уровни общаются* между собой при помощи *API-запросов*.\n\n## DDD (2003)\n\nАрхитектуру **Domain-Driven Design** (DDD) — предметно-ориентированное проектирование — предложил Эрик Эванс.\n\nИдея DDD: при разработке проекта основное внимание должно уделяться основной предметной области (domain) и её логике. Для этого нужно тесное сотрудничество между техническими специалистами и экспертами предметной области.\n\n### Единый язык\n\n**Единый, повсеместный, общий  язык** (Ubiquitous language) — общий язык между предметной областью и её технической реализацией. Источником этого языка должна быть предметная область (бизнес).  \n\nКаждая предметная область имеет свои определения, часто встречающиеся в требованиях заказчика, но программисты часто переименовывают многие вещи по-своему, создавая таким образом свою собственную модель предметной области, придумывают свои понятия, которые потом нужно объяснять другим программистам. Так возникают барьеры недопонимания, чего нужно избегать.\n\nРазработка единого языка требует много времени на тщательный анализ требований и консультации с экспертами предметной области, но это позволяет создать внутреннюю базу знаний клиента. \n\n### Слои в DDD\n* **Пользовательский интерфейс** (User Interface) обеспечивает взаимодействие окружающего мира с приложением, преобразуя входные данные в команды приложению (аналогично Boundary в EBI).\n* **Слой приложения** (Application Layer) управляет объектами предметной области (Domain Objects), чтобы выполнять приходящие команды — **Случаи использования** (the Use Cases). Не содержит бизнес-логику. На этом слое лежат *Сервисы приложения* (Application Services), являющиеся контейнерами, в которых используются такие объекты предметной области, как Репозитории, Domain-сервисы, Сущности, Value-объекты (аналогично Interactor в EBI, но не все \"не Boundary, не Entity\" объекты, а только соотвутствующие *Случаям использования*).\n* **Слой предметной области** (Domain Layer) содержит всю бизнес-логику. (аналогично Entity в EBI).\n* **Инфраструктура** (Infrastructure) содержит технические возможности, которые поддерживают слои выше (например, отправка сообщений).\n\n### Объекты предметной области\n\n**Контекст** (Context) — окружение рассматриваемого объекта, влияющее на его смысл.\n\n**Предметная область** (Domain) — сфера знаний, к которой относится программа.\n\n**Модель** (Model) — система абстракций, описывающая выбранные аспекты предметной области и использующаяся для решения её проблем.\n\n**Сущность** (Entity) — объект, имеющий индивидуальные черты. Имеет уникальный идентификатор (id). Две Сущности считаются разными даже в случае совпадения всех свойств.\n\nПример Сущности: Несмотря на то, что в кинотеатре все места выглядят одинаково, каждое место имеет свой номер (уникальный идентификатор). Посетитель приходит и садится на место, указанное в его билете. \n\n**Value-объект** (Value Object) — неизменяемый (immutable) тип объекта, полностью определяемый значениями своих свойств. В отличие от Сущности не имеет уникального идентификатора (id). Два Value-объекта с одинаковыми свойствами считаются идентичными.\n\nПример Value-объекта: В общественном транспорте все места одинаковы: можно сесть куда угодно, нумерация не имеет значения. \n\n**Агрегат** (Aggregate) — коллекция объектов, связанных вместе корневой Сущностью — **корнем агрегата** (Aggregate Root). \n\nКорневой агрегат гарантирует согласованность изменений, вносимых в агрегат, не позволяя внешним объектам хранит ссылки на его элементы. \nАгрегаты можно рассматривать как ограниченный контекст, предоставляющий корневому объекту и всему графу объектов контекст, в котором они используются.\n\n**Репозиторий** — объект, сохраняющий Сущности или Агрегаты в базовый механизм хранения или извлекающий ихиз него.  \n\nРепозиторий является частью модели предметной области (domain model), поэтому он должен быть независим от поставщика (vendor) базы данных.\n\n**Сервис** — объект, содержащий методы (команды), концептуально не привязанные к какому-либо объекту (Entity, Value-объекту). Не имеет состояния (stateless).\n\n### Ограниченный контекст\n\nВ корпоративных (enterprise) системах, где работает огромное количество разработчиков, модель может сильно разрастить. Сложно держать всю структуру огромного проекта в голове. Сложно распределять обязанности, когда все работают в одном месте. В этом случае систему обычно разбивают на подсистемы. \n\n**Ограниченный контекст** (Bounded context) — подсистема в рамках DDD, определяющая контекст применения изолированной части модели.\n\nИзолированность достигается разными способами (например, разделением схемы базы данных на части или разделением обязанностей между программистами по их умениям).  \n\nХорошее разделение: сильная функциональная связь внутри подсистем и слабая между подсистемами.\n\n### Антикоррупционный слой\n\n**Антикоррупционный слой** — промежуточный слой между двумя подсистемами, заменяющий их взаимодействие друг с другом на взаимодействие с собой; изолирующий подситемы друг от друга. В таком случае при удалении или замене одной из подсистем вторая останется неизменной (изменится только антикоррупционный слой).\n\nАнтикоррупционный слой можно использовать, когда одна или обе подсистемы не контролируются, или когда нужно предотвратить утечку из  модели одной системы в другую (изменяем одну подсистему под требования другой).\n\n### Общее ядро\n\nИногда, несмотря на преимущества изолированности, имеет смысл использовать общий код между несколькими компонентами (чтобы не дублировать его). Компоненты остаются независимыми друг от друга, хотя и используют **общее ядро** (shared kernel). \n\nТак один компонент может прослушивать событие, запущенное другим копонентом. Другой пример: использование сервиса.\n\nИзменять общее ядро нужно очень осторожно, поскольку оно может сломать сразу несколько компонент.\n\n### Общая подобласть\n\n**Подобласть** (subdomain) — хорошо изолированная часть предметной области (domain).\n\n**Общая подобласть** (generic subdomain) — подобласть, которая не специфична конкретно для приложения, а может использоваться почти в любом другом. (например, рисование статистики, работа с камерой, платёжные методы).\n\nЕсли в приложении есть общие подобласти, нужно уделять им как можно меньше внимания по сравнению с предметной область. Это не самая важная часть приложения, а если и важная, то не критичная (essential but not crucial). \n\nОбщие подобласти лучше не писать, а устанавливать как сторонние пакеты.\n\n## Шестиугольная архитектура, Порты и Адаптеры (2005)\n\n**Архитектура \"Порты и Адаптеры\"** (Ports & Adapters Architecture), **Шестиугольная архитектура**  (Hexagonal Architecture) была предложена Алистером Кокбёрном.\n\n*Цель Шестиугольной архитектуры*: позволить приложению одинаково управляться пользователями, программами, автоматизированными тестовыми сценариями, а также разрабатываться и тестироваться изолированно от возможных устройств и баз данных.\n\nОсновная идея заключается в том, чтобы оградить наше приложение (центральный блок системы) от всего ввода и вывода при помощи порта, ограничивающего приложение от внешних инструментов и технологий. Таким образом приложение не знает, что именно отправило ему входные данные и что получило выходные данные из него. Это позволяет обеспечить некоторую защиту приложения от изменений внешних технологий (например, замены технологии) и бизнес-требований.\n\n### Проблемы традиционного подхода\nПроблема на фронтенде: происходит утечка бизнес-логики в UI (например, логика для конкретного случая использования попадает во View или Contoller, что не может переиспользоваться в других UI-компонентах).\n\nПроблема на  бэкенде: происходит утечка внешних библиотек и технологий в бизнес-логику, потому что мы часто напрямую ссылаемся на эти технологии, используем их типы, создаём подклассы и экземпляры классов библиотек в нашей бизнес-логике.\n\n### Шестиугольник\n\nМногослойные архитектуры по типу EBI, DDD показывают преимущества использования слоёв в архитектуре.\n\nАвтор подхода осознал, что у всех многослойных архитектур есть что-то общее, их можно упростить следующим образом:  \nПредставление (Presentation) — Бизнес-логика (Business logic) — Данные (Data).\n\nТакже Алистер заметил, что Представление и Данные являются по сути точками входа/выхода приложения, то есть в приложении есть симметрия.\n\nПредставим схему со слоями не сверху вниз, а слева направо и разделим на две симметричные части, каждая из которых будет содержать несколько точек входа/выхода, а в центре приложение. Например, левая содержит точки UI и API, а правая — точки ODM, поисковой движок и сервис отправки SMS-сообщений.\n\nЧтобы показать, что приложение имеет много точек входа/входа, приложение представляется в виде многоугольника (одна сторона — одна точка). Несмотря на то, что сторон может быть сколько угодно, архитектуру назвали шестиугольной (hexagonal).\n\n### Порт и Адаптер\n\n**Порт** (Port) — независимая точка входа в приложение и (или) точка выхода из него. Во многих языках это называют *интерфейсом*. Мы используем этот интерфейс как точку входа/выхода, не зная конкретной реализации.\n\n**Адаптер** (Adapter) — класс, адаптирующий (adapt) один интерфейс к другому. \n\n**Первичный, управляющий адаптер** (Primary, Driving Adapter) — адаптер левой стороны схемы, отвечающей за UI и API. Запускает какое-то действие в приложении. \n\n*Первичный адаптер* зависит от порта и содержит конкретную реализацию порта, содержащую случай использования (the Use Case). Порт и его реализация (случай использования) принадлежат приложению.\n\n**Вторичный, управляемый адаптер** (Secondary, Driven Adapter) — адаптер правой стороны схемы, отвечащей за подключение бэкенд-технологий. Реагирует на действие первичного адаптера.\n\n*Вторичный адаптер* — конкретная реализация порта, внедряющаяся в бизнес-логику приложения (хотя бизнес-логика  знает только об интерфейсе). Порт принадлежит приложению, а его конкретная реализация лежит снаружи и оборачивает какой-то внешний инструмент.\n\n### Пример использования Вторичных Адаптеров\n\nНужно подключить Mongoose ODM к нашему приложению, чтобы связать его с базой данных MongoDB.  \n\nПри ипользования традиционного подхода, мы напрямую используем возможности Mongoose.\n```js\nconst mongoose = require('mongoose');\n\nconst User = mongoose.model('User', { name: String });\n\nconst create = (name) => {\n  const user = new User({ name });\n  return user.save();\n};\n```\nЧерез месяц заказчик передумал: теперь он хочет реляционную базу данных.  \nНужно заменить Mongoose ODM на Sequelize ORM.\n```js\nconst { Sequelize, Model, DataTypes } = require('sequelize');\nconst sequelize = new Sequelize(/* ... */);\n\nclass User extends Model {}\nUser.init({ name: DataTypes.STRING }, { sequelize, modelName: 'user' });\n\nconst create = name => User.create({ name });\n```\nТакая миграция кода достаточно болезненна.\n\nВ случае использования Портов и Адаптеров, мы создаём интерфейс (порт), подсказывающий, что должно быть реализовано.\n```ts\ninterface IUser {\n  name: string!\n}\n\n/* порт */\ninterface IUserRepository {\n  create: (name: string) => IUser\n}\n```\nСоздаём Адаптер `UserMongoAdapter`, в котором и будет хранится вся логика работы с Mongoose, включающая реализацию порта.\n\nТеперь, когда поступает задание о смене базы данных, мы просто создаём ещё один Адаптер `UserSequelizeAdapter` с другой реализацией порта. \n\nТеперь мы можем достаточно просто заменять один адаптер другим.  \nЕсли мы хотим делать это в режиме реального времени (runtime), то следует использовать паттерн \"Фабрика\".\n\nТестирование также упрощается. Создаются Макет (Mock) или Зуглушка (Stub) на основании интерфейса (порта) и приложение тестируется без взаимодействия с реальной базой данных.\n\n### Пример использования Первичных Адаптеров\n\nПусть у нас есть два UI: основной сайт для пользователей и инструменты модераторов.  \nХотим сделать функциональность отправки электронных сообщений, доступный в обоих UI.\n\nИспользуя Порты и Адаптеры, мы должны создать интерфейс (порт) предполагаемого сервиса `EmailService` и реализовать его в приложении.\n```ts\ninterface IEmailService {\n  sendEmail: (receiver: string, message: string) => void\n}\n```\nМетод `sendEmail` является Случаем использования (Use Case).\n\nКаждый UI имеет свой Controller (или команду консоли), использующий интерфейс, чтобы вызывать реализованные в сервисе методы. В данном случае Controller и есть Адаптер (адаптер находится на фронтенде, а порт и его реализация на бэкенде).\n\nТеперь мы можем быть уверены, что UI не затронет бизнес-логику.\n\nТестирование такогоподхода тоже имеет свои преимущества. Мы тестируем UI отдельно от приложения и тестируем случаи использования отдельно от UI.\n\n## Луковая архитектура (2008)\n\n**Луковая архитектура** (Onion Architecture) предложена Джефри Палермо.\n\nЛуковая архитектура имеет схожую идею с Шестиугольной: использовать что-то похожее на Адаптер во избежание утечки инфраструктуры (сторонние библиотеки и API) в бизнес-логику. Но, помимо предложенного в Шестиугольной архитектуре разделения на внутренний (internal) слой с бизнес-логикой и внешний (external) слой с инфраструктурой, добавляются дополнительные слои, напоминающие слои в DDD.\n\n*Слои* предтавляются в *форме луковицы*.  \n*Внешние слои зависят* от *внутренних*, *внутренние* ничего *не знают* о *внешних*.  \nЭто позволяет нам изменять внешние слои, не затрагивая внутренние, что напоминает принцип Dependency Inversion из SOLID, но на архитектурном уровне.\n\nБолее того, любой внешний слой может напрямую обращаться к любому внутреннему.  \nЭто позволяет избежать создания вспомогательных прокси-методов и классов, усложняющих код.\n\n![Onion Architecture](./assets/Onion.png)\n\n### Ключевые принципы Луковой архитектуры\n* Приложение построено вокруг независимой объектной модели (в центре луковицы располагается модель предметной области, которая ни от чего не зависит).\n* Внутренние слои определяют интерфейсы, внешние слои их реализовывают.  \n* Направление связанности идёт к центру (direction of coupling is toward the center).\n* Ядро приложения (Application Core) может быть запущено отдельно от инфраструктуры.\n\n## Кричащая архитектура (2011)\n\n**Кричащая архитектура** (Screaming Architecture) предложена Робертом С. Мартином.  \n\nИдея Кричащей архитектуры: Архитектура должна чётко показывать, в чём заключается суть системы.   \n\nПервые папки в проекте должны быть связаны с предметной областью, верхним уровнем абстракции (Users, Admins, Messages), но не с названием технологий (mongodb, jwt, redis), не с функциональными блоками (services, controllers, repositories), не с механизмами доставки (http, консоль).\n\n## Чистая архитектура (2012)\n\n**Чистая архитектура** (Clean Architecture) предложена Робертом С. Мартином (дядей Бобом).\n\n*Чистая архитектура* объединяет в себе многие известные идеи, стили и паттерны чтобы показать, как они могут работать вместе.\n\n*Цель Чистой архитектуры* та же: независимость от внешних инструментов и механизмов доставки.\n\n### Слои в Чистой архитектуре\nШестиугольная архитектура имеет только два слоя: внешний и внутренний.\n\nЛуковая архитектура: Сервисы приложения (Application services), Сервисы предметной области (Domain Services), Модель предметной области (Domain Model) — основная часть приложения (Application Core), пользовательский интерфейс и инфраструктура — внешняя часть.\n\nЧистая архитектура: Сервисы приложения, Слой сущностей (Entities Layer) — основная часть приложения. \n\nТаким образом, Роберт Мартин объединил два слоя в один для простоты, переопределив понятие сущности.\n\n**Сущность** (Entity) — объект с методами или набор структур данных и функций.\n\n### Поток управления\n\n1. HTTP-запрос поступает в Controller.  \n2. Создаётся модель запроса (Request Model).  \n3. Выполняется метод в Interactor, передавая ему модель запроса.  \n4. Interactor использует реализацию Entity Gateway, чтобы найти необходимые сущности; оперирует ими; генерирует из результата модель ответа (Response Model); передаёт модель ответа в Presenter и возвращает его в Controller.  \n5. Используется Presenter, чтобы сгенерировать ViewModel.  \n6. ViewModel привязывается ко View.  \n7. View возвращается клиенту.  \n\n# Паттерны проектирования\n\n## Порождающие\n\n**Порождающие паттерны** отвечают за удобное и безопасное создание новых объектов или групп объектов.\n\n- [Одиночка (Singleton)](#одиночка)\n- [Внедрение зависимостей (Dependency Injection)](#внедрение-зависимостей)\n- [Фабричный метод (Factory Method)](#фабричный-метод)\n- [Абстрактная фабрика (Abstract Factory)](#абстрактная-фабрика)\n- [Строитель (Builder)](#строитель)\n- [Прототип (Prototype)](#прототип)\n### Одиночка\n\n**Одиночка** (Singleton) — порождающий паттерн, гарантирующий существование только одного объекта определённого класса. Одиночка позволяет обратиться к этому объекту из любого места в приложении.\n\nЧасто считается антипаттерном, поскольку нарушает модульность кода (похож на глобальную переменную).\n\nДля реализации одиночки пишется класс с приватным конструктором; стратическим полем, хранящим экземпляр (instance) класса, и статическим методом, создающим и сохраняющим экземпляр при первом обращении и возвращает при последующих.\n```ts\nclass Singleton {\n  private static instance: Singleton;\n  private constructor() {}\n  static getInstance(): Singleton {\n    if (!Singleton.instance) {\n      Singleton.instance = new Singleton();\n    }\n    return Singleton.instance;\n  }\n}\n\nconst singletonA = Singleton.getInstance();\nconst singletonB = Singleton.getInstance();\nconsole.log(singletonA === singletonB); // true\n```\n\n### Внедрение зависимостей\n\n**Внедрение зависимостей** (Dependency Injection) — порождающий паттерн, реализующий *принцип инверсии управления* (IoC). \n\n*Внедрение зависимостей* позволяет полностью вынести создание объектов, от которых зависит некоторый класс, за пределы этого класса, предоставляя эти объекты другим путём.\n\n*Внедрение зависимостей* вводит следующие 3 понятия\n* **Клиент** — класс, зависимый (dependent) от *Сервиса*.\n* **Сервис** — класс, предоставляющий свой объект *Клиенту*.\n* **Инжектор** — класс, внедряющий объект *Сервиса* в *Клиент*.\n\nВозьмём пример из раздела с *принципом инверсии управления* и улучшим его при помощи *принципа инверсии зависимостей* (DIP), заменив классы на интерфейсы, где это возможно.\n```ts\ninterface ISalary {\n  value: number;\n  currency: string;\n}\n\ninterface IEmployee {\n  salary: ISalary;\n}\n\nconst SalaryFactory = (value: number, currency: string): ISalary => new Salary(value, currency);\n\nclass Employee implements IEmployee {\n  salary: ISalary;\n\n  constructor() {\n    this.salary = SalaryFactory(500, '$');\n  }\n\n  getSalary() {\n    return this.salary.toString();\n  }\n}\n\nclass Salary implements ISalary {\n  value: number;\n  currency: string;\n\n  constructor(value: number, currency: string) {\n    this.value = value;\n    this.currency = currency;\n  }\n\n  toString() {\n    return `${this.value}${this.currency}`;\n  }\n}\n```\nПаттерн \"Фабрика\" отвечает за создание объекта класса `Salary`, но само создание (пусть и при помощи фабрики) всё ещё происходит в классе `Employee`.\n\nВ данном примере класс `Employee` — *Клиент*, `Salary` — *Сервис*.  \nРассмотрим 3 способа, как можно написать *Инжектор*, который назовём `EmployeeInjector`:\n* *Внедрение в конструктор*\n* *Внедрение свойства*\n* *Внедрение метода*\n\n**Внедрение в конструктор** (Constructor Injection): зависимость предоставляется через параметры конструктора (`constructor(salary:  ISalary)`).\n```ts\nclass Employee implements IEmployee {\n  salary: ISalary;\n\n  constructor(salary: ISalary) {\n    this.salary = salary;\n  }\n}\n```\nИнжектор в этом случае:\n```ts\nclass EmployeeInjector {\n  emp: IEmployee;\n\n  constructor() {\n    const salary = new Salary(500, '$');\n    this.emp = new Employee(salary);\n  }\n}\n```\n\n**Внедрение свойства** (Property Injection): зависимость предоставляется через публичное свойство (`emp.salary`).\n```ts\ninterface IEmployee {\n  salary?: ISalary;\n}\n\nclass Employee implements IEmployee {\n  salary?: ISalary;\n\n  constructor() {}\n}\n```\nИнжектор в этом случае:\n```ts\nclass EmployeeInjector {\n  emp: IEmployee;\n\n  constructor() {\n    this.emp = new Employee();\n    this.emp.salary = new Salary(500, '$');\n  }\n}\n```\nПоскольку начальное значение `Salary` не задано в примере, пришлось сделать его необязательным (`?:`) в интерфейсе `IEmployee` и классе `Employee` во избежание ошибок инициализации. Задать начальное значение *внедрением свойства* нельзя. \n\n**Внедрение метода** (Method Injection): зависимость предоставляется через метод интерфейса или класса (`setSalary(salary)`).\n```ts\n/* определяем метод интерфейса, принимающий salary */\ninterface IEmployeeMethods {\n  setSalary: (salary: ISalary) => void;\n}\n\n/* реализуем метод интерфейса в классе */\nclass Employee implements IEmployeeMethods {\n  salary?: ISalary;\n\n  constructor() {}\n\n  setSalary(salary: ISalary): void {}\n}\n```\nИнжектор в этом случае:\n```ts\nclass EmployeeInjector {\n  emp: IEmployee;\n\n  constructor() {\n    this.emp = new Employee();\n    const salary = new Salary(500, '$');\n    /* приводим this.emp к типу интерфейса, чтобы вызвать метод */\n    (<IEmployeeMethods>this.emp).setSalary(salary);\n  }\n}\n```\n\n*Внедрение зависимости* на примере *модулей*:  \nПусть есть два модуля A и B, модуль B использует модуль A.  \nПростой случай включения модуля:\n```js\n/* B.js */\nconst A = require('A');\n\nmodule.exports = {\n  /* работа с модулем A */\n};\n```\n*Внедрение зависимости*:\n```js\n/* B.js */\n\nmodule.exports = (A) => {\n  /* работа с модулем A */\n};\n```\n```js\n/* C.js */\nconst A = require('A');\nconst B = require('B');\n\nmodule.exports = {\n  B: B(A);\n};\n```\n\n**IoC-контейнер, DI-контейнер** — *фреймворк*, реализующий *автоматическое внедрение зависимостей*. Он управляет созданием объектов и их временем жизни, а также внедряет зависимости в класс.\n\n*IoC-контейнер* создаёт объект какого-то класса и встраивает все объекты, от которых зависит класс, в конструктор, свойство или метод класса во время выполнения (at run-time) и утилизирует (dispose), когда это необходимо.\n\n*IoC-контейнер* предоставляет следующий жизненный цикл внедряемых зависимостей (DI lifecycle):\n* Регистрация (Register)\n* Разрешение (Resolve)\n* Ликвидация (Dispose)\n\nПример использования *IoC-контейнера* в *NodeJS-приложении*.\n```js\nconst awilix = require('awilix');\n\nconst container = awilix.createContainer({\n  injectionMode: awilix.InjectionMode.PROXY\n});\n\nconst createUserRepository = ({ db }) => ({\n  getUserById(id) { return db.query(/* ... */) }\n});\n\nfunction Database(connectionString) { /* ... */ }\n\n/* регистрация */\ncontainer.register({\n  connectionString: awilix.asValue('/* ... */'),\n  userRepository: awilix.asFunction(createUserRepository),\n  db: awilix.asClass(Database),\n});\n\n/* разрешение и использование */\ncontainer.resolve('userRepository').getUserById('1');\n\n/* ликвидация контейнера */\ncontainer.dispose();\n```\n\n### Фабричный метод\n\n**Фабричный метод** (Factory Method) — порождающий паттерн, определяющий интерфейс для создания объекта некоторого класса, при этом решение о том, какой класс будет у объекта, принимается в дочернем классах.\n\nСвязываем объекты разных классов в одно семейство, характеризующее их общие черты.\n\nЛебеди и утки являются летающими птицами. Пусть их классы `Dove` и `Duck` реализуют общий интерфейс `IFlyingBird`. \n```ts\ninterface IFlyingBird { /* ... */ }\nclass Dove implements IFlyingBird { /* ... */ }\nclass Duck implements IFlyingBird { /* ... */ }\n```\nОписываем абстрактный фабричный метод, создающий летающий птиц, и реализуем его в конкретных классах (фабриках).\n```ts\ninterface IFlyingBirdFactory {\n  create(): IFlyingBird /* фабричный метод */\n}\n\nclass DoveFactory implements IFlyingBirdFactory {\n  create(): IFlyingBird {\n    return new Dove();\n  }\n}\n\nclass DuckFactory implements IFlyingBirdFactory {\n  create(): IFlyingBird {\n    return new Duck();\n  }\n}\n\nconst doveFactory = new DoveFactory();\nconst dove = DoveFactory.create();\n\nconst duckFactory = new DuckFactory();\nconst duck = DuckFactory.create();\n\nconst flyingBirds: IFlyingBird[] = [dove, duck];\n```\nИспользовать фабрики очень удобно, поскольку мы избегаем использования оператора `new` напрямую, инкапсулируя эту логику внутри метода.\nЭто позволяет также инкапсулировать внутри метода некоторые общие свойства для какой-то подгруппы объектов, не дублируя при этом код и не создавая новый класс для них (не считая самой фабрики).\n```ts\nclass BlackDoveFactory implements IFlyingBirdFactory {\n  create(): IFlyingBird {\n    return new Dove({ color: 'black', size: 'large' });\n  }\n}\n```\n\nСделать что-то похожее можно при помощи функций, но это уже нельзя назвать фабричным методом.\n```ts\nconst createDove = (): Dove => new Dove();\nconst createWhiteDove = (): Dove => new Dove({ color: 'white', gender: 'female' });\nconst createBlackDove = (): Dove => new Dove({ color: 'black', gender: 'male' });\n\n/* создаём 5 чёрных лебедей */\nconst doves: Dove[] = [...new Array(5)].map(createBlackDove);\n/* добавляем к ним одного белого */\ndoves.push(createWhiteDove());\n```\n\n### Абстрактная фабрика\n\n**Абстрактная фабрика** (Abstract Factory) — порождающий паттерн, позволяющий создавать семейство концептуально связанных объектов, не привязываясь к классам этих объектов.\n\nИнтерфейс `IFlyingBirdFactory` из примеров выше по сути описывает абстрактную фабрику с одним абстрактным методом. Рассмотрим случай, когда абстрактных методов несколько.\n\nПусть у нас есть люди, собаки и кошки. Выделим кое-что, что объединяет их всех — разделение пола на мужской и женский.\n```ts\ninterface ICreatureFactory {\n  createMale(): Male {}\n  createFemale(): Female {}\n}\n\nclass HumanFactory implements ICreatureFactory {\n  createMale(): Male {\n    return new HumanFemale();\n  }\n  \n  createFemale(): Female {\n    return new HumanFemale();\n  }\n}\n```\n\nПусть у нас есть набор инструментов: топор, кирка, лопата. Инструменты могут состоять из разных материалов, поэтому каждый из них должен быть абстрактным (чтобы нельзя было создать сущность без материала, то есть представлен либо абстрактным классом (если содержит реализации методов), либо интерфейсом.\n```ts\nabstract class Axe { /* ... */ }\nabstract class Pickaxe { /* ... */ }\nabstract class Shovel { /* ... */ }\n```\nПусть материалами будут камень и железо.\n```ts\nclass StoneAxe extends Axe { /* ... */ }\nclass StonePickaxe extends Pickaxe { /* ... */ }\nclass StoneShovel extends Shovel { /* ... */ }\nclass IronAxe extends Axe { /* ... */ }\nclass IronPickaxe extends Pickaxe { /* ... */ }\nclass IronShovel extends Shovel { /* ... */ }\n```\nСоздаём абстрактную фабрику, с помощью которой мы сможем создавать инструменты из нужного материала.\n```ts\ninterface IToolFactory {\n  createAxe(): Axe {}\n  createPickaxe(): Pickaxe {}\n  createShovel(): Shovel {}\n}\n```\nСоздаём фабрики, которые её реализуют.\n```ts\nclass StoneToolFactory implements IToolFactory {\n  createAxe(): Axe {\n    return new StoneAxe();\n  }\n  createPickaxe(): Pickaxe {\n    return new StonePickaxe();\n  }\n  createShovel(): Shovel {\n    return new StoneShovel();\n  }\n}\n\nclass IronToolFactory implements IToolFactory {\n  createAxe(): Axe {\n    return new IronAxe();\n  }\n  createPickaxe(): Pickaxe {\n    return new IronPickaxe();\n  }\n  createShovel(): Shovel {\n    return new IronShovel();\n  }\n}\n\nconst toolFactory = new StoneToolFactory();\n\nconst stoneTools = [\n  toolFactory.createAxe(),\n  toolFactory.createPickaxe(),\n  toolFactory.createShovel(),\n];\n```\nТакой подход очень расширяемый. Если в будущем мы захотим добавить новый материал для набора инструментов (например, сталь или золото), можно будет это очень просто реализовать созданием новой фабрики.\n\n### Строитель\n\n**Строитель** (Builder) — порождающий паттерн, предоставляющий возможность поэтапного (пошагового) создания составных объектов.\n\nБудем делать пиццу.  \nПусть пицца может быть разных размеров, на разном тесте и с разными ингредиентами.\n```ts\nenum Size { Small = 'Small', Medium = 'Medium', Large = 'Large' }\nenum DoughType { Thin = 'Thin', Thick = 'Thick' }\n\nenum CheeseType { Mozzarella = 'Mozzarella', Parmesan = 'Parmesan', Gouda = 'Gouda' , Cheddar = 'Cheddar' }\nenum HamType { Chicken = 'Chicken', Pork = 'Pork', Turkey = 'Turkey' }\nenum MushroomsType { Chanterelles = 'Chanterelles', Champignons = 'Champignons' }\nenum SauceType { BBQ = 'BBQ', Ketchup = 'Ketchup', Ranch = 'Ranch', Garlic = 'Garlic' }\nenum VegetableType { Tomato = 'Tomato', Pepper = 'Pepper' }\n\ntype Ingredient = CheeseType | HamType | MushroomsType | SauceType | VegetableType;\n\nclass Pizza {\n  size: Size;\n  dough: DoughType;\n  ingredients: Ingredient[];\n  constructor(size: Size, dough: DoughType) {\n    this.size = size;\n    this.dough = dough;\n    this.ingredients = [];\n  }\n  addIngredient(ingredient: Ingredient): void {\n    this.ingredients.push(ingredient);\n  }\n}\n```\nОписываем *абстрактного строителя* `PizzaBuilder`, который может создать любую пиццу.  \nЕго наследники будут переопределять метод `build`, в котором определяется последовательность добавления ингредиентов.\n```ts\nabstract class PizzaBuilder {\n  private pizza: Pizza;\n  constructor(size: Size, dough: DoughType) {\n    this.pizza = new Pizza(size, dough);\n  }\n  addCheese(cheese: CheeseType): void {\n    this.pizza.addIngredient(cheese);\n  };\n  addHam(ham: HamType): void {\n    this.pizza.addIngredient(ham);\n  };\n  addMushrooms(mushrooms: MushroomsType): void {\n    this.pizza.addIngredient(mushrooms);\n  };\n  addSauce(sauce: SauceType): void {\n    this.pizza.addIngredient(sauce);\n  };\n  addVegetable(vegetable: VegetableType): void {\n    this.pizza.addIngredient(vegetable);\n  };\n  abstract build(): void\n  async cook(time: number): Promise<Pizza> {\n    this.build();\n    await new Promise(resolve => setTimeout(resolve, time));\n    return this.pizza;\n  }\n}\n```\nОписываем *конкретного строителя* `MargheritaPizzaBuilder`, который может создать пиццу \"Маргариту\" (тонкое тесто, томаты, мацарелла, томатная паста).\n```ts\nclass MargheritaPizzaBuilder extends PizzaBuilder {\n  constructor(size: Size) {\n    super(size, DoughType.Thin);\n  }\n  build(): void {\n    this.addVegetable(VegetableType.Tomato);\n    this.addCheese(CheeseType.Mozzarella);\n    this.addSauce(SauceType.Ketchup);\n  }\n}\n```\nОписываем *директора*, который знает, как работать со строителем, чтобы получить готовую пиццу (нужно собрать пиццу и готовить её в течение 3 секунд).\n```ts\nclass Director {\n  async cookMargherita(size: Size): Promise<Pizza> {\n    const margheritaBuilder = new MargheritaPizzaBuilder(size);\n    margheritaBuilder.build();\n    const pizza = await margheritaBuilder.cook(3000);\n    console.log('Margherita is ready!');\n    return pizza;\n  }\n}\n\nconst director = new Director();\ndirector.cookMargherita(Size.Medium).then(pizza => console.log(pizza));\n```\n\nДля полноты картины рассмотрим ещё один небольшой пример: построение ландшафта. Здесь `add`-методы будут реализованы не в родительском абстрактном строителе, а в конкретном дочернем.\n```ts\nclass Landscape { /* ... */ }\n\nabstract class LandscapeBuilder {\n  private landscape: Landscape;\n  constructor() {\n    this.landscape = new Landscape();\n  }\n  abstract addTree(): void;\n  abstract addBush(): void;\n  abstract addRiver(): void;\n  render(): Landscape {\n    return this.landscape;\n  }\n}\n\nclass ForestLandscapeBuilder extends LandscapeBuilder {\n  addTree(): void { /* ... */ };\n  addBush(): void { /* ... */ };\n  addRiver(): void { /* ... */ };\n}\n\nclass ForestDirector {\n  buildForest() {\n    const builder = new ForestLandscapeBuilder();\n    builder.addBush();\n    builder.addTree();\n    builder.addTree();\n    return builder.render(); \n  }\n}\n\nconst director = new ForestDirector();\ndirector.buildForest();\n```\n\n### Прототип\n\n**Прототип** (Prototype) — порождающий паттерн, позволящий копировать объект любой сложности без привязки к его конкретному классу.\n\n```ts\ninterface Prototype<T> {\n  clone(): T\n}\n\nclass Article implements Prototype<Article> {\n  title: string;\n  date: Date;\n  constructor(title: string) {\n    this.title = title;\n    this.date = new Date();\n  }\n  clone(): this {\n    return Object.assign({}, this);\n  }\n}\n\nconst article = new Article('Prototype Pattern');\nconst copy = article.clone();\nconsole.log(article === copy); // false\n```\nЕсть [много способов клонирования объектов](https://github.com/Max-Starling/Notes/blob/master/JavaScript.md#клонирование-объектов) реализовать функцию `clone()`. Каждый из них имеет свои преимущества и недостатки по сравнению с остальными.  \n\n## Структурные\n\n## Поведенческие\n\n# Принципы проектирования\n\n## Поддерживаемый код\n\nНаличие **поддерживаемого кода** (maintainable code base) означает возможность применения максимума концептуальных изменений с минимальным изменением кода, то есть изменение одного блока кода должно оказывать минимально возможное влияние на другие блоки. В таком случае изменения делаются проще и быстрее, а баги появляются реже.\n\n**Связанность, зацепление** (coupling) отражает зависимость блоков кода друг от друга. Два блока кода **сильно связанны** (highly coupled), если изменения в одном блоке порождают изменения в другом, **слабо связанны** (loosely coupled), если один блок не зависит или почти не зависит от изменений другого. \n\n**Сплочёность, связность** (cohesion) отражает, насколько тесно связаны по смыслу функции одного модуля. **Низкая сплоченность** (low cohesion) характерна модулям, имеющим разные несвязнные обязанности, **высокая сплочёность** (high cohesion) — модулям, функции которых выполняют во многом похожие задачи.\n\n### Основные принципы поддерживаемого кода\n1) *Инкапсуляция* (encapsulation). Процесс сокрытия внутренних деталей реализации.  \n2) *Слабая связанность* (low coupling). Достигается за счёт выбора правильного интерфейса.  \n3) *Высокая сплочёность* (high cohesion).\n\nСлово \"**функциональный** (functional; блок кода, метод, класс и тд) отражает чисто техническую роль в приложении, оно не связано с предметной областью. Примеры функционального: слой (layer), фабрика (factory), View.\n\nСлово \"**концептуальный**\" (conceptual; блок кода, метод, класс и тд) отражает бизнес-роль в приложении, оно напрямую связано с предметной областью (domain). Примеры концептуального: User, Article, Comment, Like.\n\nОдна и та же вещь в зависимости от того, с какой стороны на неё смотреть, может быть функциональной и концептуальной одновременно.\n\n**Пакет** — сгруппированный набор классов.  \n**Модуль** — функциональный пакет.  \n**Компонента** — концептуальный пакет.\n\nОписанные принципы написания поддерживаемого кода часто применяются к отдельным классам, но также и к *пакетам* (*модулям*, *компонентам*). Это позволяет разрабатывать их максимально независимо друг от друга, разделяя обязанности между разными командами разработчиков. Выполнение принципов концептуально изолирует код, который в этом случае хорошо читается, легко тестируется и переиспользуется.\n\nВ *хорошо организованном проекте* (well-organised codebase) есть только одно конкретное место, где может лежать какой-то блок кода. \nМожно не знать точное местоположение, но есть только один логический путь, ведущий туда. Это позволяет избежать несогласованности данных, потерянного и дублированного кода, экономит время и нервы разработчиков.\n\n## Принципы пакетов\n\nГруппируя классы в пакеты по каким-то критериям (концептуально), мы можем рассуждать об организации проекта на высшем уровне абстракции, управляя отношениями между этими пакетами.\n\n### Принципы сплочёности пакетов (cohesion)\n* **Reuse-Release Equivalency (RRE)**. Пакеты должны выпускаться и версионироваться отдельно друг от друга, чтобы всегда можно было выбрать старую версию пакета, если новая не корректно работает.\n* **Common-Reuse Principle (CRP)**. Классы в пакете переиспользуются (reuse) вместе. Переиспользуешь один — переиспользуешь все.\n* **Common-Closure Principle (CCP)**. Классы, изменяющиеся вместе, должны быть упакованы (packeged) вместе. Чтобы в случае изменений изменялся только один пакет, не затрагивая другие.\n\n### Принципы связанности пакетов (coupling)\n* **Acyclic-Dependencies Principle (ADP)**. Циклы в графе пакетных зависимостей недопустимы.\n* **Stable-Dependencies Principle (SDP)**. Зависимости в направлении стабильности. Часто меняющиеся части системы должны зависеть от редко меняющихся (стабильных), но не наоборот. \n* **Stable-Abstractions Principle (SAP)**. Стабильные пакеты должны быть более абстрактными и содержать те классы и модули, которые можно расширять, а не те, которые должны изменяться. При изменении классов в пакете велика вероятность изменений их использования, а значит и изменений в зависимых пакетах.\n\n## Принцип разделения ответственности (SoC)\n\n**Принцип разделения ответственности** (Separation of Concerns, SoC) предполагает разделение приложения на функциональные блоки, как можно меньше перекрывающие функции друг друга.\n\nОбъектно-ориентированные языки разделяют ответственность между объектами, процедурные языки — между процедурами и функциями, микросервисная архитектура — между сервисами. Многие архитектуры стремятся отделить представление (Presentation) от бизнес-логики (Business Logic) приложения, некоторые архитектуры дополнительно разделяют бизнес-логику на предметную область (Domain) и на её конкретную реализацию. OOCSS разделяет структурные свойства (Structure) и свойства оформления (Skin). В сетевой модели OSI протокол HTTP отвечает за взаимодействие с пользователем — прикладной уровень (Application layer), за саму передачу данных отвечает протокол TCP — транспортный уровень (Transport  layer). \n\n## Принцип инверсии управления (IoC)\n\n**Принцип инверсии управления** (Inversion of Control, IoC) предполагает передачу, дилегирование управления из одного места в другое, что позволяет достичь слабой связанности (loose coupling) кода.\n\nПример из жизни: человек может приготовить еду сам, а может просто заказать её, тогда ему не нужно заботиться о процессе приготовления пищи.  \n\nПуть есть два класса: `Employee`, отвечающий за рабочих, и `Salary`, отвечающий за заработную плату. \nПусть также каждый новый рабочий по умолчанию получает `500$`.\n```ts\nclass Employee {\n  salary: Salary;\n\n  constructor() {\n    this.salary = new Salary(500, '$');\n  }\n\n  getSalary() {\n    return this.salary.toString();\n  }\n}\n\nclass Salary {\n  value: number;\n  currency: string;\n\n  constructor(value: number, currency: string) {\n    this.value = value;\n    this.currency = currency;\n  }\n\n  toString() {\n    return `${this.value}${this.currency}`;\n  }\n}\n\nconst emp: Employee = new Employee();\nconsole.log(emp.getSalary());\n```\nКласс `Employee` напрямую создаёт объект класса `Salary` в своём конструкторе, что делает их сильно связанными. Изменения в `Salary` влекут за собой непосредственные изменения в `Employee`.\n\nБолее того, мы не можем изолированно протестировать класс `Employee`, поскольку не можем заменить создание объекта класса `Salary` заглушкой (stub) или заменить класс `Salary` макетом (mock).\n\nСделаем фабрику, которая будет создавать Salary-объекты.\n```ts\nconst SalaryFactory = (value: number, currency: string): Salary => new Salary(value, currency);\n```\nи заменим строку `this.salary = new Salary(500, '$')` на `this.salary = SalaryFactory(500, '$')`.\n\nРезультат выполнения кода тот же, но мы ослабили связь между классами `Employee` и `Salary`, передав контроль над созданием объектов фабрике. Теперь, если класс `Salary` изменится, мы сможем оградить `Employee` от изменений, внеся изменения только в фабрику.\n\nНапример, конструктор класса `Salary` должен теперь принимать один параметр вместо двух: `new Salary('500$')`, тогда фабрика изменится следующим образом\n```ts\nconst SalaryFactory = (value: number, currency: string): Salary => new Salary(`${value}${currency}`);\n```\n\n<!-- Также мы теперь можем протестировать `Employee` независимо от `Salary` при помощи следующей заглушки:\n```ts\nconst SalaryFactory = (): object => ({ value: 500, currency: '$', toString() { return this.value + this.currency; } });\n```-->\n\n## Основные принципы ООП\n\n### Инкапсуляция\n\n**Инкапсуляция** скрывает те вещи, которые не должны быть получены или изменены извне. Обычно это детали реализации.\n\nПростой пример реализации инкапсуляции на классах при помощи модификаторов доступа.\n```ts\nclass BlackBox {\n  private state: string = 'off';\n\n  start(): void {\n    this.state = 'on';\n    console.log('Something started...');\n  }\n}\n\nconst box: BlackBox  = new BlackBox();\nbox.start();\nconsole.log(box.state); // Error: Property 'state' is private and only accessible within class 'BlackBox'.\n```\nСвойство `state` инкапсулировано при помощи модификатора доступа `private` и до него нельзя достучаться снаружи, поскольку будет ошибка. Аналогично можно инкапсулировать методы класса. \n\nПо умолчанию исползуется модификатор доступа `public`, который явно не пишется. У метода `start` установлен этот модификатор.\n\nМодификатор доступа `private` не позволяет дочерним классам использовать переменные и методы родительского класса, что позволяет сокрыть некоторые его детали реализации. Если же нужно открыть доступ дочерним классам, используется модификатор `protected`. \n```ts\nclass BlackBox {\n  protected state: string = 'off';\n\n  start(): void {\n    this.state = 'on';\n    console.log('Something started...');\n  }\n}\n\nclass WhiteBox extends BlackBox {\n  logState(): void {\n    console.log(this.state);\n  }\n}\n\nconst box: WhiteBox = new WhiteBox();\nbox.logState(); // off\nbox.start();\nbox.logState(); // on\n```\nКак видно из примера выше, дочерний класс, имеющий доступ к скрытым деталям родительского, может их выставить на всеобщее обозрение. Поэтому с модификатором доступа `protected` нужно быть осторожным.\n\nИнкапсуляция на примере функций в JavaScript выглядит ещё проще. Каждая функция по умолчанию создаёт новое лексическое окружение. Все переменные `var`, `let`, `const`, объяслвенные в ней, недоступны снаружи. Доступен лишь результат выполнения функции.\n```js\nconst fn = () => {\n  var a = 1;\n  let b = 2;\n  return a + b;\n};\n\nconsole.log(a); // ReferenceError: a is not defined\nconsole.log(b); // ReferenceError: a is not defined\nconsole.log(fn()); // 3\n```\n\nЖизненный пример инкапсуляции:  \nВентилятор. Его интерфейс представлен несколькими кнопками: вкл/выкл и иногда регулировка мощности. Пользователь просто жмёт на кнопки, а внутри вентилятора проиходят процессы, скрытые от глаз пользователя. В частности, работа редуктора и электродвигателя.\n\n### Наследование\n\n**Наследование** позволяет описать *новый* класс на *основе* уже *существующего*.\n\n*Существующий* класс в этом случае называют **базовым** (base), **родительским** (parant), **суперклассом** (superclass).\n*Новый* класс — **производным** (derived), **дочерним** (child), **подклассом** (subclass).\n\n*Наследование* — включение поведения и состояния родитеского класса в дочерний. **Поведение** представлено *методами*, **состояние** — *свойствами*.\n\nДочерний класс включает в себя свойства и методы родительского класса, которые он может доопределять, переопределять или же дополнять своими.\n\n*Наследование* — один из способов *повторного использования кода*. Оно влечёт за собой *сильную связанность* кода: если нужно *изменить класс*, то также придётся *изменить* его *дочерние классы*.\n\nНиже представлены класс `Point`, описывающий точку с координатами `(x, y)`, и его дочерний класс `ColorfullPoint`, представляющий точку какого-то цвета.\n```ts\nclass Point {\n  x: number = 0;\n  y: number = 0;\n  \n  constructor(x: number, y: number) {\n    this.x = x;\n    this.y = y;\n  }\n\n  setX(x: number) {\n    this.x = x;\n  }\n\n  setY(y: number) {\n    this.y = y;\n  }\n}\n\nclass ColorfullPoint extends Point {\n  color: '#000';\n\n  constructor(x: number, y: number, color: string) {\n    super(x, y);\n    this.color = color;\n  }\n}\n```\n\n### Полиморфизм\n\n**Полиморфизм** — способность предоставлять один и тот же интерфейс для различных реализаций.\n\nВ программировании есть 3 типа полиморфизма:\n* *Ad-hoc*\n* *Параметрический*\n* *Полиморфизм подтипов*\n\nВ ООП *полиморфизм подтипов* называется **полиморфизмом**, *параметрический полиморфизм* — **обобщённым программированием**. \nПонятия *Ad-hoc-полиморфизм* в рамках ООП нет.\n\n**Ad-hoc-полиморфизм** поддерживается посредством перегрузки (overload) функций, операторов и методов, в слаботипизированных языках - неявное приведение типов.\n\n* Перегрузка операторов в C++.  \n\nЗадана структура `Point`, описывающая точку на плоскости `(x, y)`.  \nВ примере перегружаются операторы сложения и вывода для структуры `Point`.\n```cpp\n#include <iostream>\nusing namespace std;\n\ntypedef struct point {\n  int x;\n  int y;\n\n  point(int x0 = 0, int y0 = 0) {\n    x = x0;\n    y = y0;\n  }\n} Point;\n\n/* перегрузка оператора вывода */\nostream& operator << (ostream &stream, const Point &p) {\n  return stream << \"(\" << p.x << \", \" << p.y << \")\";\n}\n\n/* перегрузка оператора сложения */\nPoint operator + (const Point &A, const Point &B) {\n  return Point (A.x + B.x, A.y + B.y);\n}\n\nint main() {\n  Point A(1, 2);\n  Point B(3, 4);\n  cout << A + B; /* выведет (4, 6) */\n  return 0;\n}\n```\n\n* Перегрузка функций и методов в TypeScript.\n\nЗадан класс `Point`, описывающий точку на координатной прямой `(x)`.  \nВ примере перегружены методы и функции, связанные со сложением точек.\n```ts\n/* перегрузка функции */\nfunction sum(A: Point, B: Point): Point;\nfunction sum(A: Point, B: number): Point;\nfunction sum(A: any, B: any): Point {\n    return new Point(A + B);\n}\n\nclass Point {\n  x = 0;\n  constructor(x: number) {\n    this.x = x;\n  }\n  valueOf() { return this.x; }\n  /* перегрузка методов */\n  add(point: number): void;\n  add(point: Point): void;\n  add(point: any): void {\n    this.x += point;\n  }\n}\n\nconst A = new Point(1);\nA.add(10);\nconsole.log(A); // // Point { x: 11 }\nconsole.log(sum(A, 6)); // Point { x: 17 }\n\nconst B = new Point(20);\nconsole.log(sum(A, B)); // Point { x: 31 }\n```\n\n* Неявное приведение типов в JavaScript.\n```js\n1 + \"7\" // 17\n1 + true // 2\ntrue == 'true' // false\n```\n\n**Параметрический полиморфизм** позволяет определять функцию или тип данных обобщённо (generically), чтобы их значения обрабатывались идентично вне зависимости от их типа.\n\n```ts\nfunction valueOf<T>(obj: T): any {\n  return obj.valueOf();\n}\n\n/* экземпляр класса Point из примеров выше */\nconst A = new Point(17);\nconsole.log(valueOf<Point>(A));\n```\n```ts\ninterface Item<T> {\n  field: T;\n  method(prop: T) => T; \n}\n\nclass NumberItem extends Item<number> {\n  field: number;\n  method(prop: number) => prop + 1;\n}\n\n```\n```ts\ntype Props = { /* ... */ };\ntype State = { /* ... */ };\n\nclass MyComponent extends React.Component<Props, State> { /* ... */ }\n```\n\n**Полиморфизм подтипов**, **полиморфизм включения** — свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.\n```ts\nabstract class Shape {\n  abstract draw(): void;\n}\n\nclass Triangle extends Shape {\n  draw(): void {\n    console.log('drawing a triangle');\n    /* ... */\n  }\n}\n\nclass Circle extends Shape {\n  draw(): void {\n    console.log('drawing a circle');\n    /* ... */\n  }\n}\n\nconst shapes: Shape[] = [new Triangle(), new Circle()];\nshapes.forEach(shape => shape.draw());\n```\n\n## Основные термины ООП\n\n### Ассоциация\n\n**Ассоциация** (Association) — связь, отношение между двумя объектами. Слова «один-к-одному» (one-to-one), «один-ко-многим» (one-to-many), «многие-к-одному» (many-to-one), «многие-ко-многим» (many-to-many) определяют связь между объектами.\n\nПримеры:\n* У человека только одна жизнь (один-к-одному).\n* В библиотеке много книг (один-ко-многим).\n* Несколько книг могут принадлежать одному писателю. (многие-к-одному)\n* Здесь есть десятки ресторанов, в каждом из которых есть по-своему интересные блюда (многие-ко многим).\n\n### Агрегация\n\n**Агрегация** (Aggregation) является формой ассоциации, когда один объект \"имеет\" (отношение \"has-a\") имеет ссылку на другой объект. Содержащий объект иногда называют **контейнером**.\n\nАгрегация не подразумевает владения: оба объекта могут существовать независимо друг от друга. Уничтожение контейнера не означает уничтожение содержащегося в нём объекта.\n```js\nclass Book {\n  constructor(name, author) {\n    this.name = name;\n    this.author = author;\n  }\n}\n\n/* книги созданы вне класса Library, поэтому они не зависят\nот его экземпляров */\nconst books = [\n  new Book({ name: 'Martin Eden', author: 'Jack London' });\n  new Book({ name: '1984', author: 'George Orwell' });\n];\n\nclass Library {\n  books = [];\n  /* одним из способов передачи книг при агрегации является\n  передача через конструктор */\n  constructor(books) {\n    this.books = books;\n  }\n}\n\nlet library = new Library(books);\nlibrary = null; // уничтожение экземпляра класса Library \nconsole.log(books); // книги продолжают существовать\n```\n\n*Агрегация* на примере *React*. *Компоненту* можно представить как *класс* (функцию), *блоки* можно представить как *объекты*, *экземпляры класса* (константы).  \n```jsx\n/* компонента (класс, функция) */\nconst Text = ({ title }) => (<span>{title}</span>);\n\n/* блок (экземпляр класса, константа) */\nconst helloBlock = (<Text title=\"Hello\" />);\n\nconst TextWrapper = () => (\n  <div>{helloBlock}</div>\n);\n```\nБлок `helloBlock` создаётся вне компонента `TextWrapper`, поэтому в случае создания и уничтожения блока `<TextWrapper />`, `helloBlock` не уничтожится вместе с ним (он может быть удалён сборщиком мусора, если не будет использован в других местах, но всё же возможность его использования имеется).\n\n### Композиция\n\n**Композиция** (Composition) является *более строгой формой агрегации*, при которой *содержащийся объект не может существовать без своего контейнера*: *уничтожается вместе с контейнером*.\n\n```js\nclass Book {\n  constructor(name, author)\n}\n\nclass Library {\n  books = [];\n  constructor() {\n    /* книги созданы в конструкторе, поэтому при уничтожении\n    объекта они не смогут больше использоваться и будут удалены */\n    this.books = [\n      new Book({ name: 'Martin Eden', author: 'Jack London' });\n      new Book({ name: '1984', author: 'George Orwell' });\n    ];\n  }\n}\n\nlet library = new Library();\nlibrary = null; // книги удаляются и к ним нет доступа\n```\n\nПримеры композиции:\n* Квартира не может существовать без дома.\n* Работник не может существовать без места работы.\n\nПример использования композиции в React.\n```jsx\n/* Экземпляр компонента Title создаётся внутри Article. */\nconst Article = ({ title }) => (<Title>{title}</Title>);\n\nclass App {\n  state = {\n    isShown: false,\n  };\n\n  render() {\n    return (\n      <div>\n        { this.state.isShown\n          ? <Article title=\"notes\" />\n          : null }\n      </div>\n    );\n  }\n};\n\nReactDOM.render(<App />, document.getElementById('root'));\n```\n```js\nthis.setState({ isShown: true });\n/* Когда экземпляр компонента Article создаётся,\nэкземляр компонента Title создаётся вместе с ним. */\n\nthis.setState({ isShown: false });\n/* Когда экземпляр компонента Article уничтожается (unmount),\nэкземляр компонента Title уничтожается вместе с ним. */\n```\n\n### Обобщение, Специализация и Наследование\n\n**Обобщение** (Generalization) — процесс извлечения общих характеристик (shared characteristics) из нескольких похожих классов и размещения этих характеристик в **обобщённом суперклассе** (generalized/generic/general superclass). Похожие классы становятся его подклассами (subclasses). \n\n*Общими характеристиками* могут выступать *атрибуты*, *методы* и *ассоциации*.\n\nОбщие характеристики перечислены только в суперклассе, но они также применяются и к его подклассам.\n\n**Наследование** (Inheritance) — механизм, передающий характеристики суперкласса его подклассам. Подклассы **наследуют** характеристики свеого суперкласса.\n\nПримет *обобщения*: собака и лошадь являются домашними животными, самолёт и машина являются транспортными средствами.\n\n*Обобщение* использует *отношение \"is-a\" (является)*.\n\n*Horse is an Domestic Animal, Dog is an Domestic Animal* (класс `DomesticAnimal` — *обобщённый суперкласс*, `Horse`, `Dog` — его *подклассы*).\n```js\nclass DomesticAnimal {}\nclass Horse extends DomesticAnimal {}\nclass Dog extends DomesticAnimal {}\n```\n\n**Специализация** (Specialization) — *процесс создания новых подклассов из* уже *существующего*. \n\n*Специализация используется*, если *определённые характеристики* (атрибуты, методы и ассоциации) *применяются только* к *некоторым* *объектам класса*. В таком случае *может быть создан специальный подкласс* (special/specific subclass).\n\n*Специализация не* всегда *означает*, что в *подклассе* должны быть *поля*, которых *нет* в *суперклассе*. *Фиксация конкретных полей суперкласса* в *дочернем подклассе* также *является специализацией*.\n\n*Специализация* может *достигаться* за счёт *наследования* и *композиции*.\n\nПример *специализации* за счёт *композиции* в *React*.\n```jsx\n/* два параметра */\nconst Mesage = ({ title, text }) => (\n  <div>\n    <p>{title}</p>\n    <p>{text}</p>\n  </div>\n);\n\n/* один параметр фиксирован */\nconst WelcomeMessage = ({ text }) => (\n  <Message title=\"Welcome\" text={text} />\n);\n```\n\n### Реализация\n\n**Реализация** (Realization) — связь между классом и каким-то объектом, содержащим сведения о том, что должно быть реализовано в классе. Этот объект обычно называют **интерфейсом** (Interface).\n```ts\n/* пример на typescript */\ninterface IArticle {\n  title: string;\n  getTitle: () => string;\n}\n\nclass Article implements IArticle {\n  title: string = '';\n  getTitle(): string {\n    return this.title;\n  }\n}\n```\n\nГоворят, что *класс реализует* (implements) *интерфейс*.\n\n## Зависимость\n\n**Зависимость** (Dependency) — связь между двумя классами, при которой изменение структуры или поведения одного класса влияет на другой класс. \n\nЗависимость работает только в одну сторону. \n\nЗависимым является класс, использующий другой класс.\n \n### Абстракция\n\n**Абстрагировать** объект означает отбросить его маловажные, несущественные детали, чтобы выделить самую важную часть. Что войдёт в \"самую важную часть\", зависит от решаемой задачи.\n\n**Абстракция** — использование только тех характеристик объекта (свойств и методов), которые с необходимой точностью представляют его данные в системе.\n\n*Абстракция* позволяет работать с объектами, не вдаваясь в особенности их реализации.\n\nУ всего есть **уровни абстракции**.\n\nПытаясь что-то описать, мы выбираем какой-то уровень абстракции.\nНапример, мы можем описать птицу следующим образом.\n```ts\nclass Bird {\n  age: number;\n  size: number;\n  name: string;\n}\n```\nСейчас мы создали некоторую абстракцию, но также можем добавить некоторые другие детали, чтобы выйти на новый уровень абстракции.\n```ts\nclass Bird {\n  age: number;\n  size: number;\n  name: string;\n  eat(): void { /* ... */ };\n  sleep(): void { /* ... */ };\n}\n```\nХорошо подумав, мы решаем, что одного класса для нашего приложения мало: необходимы более конкретные виды птиц (чайка, лебедь). Помечаем класс `Bird` как абстрактный, чтобы нельзя было создавать экземпляры \"неконкретных\" птиц (`new Bird()`), а также помечаем некоторые его методы (в приложении голубь и лебедь могут спать и есть по-разному, пусть в них это и определяется).\n```ts\nabstract class Bird {\n  age: number;\n  size: number;\n  name: string;\n  abstract eat(): void;\n  abstract sleep(): void;\n}\n\nclass Gull extends Bird {\n  eat(): void {\n    console.log('eating a fish');\n  }\n\n  sleep(): void {\n    console.log('sleep on the mountain');\n  }\n}\n\nclass Dove extends Bird { /* ... */ }\n```\nЗатем мы решаем, что есть плавающие и летающий птицы.\n```ts\nabstract class Bird { /* ... */ }\n\nabstract class SwimmingBird extends Bird {\n  abstract swim():void;\n}\n\nabstract class FlyingBird extends Bird {\n  abstract fly():void;\n}\n```\nПоскольку в TypeScript множественное наследование запрещено, а в абстрактных классах у нас нет реализованных методов, используем интерфейсы.\n```ts\ninterface IBird {\n  age: number;\n  size: number;\n  name: string;\n  eat: () => void;\n  sleep: () => void;\n}\n\ninterface ISwimmingBird extends IBird {\n  swim: () => void;\n}\n\ninterface IFlyingBird extends IBird {\n  fly: () => void;\n}\n\nclass Gull implements ISwimmingBird, IFlyingBird { /* ... */ }\nclass Starling implements IFlyingBird { /* ... */ }\nclass Chicken implements IBird { /* ... */ }\n```\nМожно продолжать детализировать и дальше, если этого требует приложение.\n\n\n## CRP: Композиционный принцип повторного использования\n\n**Composite Reuse Principle** (CRP) гласит, что *классам* следует *достигать полиморфного поведения* и *переиспользуемости кода* за счёт *композиции* классов, а *не наследования*.\n\n### Способ реализации CRP\nСоздаётся множество интерфейсов, описывающих поведение системы. Классы выборочно реализуют эти интерфейсы, формуруя таким образом доменные модели. \n\nПри таком подходе *интерфейсы помогают добиться полиморного поведения*.\n\nЕсли нужно создать класс, похожий на уже существующий, но в то же время имеющий некоторые изменения, можно просто создать новый класс, по-своему реализующий тот же интерфейс. \n\nВероятно, при использовани композиции часть кода будет дублироваться, но он будет менее связанным, а значит более поддерживаемым.\n\n### Преимущества композиции над наследованием\n* **Переиспользуемость кода**. Множественное наследование запрещено во множестве языков программирования из-за ромбовидной проблемы (diamond problem): два родительских класса могут иметь метод с одинаковым названием и не известно, какой из них нужно унаследовать. По этой причине есть возможность наследоваться только от одного класса. Композицию же можно использовать неограниченное количество раз.\n* **Тестирование**. Композицию можно просто подменить тестовым объектом (заглушкой, реализующей интерфейс), наследование требует использование родительского класса, заменить который нельзя и придётся напрямую его импортировать.\n* **Инкапсуляция**. Наследование нарушает принцип инкапсуляции, так как дочерний класс зависит от поведения своего родительского класса и изменение последнего повлечёт за собой непосредственное изменение дочернего класса. Модификаторы доступа частично решают эту проблему.\n\n## Принципы SOLID\n\nПринципы SOLID были разработаны Робертом С. Мартином.\n\n* **Single Responsibility Principle** (SRP, Принцип единственной ответственности).\n* **Open-Closed Principle** (OCP, Принцип открытости-закрытости).\n* **Liskov Substitution Principle** (LSP, Принцип подстановки Барбары Лисков).\n* **Interface Segregation Principle** (ISP, Принцип разделения интерфейса).\n* **Dependency Inversion Principle** (DIP, Принцип инверсии зависимостей).\n\n### SRP: Принцип единственной ответственности\n\nДолжна быть лишь одна причина изменить класс (функцию, метод), то есть класс должен быть ответственен лишь за что-то одно. Если у класса несколько причин для изменения, то он отвечает за несколько задач.\n\nЕсли класс отвечает за решение нескольких задач, его подсистемы, реализующие решение этих задач, оказываются сильно связанны друг с другом. Изменения в одной такой подсистеме ведут к изменениям в другой.\n\nПусть есть массив каких-то объектов и функция поиска объекта в этом массиве по `id`.\n```js\nimport _ from 'lodash';\n\nconst items = [{\n  id: '1',\n  name: 'n',\n  description: 'd',\n}, /* ... */];\n\nconst findItem = (id) => {\n  /* находим элемент */\n  const item = items.find(item => item.id === id);\n  /* убираем ненужные поля */\n  return _.omit(item, ['description']);\n}\n\nconst item = findItem('1');\n```\nФункция `findItem` нарушает принцип единственной ответственности, поскольку совершает две операции: поиск элемента и удаление ненужных полей из найденного объекта. Если изменятся поля, которые не нужно возвращать, то изменится и функция `findItem`. Если изменится поле `id` (например, `_id`, `username`), то `findItem` снова изменится. Имеем две причины для изменения. Такой код сильно связан и его сложно переиспользовать. \nРазбиение функции `findItem` на две сделает код более переиспользуемым и менее связанным. Пример можно применить и к классу, создав класс `Items` с методами `findItem` и `omitFields`.\n```js\nconst findItem = id => items.find(item => item.id === id);\nconst mapItem = (item, fields) => _.omit(item, fields);\n\nconst searchResult = findItem('1');\nconst item = mapItem(searchResult, ['description']);\n```\n\nРассмотрим более неоднозначный пример. Допустим, мы отправляем HTTP-запрос на создание пользователя к серверу. Часто такой запрос сперва создаёт пользователя, а затем возвращает его. То есть по сути совмещает в себе две операции: создание и получение пользователя. Несмотря на то, что это является оптимизацией (один запрос к серверу вместо двух), это также является нарушением принципа единственной ответственности. Такую логику можно разбить на Команду (Comand), которая совершает действие (создаёт пользователя) и ничего не возвращает, и Запрос (Query), который получает пользователя из базы данных и возвращает его. Правда, чаще всего выбирают оптимизацию.\n```js\nconst createUser = async (req, res) => {\n  try {\n    const user = await userRepository.create(req.body);\n    return user;\n  } catch (e) {\n    res.send(404);\n  }\n};\n```\nСуществуют и исключения из правила. Например, структура данных Стек имеет функцию `pop()`, которая удаляет элемент и возвращает его. Такое поведение функции общепринято и считается ожидаемым.\n\nМинусом подхода является увеличение количества классов (функций, методов), которые хоть и делают код более переиспользуемым, но усложняют его в целом.\n\n### OCP: Принцип открытости-закрытости\n\nПрограммные сущности (классы, модули, функции) должны быть открыты для расширения (extension) и закрыты для модификации (modification).\n\nЭто довольно сложный в реализации принцип.  \nЕго идея заключается в том, что следует код, который будет достаточно гибким для изменений в будущем. Новый функционал должен вносить минимум изменений в уже существующий. Но обычно новый функционал не всегда просто предсказать. \n\nПытаясь продумать многие случаи использования наперёд, разработчик часто создаёт такие вещи, которые в данный момент ему не нужны, а в будущем могут даже и не понадобиться. При этом они увеличивают сложность кода, требуют время на написание и поддержку. Нелегко найти баланс между написанием гибкого кода и написанием ненужного кода, поэтому у принципа открытости-замкнутости есть противоположный принцип - принцип YAGNI.\n\nПусть нужно необходимо написать класс, описывающий личного ассистента с возможностью приветствия.  \nСоздаём класс `Assistant` с методом для приветствия `sayHi`.\n```ts\nclass Assistant {\n  sayHi(): void {\n    console.log('Hi!');\n  }\n}\n```\nПока сложно предсказать, как изменится класс. Можно только догадываться.\n\nТребования изменились и нужно написать функциональность для прощания `sayGoodbye`.\n```ts\nclass Assistant {\n  sayHi(): void {\n    console.log('- Hi! Nice to meet you.');\n  }\n  sayGoodbye(): void {\n    console.log('- Bye! See you later.');\n  }\n}\n```\nЧасть логики повторилась и теперь понятно, что в будущем могут появиться новые методы (например, поблагодарить).\nСделаем класс более гибким для дальнейшего расширения.\n```ts\nclass Assistant {\n  say(phrase): void {\n    if (phrase === 'hi') {\n      console.log('- Hi! Nice to meet you.');\n    } else if (phrase === 'bye') {\n      console.log('- Bye! See you later.');\n    }\n  }\n}\n```\nТеперь на каждую новую фразу не создаётся новый метод: есть один метод, который обрабатывает все фразы. Но с появлением новых фраз метод будет бесконечно изменяться и расти (новые `else-if` блоки), а следовательно и класс. Более того сами фразы могут измениться в будущем или же разные экземпляры класса `Assistant` захотят произносить разные фразы.\n\nУпрощаем метод `say`, вынося фразы за его пределы.\n```ts\nconst SARA_PHRASES = {\n  hi: 'Hi! Nice to meet you.',\n  bye: 'Bye! See you later.',\n};\n\nclass Assistant {\n  say(phrase: string): void {\n    console.log(`- ${phrase}`);\n  }\n}\n\nconst Sara: Assistant = new Assistant();\nSara.say(SARA_PHRASES.hi);\n```\n\nПусть помимо слов в привествии, появляется дополнительное действие (например, похмахать рукой), тогда можно создать новый метод, не затрагивая старый код.\n```ts\ngreat(phrase: string): void {\n  this.say(phrase);\n  waveHand();\n}\n```\n\nУдалось сделать достаточно гибкий к изменениям класс. Eсли в дальнейшем понадобится дополнительно добавить локализацию, заменить фразу, считать фразы из файла или получать их по запросу, потребуется минимум изменений в уже написанном коде.\n\n\n### LSP: Принцип подстановки Барбары Лисков\n\n**Принцип Барбары Лисков**.\n> Пусть `q(x)` — свойство, справедливое для любых объектов `x`, принадлежащих типу `T`, тогда свойство `q(y)` должно быть также справедливо для любых объектов `y`, принадлежащих типу `S`, являющимся подтипом типа `T`.\n\n```math\nq(x) = true ∀x ∈ T => q(y) = true ∀y ∈ S, ∀S ⊂ T\n```\n\n*Роберт Мартин переформулировал принцип Барбары Лисков* следующим образом.\n> Функции, использующие базовый тип, должны иметь возможность использовать его подтипы, не зная об этом.\n\nРассмотрим пример с *птицами*. Есть *класс* (базовый тип) `Bird` и его *дочерний класс* (подтип) `Chicken`.\n```ts\nimport { loadAnimation, showAnimation } from '/* ... */';\n\nconst animateFlight = (bird: Bird) => {\n  /* подгружается анимация полёта птицы и отображается */\n  const animation = loadAnimation(bird.constructor.name);\n  showAnimation(animation);\n};\n\nclass Bird {\n  makeSound(): void {};\n  fly(): void {\n    animateFlight(this);\n  }\n}\n\nclass Chicken extends Bird {\n  constructor() { \n    super();\n  }\n  \n  /* переопределение метода родительского класса */\n  makeSound(): void {\n    console.log('cackle');\n  }\n}\n```\nФункция `animateFlight(bird: Bird)` использует базовый тип `Bird`. Опираясь на высказывание Роберта Мартина, функция `animateFlight` не должна знать о том, что использует класс `Chicken` вместо `Bird`, то есть в ней не должно быть проверки принадлежности к конкретному классу (например, `if (bird instanceof Chicken)`).\n\nКласс `Bird` имеет метод `fly` (летать), `Chiken` наследует этот метод. Тем не менее, пример ниже выдаст ошибку.\n```ts\nanimateFlight(new Bird());\nanimateFlight(new Chicken()); // ошибка\n```\n*Курица не умеет летать* и функция `animateFlight()` *не имеет* подходящей *анимации* для её *полёта*. Такие случаи придётся *обрабатывать отдельно* для *нелетающих птиц*. То есть мы не можем использовать функцию `animateFlight()` с `Chicken`, но можем использовать её с родительским классом `Bird`. Это свидетельствует о *плохо подобранной абстракции*, *нарушающей принцип Барбары Лисков*.\n\nЧтобы принцип *соблюдался*, следует убрать метод `fly()` из класса `Bird` (поскольку не всем птицам характерно летать). \n\nОдним из способов это сделать является перенос метода `fly()` из `Bird` в новый подкласса `FlyingBird`. В этом случае используется принцип разделения интерфейса. \n```ts\nclass Bird {\n  makeSound(): void {};\n}\n\nclass Chicken extends Bird {\n  makeSound(): vold { /* ... */ }\n}\n\nclass FlyingBird extends Bird {\n  fly(): void { /* ... */ }\n}\n```\nПоскольку полёт теперь не является обязательным поведением птицы `Bird`, но является для `FlyingBird`, меняем также тип параметра у функции `animateFlight()`. \n```ts\nconst animateFlight = (bird: FlyingBird) => { /* ... */ };\n\nclass Dove extends FlyingBird {\n  makeSound(): vold { /* ... */ }\n}\n\nanimateFlight(new FlyingBird());\nanimateFlight(new Dove());\n```\nФункция `animateFlight` не может работать с `Chiken`, но она не может работать и с `Bird`: принцип Барбары Лисков не нарушен. \n\n<!--Вообще говоря, класс `Bird` следовало бы сделать абстрактным вместе с его методами, поскольку в подобранной абстракции рассматриваются конкретные виды птиц (голубь, курица и так далее). Более того, и спользуется функция с анимацией полёта и трудно представить полёт какой-то абстрактной птицы. Но в случае абстрактного класса `Bird` невозможно было бы создать экземпляр `new Bird()` и передать -->\n\n<!-- не существует неконкретной птицы: каждая птица имеет название, внешний вид, способы передвижения. Невозможно также представить анимацию полёта `Bird` (в примере мы предполагаем, что она задана). Но в случае абстрактного класса `Bird `невозможно было бы создать его экземпляр `new Bird()`, а следовательно и заменить его на `new Chicken()`. -->\n\n### ISP: Принцип разделения интерфейса\n\nНужно создавать узкоспециализированные интерфейсы, предназначенные для конкретных целей.  \n\nКлиент не должен зависеть от тех элементов интерфейса, которые он не использует.\n\nНапример, имеем следующий интерфейс.\n```ts\ninterface IPerson {\n  sleep: () => void;\n  watchFilm: () => void;\n  visitMeeting: () => void;\n  writeCode: () => void;\n  testCode: () => void;\n}\n```\nДопустим, нужно написать класс `Worker`, который описывает программиста и реализует интерфейс `IPerson`. В этом случае он также реализует методы `sleep()` и `watchFilm()`, хоть они ему и не нужны. Получается, что имеем лишний код, который увеличивает сложность приложения.\n```ts\nclass Worker implements IPerson {\n  visitMeeting() { /* ... */ }\n  writeCode() { /* ... */ }\n  testCode() { /* ... */ }\n  \n  watchFilm() {\n    throw new UnsupportedActionError();\n  }\n  sleep() {\n    throw new UnsupportedActionError();\n  }\n}\n```\nИнтерфейс `IPerson` выше называют жирным (fat) или загрязнённым (polluted). Такие интерфейсы по возможности следует разбивать на несколько. Разбиение проводится до тех пор, пока интерфейсы не будут содержать только действительно необходимые им методы.\n```ts\ninterface IPerson {\n  sleep: () => void;\n  watchFilm: () => void;\n}\n\ninterface IWorker {\n  visitMeeting: () => void;\n  writeCode: () => void;\n  testCode: () => void;\n}\n```\n\nНедостатком принципа считается рост количества интерфейсов. Их может стать слишком много.\n\n### DIP: Принцип инверсии зависимостей\n\nЗависимость должна идти в сторону абстракции, а не чего-то конкретного.\n\nАбстракции не должны зависеть от деталей.  \nДетали должны зависеть от абстракций.\n\nМодули верхних уровней (high-level modules) не должны зависеть от модулей нижних уровней (low-level modules).  \nОба типа модулей должны зависеть от абстракций.\n\nПусть у нас есть класс `Service` (модуль верхнего уровня), который использует класс `Repository` (модуль нижнего уровня) и, соответственно, зависит от него. Аналогично, класс `Repository` использует класс-сущность `User`.\n```ts\nclass UserService {\n  repo: UserRepository;\n  constructor () {\n    this.repo = UserRepositoryFactory();\n  }\n}\n\nconst UserRepositoryFactory = (): UserRepository => new UserRepository();\n\nclass User {\n  id: string;\n  name: string;\n  constructor (id: string, name: string) {\n    this.id = id;\n    this.name = name;\n  }\n}\n\nclass UserRepository {\n  constructor () {}\n  getUserById(id: string): User { /* ... */ }\n}\n```\nВ примере выше использован паттерн проектирования \"Фабрика\", чтобы вынести логику создания объектов из сервиса (Inversion of Control).\n\nВыделим абстракцию, определив интерфейсы.\n```js\ninterface IUser {\n  id: string;\n  name: string;\n}\n\ninterface IUserRepository {\n  getUserById(id: string): IUser\n}\n```\nЗаменяем в `UserService` строчку `repo: UserRepository` на `repo: IUserRepository` и в `UserRepository` строчку `getUserById(id: string): User` на `getUserById(id: string): IUser`. Теперь модули верхних уровней не зависят от модулей нижних уровней, а зависят от интерфейсов, то есть абстракции.\n\nЗаменим также строчку `class User {}` на `class User implements IUser {}`,  строчку `class UserRepository {}` на `class UserRepository implements IUserRepository {}`. Теперь модули нижних уровней (классы; детали реализации) зависят от абстракции, но не наоборот.\n\n## Принцип DRY\n\n**Принцип DRY** (Don’t repeat yourself) переводится как \"Не повторяйся\" и нацелен на снижение повторения информации различного рода.\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\n«Каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в рамках системы».\n\nКогда DRY применяется правильно, изменение элемента системы не требует изменений в других, логически не связанных элементах.\n\nНарушение принципа DRY называют WET (Write Everything Twice), переводящееся как \"Пиши всё дважды\".  \nИгра слов: dry — сухой, wet — влажный.\n\nПростой пример применения принципа DRY: видишь часто повторяющийся код — выносишь его в функцию или переменную.\n\n## Принцип KISS\n\n**Принцип KISS** (keep it short and simple) переводится как \"Держи это коротким и простым\" и утверждает, что в большинстве случаев система работает лучше, если отаётся простой, поэтому в области проектирования простота должна быть одной из ключевых целей и следует избегать ненужной сложности.\n\n## Принцип YAGNI\n\n**Принцип YAGNI** (You aren't gonna need it) переводится как \"Вам это не понадобится\" и утверждает, что не следует добавлять функциональность пока не возникнет необходимость в этом.\n> Always implement things when you actually need them, never when you just foresee that you need them.\n\n«Всегда реализуй вещи, когда ты действительно в них нуждаешься, никогда, если ты предвидишь, что они тебе понадобятся».\n\nНаписание ненужного в данный момент кода требует время и поддержку, которые могли быть затрачены на важные в данный момент вещи. Такой код ограничивает то, что может быть сделано в будущем, увеличивает сложность приложения.\n"
  },
  {
    "path": "Auth.md",
    "content": "- [Аутентификация, Авторизация и Идентификация](#аутентификация-авторизация-и-идентификация)\n- [Разновидности идентификаторов. `ID`, `UID`, `UUID` и `GUID`](#разновидности-идентификаторов-id-uid-uuid-и-guid)\n- [Виды аутентификации](#виды-аутентификации)\n  - [Базовая (`Basic`)](#базовая-basic)\n  - [`Bearer`](#bearer)\n- [Сессии (`Sessions`) и `Cookie`](#сессии-sessions-и-cookie)\n- [`JSON Web Token` (`JWT`)](#json-web-token-jwt)\n  - [Виды токенов](#виды-токенов)\n- [`OAuth`](#oauth)\n\n## Аутентификация, Авторизация и Идентификация\n\n**Идентификация** (англ. `identification`, лат. `identifico` — отождествлять) — *процедура распознавания объекта* (субъекта) по его *идентификатору*.  \n\nНапример, можно *идентифирировать* человека по имени, электронной почте, номеру телефона или паспорта.\n\n**Аутентификация** (англ. `authentication`, греч. `αυθεντικός` — подлинный) — процедура проверки подлинности.\n\nНапример, можно определить подлинность проверкой пароля, отпечатка пальца, наличием пропуска или ключа от двери.\n\n**Авторизация** (англ. `authorization` - разрешение, уполномочивание) – предоставление доступа к какому-либо ресурсу.\n\n### Взаимосвязь\nСтандартная последовательность действий на любом сайте.\n* *Установление личности* (username, email, phone) – *идентификация*.\n* *Проверка подлинности* (password) – *аутентификация*.\n* *Предоставление доступа* – *авторизация*.\n\n## Объект и субъект\n**Объект** (лат. objectum — предмет) — философская категория, обозначающая вещь, явление или процесс, на которые направлена предметно-практическая, управляющая и познавательная деятельность субъекта (наблюдателя); при этом в качестве объекта может выступать и сам субъект.\n\n## Разновидности идентификаторов. `ID`, `UID`, `UUID` и `GUID`\n\n**Идентификатор** (англ. `identifier`, `ID`) — уникальный признак объекта (субъекта), позволяющий отличать его от других объектов (идентифицировать). \n\nПоле `ID` обычно является главным ключом (англ. `primary key`), по которому можно найти конкретный объект в коллекции объектов.\n\nПоле `ID` чаще всего представлено в виде некоторого хэша, сгенерированного в заданном формате по некоторому алгоритму. Существует множество форматов ID, при этом для генерации могут использовать индексы (последовательное инкрементирование - самый простой случай), даты (англ. `timestamps`), рандомные числа, другие поля объекта, которые тоже по некоторому алгоритму становятся хэшем. Далее рассмотрим некоторые варианты `ID`.\n\n`UUID` (англ. `Universally Uique IDentifier`) — *стандарт идентификации*, стандартизированный Open Software Foundation как часть среды распределённых вычислений (DCE). \n\nUUID позволяет уникально идентифицировать информацию, не имея при этом центра координации.  \nТо есть любой может создать `UUID` и использовать его для идентификации чего-либо с достаточным уровнем уверенности, что этот идентификатор не будет использован для чего-либо ещё непреднамеренно.\n\n`UUID` имеет *несколько версий*, в которых используются:\n* **Версии 1 и 2** - *время* и *MAC-адрес*.\n* **Версия 3** - *хеширование* алгоритмом *MD5*.\n* **Версия 4** - генерация *случайным образом*.\n* **Версия 5** - *хеширование* алгоритмом *SHA-1*.\n\nБиблиотека в `npm`, позволяющая создавать и проверять валидность `UUID`:\n```bash\nnpm i uuid\n```\n\n# Виды аутентификации\n\nДля *проверки подлинности* (аутентификации) пользователя обычно на сервер вместе с запросом передаётся заголовок `Authorization`, в котором размещают тип аутентификации и соответствующие ей данные.\n```http\nAuthorization: <type> <credentials>\n```\n\n## Базовая (`Basic`)\n\n**Базовая** (Basic) **аутентификация** подразумевает передачу в заголовок имени пользователя и пароля в виде строки следующего вида `username:password`, зашифрованной в Base64.\n```http\nAuthorization: Basic <encodedCredentials>\n```\nВ JavaScript можно кодировать и декорировать Base64 следующим образом.\n```js\nconst credentials = 'admin:admin';\n/* кодировка */\nconst encodedCredentials = btoa('admin:admin');\nconsole.log(encodedCredentials); // 'YWRtaW46YWRtaW4='\n/* декодировка */\nconst decodedCredentials = atob('YWRtaW46YWRtaW4=');\nconsole.log(decodedCredentials); // 'admin:admin'\n```\n\n## `Bearer`\n\n```http\nAuthorization: Basic <encodedCredentials>\n```\n\n# Сессии (`Sessions`) и `Cookie`\n\n```js\n/* получение Cookie */\n       /* Auth request\n       with credentials */\nClient --------------------> Server\n\n        /* Set-Cookie \n        response header */\nClient <-------------------- Server\n\n        /* Cookie \n        request header */\nClient --------------------> Server\n```\nКогда Cookie устаревают, они удаляются и на клиенте, и на сервере, поэтому описанный выше алгоритм повторяется.\n```js\n/* обновление auth_token */\n\n        /* Cookie is\n        missing (401) */\nClient <-------------------- Server\n```\n\n<!-- Когда появляется внешний сервер аутентификации (другой url), -->\n\n# `JSON Web Token (JWT)`\n\n## Содержимое токена\n```js\n/* header.payload.signature */\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImdhcnJ5QGl0ZWNoYXJ0LWdyb3VwLmNvbSIsInN1YiI6MSwiaWF0IjoxNTgxMzUzMTk1LCJleHAiOjE1ODEzNjM5OTV9.sIAg18cc66dLi4PyLGzITOou8nppH056pxVVH0pgWMs\n```\n## Виды токенов\n\n### Токен доступа\n\n```js\n/* получение или обновление refresh_token */\n\n       /* Auth request\n       with credentials */\nClient --------------------> Auth Server\n\n        /* auth_token,\n        refresh_token */\nClient <-------------------- Auth Server\n\n        /* Authorization\n        request header with\n        auth_token */\nClient --------------------> Server\n```\n\n**Токены доступа** (Access Tokens) представляют собой учётные данные (credentials), которые используеются для доступа к защищенным ресурсам.\n\nТокен доступа отправляется на сервер с каждым запросом в `Authorization` заголовке запроса.\n\n*Токены доступа* можно использовать неограниченное количество раз, но они обычно имеют *короткую продолжительность жизни* (обычно устанавливают ~15 минут), чтобы их нельзя было украсть и постоянно использовать.\n\nТаким образом, если токен доступа украдут, то им можно будет пользоваться не более указанного в нём времени (например, не более 15 минут).\n\nВремя истечения токена может быть определено по полям  `exp` (expiration time), определяющим время жизни токена и `iat` (issued at), определяющим дату создания токена. Подделать это время нельзя, поскольку формула формирования токена нарушится и он станет невалидным.\n\nТокены доступа не хранятся на сервере, на клиенте чаще всего хранятся в Local Storage (и берутся оттуда, чтобы их можно было отправить в заголовке запроса).\n\n### Токен обновления\n\n```js\n/* обновление auth_token */\n\n        /* auth_token is\n        expired (401) */\nClient <-------------------- Auth Server\n\n        /* Auth request\n       with refresh_token */\nClient --------------------> Auth Server\n\n        /* auth_token,\n        refresh_token */\nClient <-------------------- Server\n```\n\nКогда токен доступа истекает и требуется новый для доступа к ресурсу, клиенту необходимо обратиться к серверу аутентификации (Auth Server) для получения нового токена.\n\n**Токен обновления** (Refresh Token) содержит информацию, необходимую для получения нового токена доступа.\n\nСерверу аутентификации отсылается токен обновления и возвращается токен доступа.\n\nТокены обновления одноразовые и обычно имеют долгую продолжительность жизни. \n\nЕсли токен обновления украден, его можно использовать только один раз, а значит получить только один токен доступа.\n\nЕсли клиент видит, что его токен доступа невалиден (им уже воспользовались), он может перелогиниться. Таким образом сгенерируется новый токен обновления, а старый станет невалидным.\n\nТокены обновления не хранят в себе какую-то информацию, как JWT-токены, и обычно представляют обычную хеш-строку. Токены обновления необходимо хранить на сервере.\n\nВ отличие от токенов доступа токены обновления хранятся на сервере. Они также хранятся и на клиенте.\n\n# `OAuth`\n"
  },
  {
    "path": "Browsers.md",
    "content": "- [Файлы](#файлы)\n- [Как всё устроено](#как-всё-устроено)\n\t- [Адресная строка браузера](#адресная-строка-браузера)\n\t- [Обработка URL](#обработка-url)\n  - [Этап поиска DNS](#этап-поиска-dns)\n  - [Построение объектных моделей (DOM, CSSOM)](#построение-объектных-моделей-dom-cssom)\n  - [Запуск JavaScript](#запуск-javascript)\n  - [Построение дерева рендеринга](#построение-дерева-рендеринга)\n  - [Рассчёт макета (Layout, Reflow)](#рассчёт-макета-layout-reflow)\n  - [Прорисовка, отрисовка (Painting, Paint)](#прорисовка-отрисовка-painting-paint)\n\n# Файлы\n\n<!-- Физическая память компьютера имеет байтовую структуру – единицей адресации является байт -->\n\n**Файл** (англ. `file`) — определенное количество информации (в виде программы или данных), имеющее имя и хранящееся во внешней памяти компьютера.\n\n**Текстовый** файл (англ. `text file`) содержит *текстовые данные* (символы, составлящие слова, формулы и прочее).\n\n**Двоичный, бинарный** файл (англ. `binary file`) содержит последовательность байт (состоящих из бит), которая что-то может значить.\n\nОбычно принято *разделять файлы* на *текстовые* и *бинарные*, но оба этих типа хранятся в памяти компьютера *одинаково*: как *последовательность байт*. Текстовый файл просто интерпретируется в текст, но на самом деле является *частным случаем бинарного файла*. На низком уровне кодировки и символы компьютером не воспринимаются: он понимает только нули и единицы.  \n\n**Имя файла** (англ. `filename`) состоит из двух частей, разделенных точкой `.`: *название файла* и *расширение*, *тип файла* (англ. `extension`).\nНапример, `Notes.pdf`, `Browsers.md`, `users.txt`.\n\n**Файловая система** (англ. `file system`) — система хранения файлов и организации каталогов (папок).\n\n# Как всё устроено\n\nПримечание: в браузерах Webkit (Chrome, Safari) и Gecko (Mozilla) *терминология* немного отличается.  \nДалее *будет рассматриваться версия Webkit* с *пометками* о том, как это называется в *Gecko*.\n\n## Адресная строка браузера\n\n**Uniform Resource Locator** (URL) — ссылка на веб-ресурс (документ, изображение и прочее), определяющая его расположение в компьютерной сети и способ его получения (зависит от протокола).\n\n**Адресная строка** (address bar, URL bar) — элемент интерфейса веб-браузера, который отображает текущий URL и позволяет изменять его на любой другой.\n\nСледующая схема URL описывает большинство случаев использования адресной строки.\n```\nprotocol:[//hostname[:port]][/path][?query]\n```\nДопустим, пользователь ввёл URL и нажал Enter.\n\n## Обработка URL\n\n**Лексема** — последовательность допустимых символов языка, имеющая определённый смысл для транслятора.\n\nБраузер принимает введённую пользователем строку и пытается понять, из чего она состоит — разбивает её на лексемы (за счёт наличия `:`, `//`, `/`, `?`).  \n\nПримеры *разбиения URL на лексемы*:  \n* http://localhost:3000  \n`http` - протокол, `localhost` - hostname, `3000` - порт\n* https://twitter.com/favicon.ico:  \n`https` - протокол, `twitter.com` - hostname, `favicon.ico` - путь\n* https://github.com/Max-Starling?from=2018-12-01&to=2018-12-31  \n`https` - протокол, `github.com` - hostname, `Max-Starling` - путь, `from=2018-12-01&to=2018-12-31` - query.\n\nПри наличии в URL корректного hostname и отсутствии протокола, браузер подставляет нужный протокол самостоятельно (по умолчанию HTTP, HTTPS для сайтов).\n\nЕсли отсутствует протокол и браузеру не удаётся найти корректный hostname в строке, то она воспринимается как поисковой запрос.  \n\nНапример, если пользователь ввёл в адресную строку `hello world`, то браузер с поисковиком Chrome по умолчанию перейдёт на https://www.google.com/search?q=hello%20world.\n\n## Этап поиска DNS\n\n**Domain Name System** (DNS) — система доменных имён, которая хранит информацию о доменах по их именам.  \nОбычно её используют для получения IP-адреса по имени хоста.\n\n**Домен** (domain) — узел в дереве имён, вместе со всеми дочерними (подчинёнными) ему узлами.  \n\n**Доменное имя** — символьное обозначение, зарегистрированное для сетевой адресации, в которой используется DNS. \nДоменные имена в доменах отделяются точками. Разрешённые символы в доменном имени: `a-z`, `0-9`, `-`. \n\nDNS имеет *древовидную* (иерархическую) структуру.\n\nНапример, система доменных имён из двух доменов `subdomain.domain.com` и `wikipedia.org` имеет вид:\n![DNS](./assets/DNS.png)\n\nНа самом деле полный домен выглядит вот так `wikipedia.org.`, cимвол `.` в конце — **корневой домен**, `org` — домен верхнего (первого) уровня, `wikipedia` — домен второго уровня.  \n\n**Поддомен** (subdomain) — подчинённый домен.  \n`domain.com` - поддомен домена `com`, `subdomain.domain.com` - поддомен домена `domain.com`.\n\nСперва браузер проверяет *локальный кэш DNS*.\n\nВ случае отсутствия кэша, для поиска (lookup) браузер вызывает функцию `gethostbyname`, которая работает по-разному в зависимости от операционной системы.\n\nФункция `gethostbyname` сперва проверяет локальный файл `hosts` (текстовый файл, содержащий базу данных доменных имён и используемый для их трансляции в IP-адреса), содержимое которого задаётся администрартором компьютера.\n```css\n/* файл hosts */\n127.0.0.1       localhost\n```\n\nЕсли файл `hosts` не содержит нужного домена, делается запрос к DNS-серверу, являющемуся частью настроенного стека протоколов (network stack).\n\nЕсть два популярных DNS-сервера: `1.1.1.1` — Cloudflare, `8.8.8.8` — Google), однако большинство людей используют DNS-сервер, предоставленный интернет-провайдером.\n\nDNS-сервер может хранить IP-адрес домена в своём кэше, если недавно уже разрешал (resolve) его.  \nЕсли в кэше домена нет, то делается запрос к корневому DNS-сервер `.` (система из множества серверов по всему миру, управляющая интернетом).\n\nКорневой DNS-сервер не знает IP-адреса каждого домена в мире, но может перенаправить запрос на тот DNS-сервер, который может знать — сервер верхнего уровня.\n\n\n\nБраузер выполняет DNS-запрос по протоколу UDP, являющимся транспортным протоколом без установки соединения (connectionless).\n\n## Построение объектных моделей (DOM, CSSOM)\n\n**Байты** (bytes) → **Символы** (characters) → **Лексемы** → **Токены** (tokens) → **Узлы** (nodes) → **Объектная модель** (object model)\n\n*Алгоритм обработки HTML-файла*:\n1) **Загрузка файла**. Браузер загружает файл (обычно из сети или локальной файловой системы, т.е. с диска), закодированный какой-то кодировкой. В компьютерной памяти все файлы представляются байтами, поэтому при загрузке файла браузер получает поток байтов.\n```css![DNS](./assets/DNS.png)\nEF BB BF EF B9 A4 68 74 6D 6C EF B9 A5\n```\n2) **Распознавание кодировки**. Сначала браузер пытается распознать кодировку. Он считывает первые три байта из потока в буфер и проверяет, являются ли они символом маркера последовательности байтов (Byte Order Mark, `U+FEFF`), который указывает кодировку. Если да, то оставшиеся символы расшифровываются с учётом этой кодировки, если нет, то браузер пытается распознать кодировку самостоятельно, в случае неудачи далее используется кодировка UTF-8.\n```css\n0xEF 0xB9 0xA4 → U+FEFF → маркер кодировки UTF-8\n```\n3) **Преобразование байтов в символы (decoding)**. Браузер расшифровывает (decode) поток байтов (stream of bytes) в символы, опираясь на заданную кодировку (encoding). [Подробнее об алгоритме декодирования можно прочитать здесь](./Encoding.md).\n```html\n0xEF 0xB9 0xA4 → U+FE64 → \"<\"\n0x68 → U+0068 → \"h\"\n0x74 → U+0074 → \"t\"\n0x6D → U+006D → \"m\"\n0x6C → U+006C → \"l\"\n0xEF 0xB9 0xA5 → U+FE65 → \">\"\n\n<html>\n```\n4) **Лексический анализ (lexing, tokenization)**. Браузер конвертирует последовательности символов в отдельные распознанные группы — **лексемы**, каждая из которых имеет определённый смысл. Затем лексемы преобразовываются в объекты — **токены**, имеющие определённые свойства и правила.  \n\nПример *разбиения HTML на лексемы*: `<div class=\"box\"></div>`.  \n`<` - начало тега, `div` - название тега, `class` - название атрибута, `=` - символ, объединяющий атрибут и его значение, `\"` - начало значения атрибута, `box` - значение атрибута, `\"` - конец значения атрибута, `>` - конец тега, `</` - начало закрывающего тега, `div` - название тега, `>` - конец тега.  \n\n5) **Построение объектной модели**. На основании информации токенов и их последовательности выясняется, из каких элементов состоит HTML и как они расположены по отношению друг к другу. Первое позволяет представить html-элементы в виде объектов. Второе — в виде дочерних и родительских элементов одного дерева. Поскольку в качестве узлов, вершин (nodes) дерева берутся объекты, полученная модель называется **объектной моделью**.  \n\nПолученную модель называют **объектной моделью документа** (Document Object Model, DOM).  \n\nЭтот *алгоритм браузер* проделывает *каждый раз*, когда нужно *обработать HTML*.\n\nТеперь *браузер загружает подключенные* к HTML-документу *ресурсы* (делая к ним запросы): картинки, видео, иконки, шрифты, а также *CSS*.\n```html\n<link rel=\"stylesheet\" type=\"text/css\" href=\"/styles.css\"/>\n```\n\nЛюбой CSS, присутствующий в документе (загруженный файлом или встроенный в документ при помощи `<style>`, `style=\"\"`), браузер обрабатывает по тому же алгоритму, что и HTML, однако до построения объектной модели происходит ещё два важных действия:\n* каскад\n* обработка значений.\n\n\nПолучившееся в результате работы алгоритма представление в виде дерева называется **объектной моделью CSS** (CSS Object Model, CSSOM).\n\nПример *разбиения CSS на лексемы* : `.box { width: 40px; }`.  \n`.` - начало селектора по классу, `box` - название класса, `{` - начало блока объявлений, `width` - свойство, `:` - символ, объединяющий свойство с его значением, `40px` - значение свойства, `;` - конец объявления, `}` - конец блока объявлений.\n\nВ CSS многие стили применяются не только к самому элементу, но и к его потомкам.  \nЭто называется наследованием: потомки наследуют свойства предка.  \nИменно поэтому CSS имеет древовидную структуру.\n\n## Обработка значений (Value Processing)\n\n*Значение* при *обработке* проходит *следующие этапы*.\n* **Объявленное значение** (Declared value) — значение, *объявленное автором* (author declaration), то есть *разработчиком*.\n* **Каскадное значение** (Cascaded value) — значение, полученное в *результате применения каскада*. Если есть *конфликтующие объявления*, *применяется более приоритетное* из них и становится *каскадным*. *Значение*, заданное *браузером* (user-agent declaration), *становится каскадным*, если *бъявленное значение отсутствует*. Примеры *браузерных значений*: синий цвет и подчёркивание ссылок `<a>`.\n* **Указанное значение** (Specified value) — значение, заданное по умолчанию для свойства (initial value). *Указанное значение* *используется*, если *каскадное значение отсутствует*. *Каждое свойство* имеет *указанное значение*. *Значение*, заданное *браузером не является* *значением по умолчанию*. Пример значений *по умолчанию*: для `margin` и `padding` по умолчанию применяется `0px`, для шрифтов — `16px`, для цвета — `#000`. *Наследуемое значение* также *становится указанным* (применяется, если объявленное и каскадное отсутствуют). *Наследуется вычисленное значение* (computed value) *родительского элемента*, а *не объявленное*.\n* **Вычисленное значение** (Computed value) — значение, переведённое из *относительных единиц* (relative) в *абсолютные* (absolute). Также на этом шаге происходит перевод таких значений, как `auto`, `green`, `bold` и других.\n* **Используемое значение** (Used value). CSS-движок использует *отрисованный макет* (rendered layout), чтобы понять, как следует поступить с оставшимися значениями, зависящими от макета (например, `vh`, `vw`, `%`). Используемое значение считается точно (с плавающей точкой). Например, `359.6px`.\n* **Фактическое значение** (Actual value) — финальное значение, готовое для использования в макете. Вычисляется на этапе этапе рендеринга страницы. Используемое значение может быть значением с плавающей точкой. Браузер не может разделить пиксели, поэтому округляет значение до целого `360px`.\n\n### Перевод относительных величин и процентов в пиксели\n\n`%` технически не является единицой измерения (unit).\n\nВсе величины переводятся в пиксели, поскольку страница состоит из них.\n\n*Относительное значение переводится* в *пиксели относительно* *вычисленного значения* (computed value).\n \n* `%` для шрифта (font) вычисляется относительно размера шрифта родительского элемента.\n```css\n.parent {\n  font-size: 24px;\n}\n.child {\n  font-size: 150%; /* 36px */\n}\n```\n* `%` для `width`, `margin`, `padding` вычисляются относительно `width` родительского элемента, `height` — относительно `height` родительского элемента, `border` *не вычисляется* в *процентах*.\n```css\n.parent {\n  width: 200px;\n  height: 100px;\n  border: 2px solid #000;\n}\n\n.child {\n  width: 80%; /* 160px (от ширины родителя) */\n  padding: 10%; /* 20px (от ширины родителя) */\n  margin-top: 50%; /* 100px (от ширины родителя) */\n  /* но */\n  height: 50%; /* 100px (от высоты родителя) */\n  border: 50% solid #000; /* 0px (не считается в процентах) */\n}\n```\n* `em` для шрифта вычисляется относительно `font-size` родительского элемента. `1em` = `font-size` родителя.\n```css\n.parent {\n  font-size: 16px;\n}\n\n.child {\n  font-size: 4em; /* 64px */\n}\n```\n* `em` для `width`, `height`, `margin`, `padding` вычисляется относительно `font-size` текущего элемента.\n```css\n.parent {\n  font-size: 24px;\n}\n\n.child {\n  font-size: 16px;\n  height: 4em; /* 64px (от шрифта текущего элемента) */\n  padding: 1em; /* 16px (от шрифта текущего элемента) */\n}\n```\n* `rem` для всех свойств вычисляется относительно корневого (root) `font-size`. Браузер обычно по умолчанию задаёт корневой `font-size` в `16px`. Корневым элементом является `html`.\n```css\n.block {\n  width: 10rem; /* 160px */\n  font-size: 2rem; /* 32px */\n}\n```\n* `vh` и `vw` для всех свойств вычисляются относительно высоты и ширины `viewport` соответственно.\n```css\n.block {\n  width: 10vw; /* 160px */\n  font-size: 10vh; /* 32px */\n}\n```\n\n## Запуск JavaScript\n\nJavaScript — блокирующий ресурс для отрисовки, поскольку может напрямую работать с DOM.\n\nЕсли скрипт внешний, то файл сперва загружается, а потом запускается (evaluate).\n```js\n<script src=\"/main.js\"></script>\n```\nЕсли скрипт встроенный, то файл сразу запускается.\n```js\n<script>console.log('Hi')</script>\n```\n\nЕсли скрипт как-то повлиял на объектные модели, то в них вносятся соответствующие изменения.\n\n<!-- * файл загружается\n* код парсится и строится Абстрактное Синтаксическое Дерево\n* код преобразуется в машинный\n* запускается машинный код -->\n\n## Построение дерева рендеринга\n\nДеревья DOM и CSSOM вместе объединяются в **дерево рендеринга** (Render Tree).  \n\n*Алгоритм построения дерева рендеринга*   \n1) На рассмотрение берутся вершины DOM.\n2) Из рассмотрения *исключаются вершины*, которые не видны на странице: `<head>` со своим содержимым, `<script>` и прочие подобные.  \n3) Для каждой *оставшейся вершины* находятся соответствующие наборы правил в CSSOM.  \n4) Из рассмотрения *исключаются вершины*, которые *скрыты* при помощи *CSS*: имеют *объявление* `dispalay: none`.  \n5) Каждая оставшейся вершина DOM вместе со своими наборами правил становится вершиной дерева рендеринга.  \n\n*Вершина дерева рендеринга* называется **объектом рендеринга** (Render Object).  \n\nВ браузерах на движке *Gecko дерево рендеринга* может также называться **деревом фреймов** (Frame Tree), а *вершина дерева* - **фреймом** (frame) или **боксом** (box).\n\nПосле построения дерева рендеринга браузер знает, какие объекты видны на странице и какие стили к ним нужно применить.\n\n## Рассчёт макета (Layout, Reflow)\nПри **рассчёте макета** (Layout) *вычисляются расположение элементов на экране* и *занимаемое* ими *место*, то есть их *геометрия*.  \n\nВ первую очередь проверяется вьюпорт.  \nЕго значение берётся из соответствующего метатега, в случае его отсутствия браузером задаётся значение по умолчанию.  \n```html\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n```\nЗначения высоты и ширины вьюпорта берутся как базовые, затем браузер обходит (traverse) дерево рендендеринга, начиная от его корня `<html>`, вычисляя все значения элементов: часто значения дочерних элементов зависят от родительских.\n\nПусть ширина вьюпорта составляет 800px.\n```html\n<body>\n\t<div class=\"parent\">\n\t\t<div class=\"child\"></div>\n\t</div>\n</body>\n```\n```css\nhtml {\n  width: 50%; /* 400px, 50% от viewport */\n}\nbody {\n  width: 25%; /* 100px, 25% от html */\n}\n.parent {\n  width: 100vw; /* 800px, 100% от viewport */\n}\n.child {\n  width: 10%; /* 80px, 10% от parent */\n}\n```\n\nПосле рассчёта макета каждый элемент на странице имеет свою блочную модель (box model), а все величины измерения переводятся в пиксели.  \n## Прорисовка, отрисовка (Painting, Paint)\n\n**Прорисовка** (Painting) – это процесс заполнения пикселей на экране.  \nИногда этот процесс называют **растеризацией, растрированием** (rasterisation, rasterization).\n\n<!-- **Растеризация (информатика)** - один из методов рендеринга, при котором визуализация проводится проецированием объектов сцены на экран без сохранения эффекта перспективы -->\n\nОн подразумевает вывод текста, цветов, изображений, границ и теней, по сути – всех визуальных частей элементов. Прорисовка обычно выполняется на нескольких поверхностях, которые называются слоями.\n\n"
  },
  {
    "path": "Bundling.md",
    "content": "# Сборка проекта (Bundling)\n\n## Встряска дерева (Three Shaking)\n\n**Встряска дерева** (англ. `three shaking`) - подход к сборке проекта, позволяющий добиться исключения \"мёртвого кода\" (англ. `dead code elimination`) - тех файлов (или методов в этих файлах), которые не задействованы в приложении. \n\nПри этом подходе проект представляется в виде **дерева зависимостей**:\n* Выбирается *лючевой, корневой* файл (`app.js`, `index.js`), с которого *начинается запуск приложения* (например, `node index.js`). Этот *файл* становится *корнем дерева* (англ `root`).\n* *Корневой файл* чаше всего *имеет зависимости* (`require`, `import`) в виде *других файлов*, которые в *дереве зависимостей* *становятся* его *дочерними узлами*. Поскольку эти *файлы тоже* могут *иметь зависимости*, то и у них могут быть *дочерние узлы*. *Выстроив все цепочки зависимостей файлов*, *получаем* некоторый *граф*.\n* Если некоторая *зависимость* уже была *внедрена* (импортирована), то *второй раз* она в итоговую *сборку не включается* - *берётся уже существующая ссылка на файл*. Это *позволяет исключить циклические зависимости*, а значит *граф можно представить* в виде *дерева*.\n```js\n/* пример циклической зависимости */\n// a.js\nrequire('b.js');\n// b.js\nrequire('a.js');\n```\n* Таким образом, те файлы в проекте, которые по каким-либо причинам нигде не были импортированы, не попадают в дерево зависимотей, а значит и в сборку проекта. Это позволяет уменьшить размер бандла, а значит ускорить загрузку страницы.\n* Если учесть, что можно импортировать не только файлы, но и отдельные методы, то узлами дерева могут становиться эти самые методы. В таком случае неимпортированные методы не так же не попадут в сборку.\n\nОтсюда можно сделать *вывод касательно названия*. Когда мы *трясём дерево* - то, что *держится за ветку не* достаточно *прочно*, - *падает* (яблоки, шишки, каштаны, листья, а в нашем случае - *\"мёртвый код\"*).\n\n*Концепция \"встряски дерева\"* была *популяризирована* [Rollup](https://github.com/rollup/rollup#tree-shaking) и [Webpack](https://webpack.js.org/guides/tree-shaking/).\n\nРассмотрим *пример исключения неиспользуемых методов* из *сборки* (с *файлами* всё *аналогично*).\n\nДопустим, есть некоторый *модуль* `utils.js` по типу `lodash`, *содержащий множество полезных утилит*.\n```js\n// utils.js\nexport const debounce = () => { /* ... */ };\nexport const throttle = () => { /* ... */ };\nexport const groupBy = () => { /* ... */ };\n```\nИ есть *модуль* `app.js`, который *использует метод* `splitIntoChunks` из этого *модуля*. Пусть *другие модули не используют* `utils.js`.\n```js\n// app.js\nimport { groupBy } from 'utils';\n```\nВ таком случае в *сборку попадёт только метод* `groupBy`, а *методы* `debounce` и `throttle` в неё *не попадут*.\n\n## Разбиение на чанки\n\n\n"
  },
  {
    "path": "CSS.md",
    "content": "- [Основы CSS](#основы-css)\n  - [Утверждения](#утверждения)\n  - [Правило и его составляющие](#правило-и-его-составляющие)\n  - [Типы элементов в CSS](#типы-элементов-в-css)\n  - [CSS Box Model](#css-box-model)\n  - [Типы отношений элементов в CSS](#типы-отношений-элементов-в-css)\n  - [Селекторы в СSS](#селекторы-в-сss)\n  - [Специфичность селекторов](#специфичность-селекторов)\n  - [Наследование](#наследование)\n  - [Каскад](#каскад)\n- [Подходы к написанию CSS](#подходы-к-написанию-css)\n  - [Проблемы CSS](#проблемы-css)\n  - [Некоторые рекомендации](#некоторые-рекомендации)\n  - [БЭМ](#бэм)\n  - [OOCSS](#oocss)\n  - [SMACSS](#smacss)\n  - [Atomic CSS](#atomic-css)\n  - [AMCSS](#amcss)\n  - [Enduring CSS](#enduring-css)\n- [Динамический CSS](#динамический-css)\n  - [Добавление и удаление классов по условию](#добавление-и-удаление-классов-по-условию)\n  - [CSS-in-JS](#css-in-js)\n- [Вёрстка под различные девайсы](#вёрстка-под-различные-девайсы)\n  - [Viewport](#viewport)\n  - [Виды вёрстки](#виды-вёрстки)\n- [Препроцессоры и постпроцессоры CSS](#препроцессоры-и-постпроцессоры-css)\n  - [Препроцессоры](#препроцессоры)\n  - [Постпроцессоры](#постпроцессоры)\n  - [Возможности постпроцессоров в деталях](#возможности-постпроцессоров-в-деталях)\n- [CSS Modules](#css-modules)\n\n# Основы CSS\n**Cascading Style Sheets**, **CSS** — *каскадные таблицы стилей*; *язык таблиц стилей* (stylesheet), используемый для *представления внешнего вида* HTML-документа. \n\n*Язык CSS* описывает, *как* элемент должен *отображаться* на экране.\n\n*Документом* (`document`) называют *текстовый файл*, структурированный при помощи *языка разметки HTML*, *SVG* или *XML*.\n\n## Утверждения\n\n**Утверждение**, **заявление** (англ. `statement`) — *структурный элемент CSS*, который *начинается* с *любых непробельных символов* и *заканчивается первой закрывающей фигурной скобкой* или *точкой с запятой*.\n\nЕсть два *типа утверждений*:\n* **Набор правил**, или проще: **правило** (англ. `ruleset`, `rule`).\n* **At-правило** (англ. `at-rule`).\n\n*Любое другое утверждение* считается *недопустимым* и *игнорируется*.\n\n### Правило и его составляющие\n\n**Правило** (англ. `rule`) — *CSS-утверждение*, состоящее из *селектора* (или *группы селекторов*) и *блока объявлений*.  \n\n```css\nselector1, selector2 /*, ...*/ {\n   property1: value1;\n   property2: value2;\n   /* ... */\n}\n```\n\n*Cелектор* (англ. `selector`) используется для *выбора элементов страницы*. Ниже приведены *примеры пяти селекторов*:\n```css\n#menu\n.title\ndiv\n.parent .child\nbutton:hover\n```\n\n*Два и более селекторов отделяются* друг от друга *запятыми* и образуют **группу селекторов**.\n```css\np, span, h1\n.sister, .brother\n```\n\n**Блоком объявлений** (англ. `declaration block`) называется *всё*, что находится *внутри фигурных скобок*. Обычно там находятся *объявления*.\n```css\n{\n  property1: value1;\n  property2: value2;\n}\n```\nНапример,\n```css\n{\n  font-size: 14px;\n  color: red;\n}\n```\n\n**Объявлением** (англ. `declaration`) называется пара *свойство-значение* (англ. `property-value`).  \n```css\npropetry: value;\n```\n\n*Объявления отделяются* друг от друга *точками с запятой*. Если *пропущена точка с запятой*, то стили могут *примениться неправильно* или *не примениться* вовсе.\n```css\n{\n  width: 50% /* отсутствие \";\" - синтаксическая ошибка */\n  height: 100%; /* данное правило и все последующие не будут применены, поскольку внутри этого блока объявлений выше имеется синтаксическая ошибка */\n}\n```\n\n```css\n.article-title, .header-title {\n  color: #ccc;\n  font-size: 18px;\n}\n```\n\n*Блоки объявлений* могут быть *пустыми*.\n```css\ndiv {}\n```\n\n*Блоки объявлений* могут быть *вложенными*, если используются некоторые *at-rules* (например, `@media() {}`.\n```css\n@media (/*...*/) {\n  div {\n    /*...*/\n  }\n}\n```\n\n### At-правило\n\n**At-правило** (англ. `at-rule`) — *CSS-утверждение*, *указывающее CSS*, *как себя вести*.  \nНачинается с *символа @* (`at`, `at sign`) и *включает* в себя *весь следующий блок объявлений* или *весь код до* `;`.\n```scss\n@charset \"utf-8\"; /* определение набора символов */\n@import 'custom.css'; /* импорт таблицы стилей (дополнительный запрос к серверу) */\n\n/* медиазапросы, объявление CSS-правил в зависимости от девайса */\n@media screen and (max-width: 1080px) {\n  /* ... */\n} \n\n/* объявление CSS-правил в зависимости от поддержки браузером */\n@supports (display: grid) {\n  /* ... */\n} \n\n/* объявление и использование шрифта */\n@font-face { \n  font-family: \"Open Sans\";\n  src: url(\"/* ... */\") format(\"/* ... */\");\n}\n.className {\n  font-family: \"Open Sans\";\n}\n\n/* объявление и использование анимации */\n@keyframes fadeIn { \n  from {\n    opacity: 0;\n  }\n  to {\n     opacity: 1;\n  }\n}\n.className {\n  animation-name: fadeIn;\n  animation-duration: 200ms;\n}\n```\n*At-правила* могут быть *вложенны друг в друга*.\n```scss\n@supports (display: grid) {\n  @media screen { /* ... */ }\n}\n```\n\n## Типы элементов в CSS\n\n### Блочные и строчные элементы\n\n**Блочные элементы** — элементы высшего уровня (визуально выглядят как блоки), располагающиеся на странице *вертикально* и задающие её структуру. Они создают разрыв строки перед элементом и после него, образуя прямоугольную область, по ширине занимающую всю ширину блока-родителя. \n\n*Блочные элементы* могут содержать как *строчные*, так и другие *блочные* элементы.  \n*Блочный* элемент `<p>` является *иключением* *не должен содержать* внутри себя другие *блочные* элементы (в том числе и *p*).\n\n**Строчные (встроенные) элементы** используются для *форматирования текстовых фрагментов* (кроме `<area>`, `<img>`). Они *не формируют новые блоки контента*, *не создают разрыв строки* вокруг себя. Многие строчные элементы *не контролируют* поля, отступы, ширину и высоту. \n\n*Строчные элементы* могут содержать *только данные* и другие *строчные* элементы, они *не могут* содержать *блочные* элементы. (кроме *a*)\n\n*Блочные* и *строчные* элементы являются *основными* элементами страницы. С них всё начиналось, и можно сделать практически что угодно, используя только их. \n\nТакже есть **блочно-строчные** элементы, которые обладают смешанной характеристикой: являются встроенным, но могут задавать поля, отступы, ширину и высоту.\n\nСделать элемент *блочным*, *строчным* или *блочно-строчным* в *CSS* можно с помощью:\n```css\n.block { display: block; }\n.inline { display: inline; }\n.inline-block { display: inline-block; }\n```\n\nНа данный момент существует много других типов элементов, все из которых отражает свойство `display` (`table`, `flex`, `grid` и прочие).\n\n### Замещаемые и незамещаемые элементы\n\n**Замещаемый** (replaced) является элемент, *представление* которого *выходит за рамки CSS*. *Стили CSS не могут влиять* на *содержимое замещаемых элементов* — *только* на их *позиционирование*. \n\n*Замещаемые элементы*.\n* `<iframe>` — *содержит веб-страницу*, *не зависящую* от *родительской* страницы и её *стилей*.\n* `<video>`\n* `<img>`\n* `<embed>`\n* `<input type=\"image\">`\n\n*В некоторых случаях* являются *замещаемыми*.\n* `<canvas>`\n* `<audio>`\n* `<option>`\n* `<applet>`\n* `<object>`\n\n*Внешний вид* и *размеры замещаемых* элементов могут *определяться извне*.  \nНапример, *img без заданных* свойств `width` и `height`, *занимает* свой *естественный размер*.  \nПри *изменении* одного из этих свойств, второе высчитается *автоматически*, с учётом *пропорций*.\n\n*Остальные элементы* можно отнести к **незамещаемым**.\n\n## CSS Box Model\n\n*Каждому HTML-элементу* соответствует *прямоугольная область*, называемая **боксом** (box).  \n*Структура* этой области называется **блочной**, **боксовой моделью** (box model).\n* **content** — *контент*, *содержимое*; *внутренняя область* элемента, содержащая *текст* или *другие элементы*.\n* **padding** — *поля* элемента, *окружающие контент*. \n* **border** — *рамки* элемента, *окружающие поля* элемента.\n* **margin** — *внешние отступы* (*пустое пространство вокруг* элемента), определяющие *расстояние* до *соседних* элементов.\n\n*Каждая* из этих *составляющих* имеет какую-то *область* (area) и *границу* (edge).  \n\n![css-box-model](/assets/css-box-model.png)\n\n*Каждый бокс*, являясь *прямоугольником*, должен иметь *ширину* и *высоту*. \n\nСуществует *два способа задать бокс*, от которых *зависят* его *ширина* и *высота*.\n* `border-box` — `padding` и `border` входят в указанные в свойствах `width` и `height` ширину и высоту.\n* `content-box` — `width` и `height` определяют *ширину* и *высоту* *только для контента* (content width, content height), `padding` и `border` *дополнительно увеличивают* размер *бокса*.\n```css\n/*\n  высота бокса: 100px,\n  высота контента: 100px - (16 * 2)px - (4 * 2)px = 60px\n*/\n.border-box {\n  box-sizing: border-box;\n  height: 100px;\n  padding: 16px;\n  border: 4px solid;\n}\n```\n```css\n/*\n  высота контента: 100px,\n  высота бокса: 100px + (16 * 2)px + (4 * 2)px = 140px\n*/\n.content-box {\n  box-sizing: content-box;\n  height: 100px;\n  padding: 16px;\n  border: 4px solid;\n}\n```\n\n*Величина внешних отступов* может быть *отрицательной*, *величина остальных составляющих* блочной модели — *не может*.\n```css\n.class {\n  margin-top: -16px;\n}\n```\n*Внешние отступы прозрачны*, *остальные составляющие* могут быть *закрашены* в какой-то *цвет* (`border-color` для *рамок*, `background-color` для *полей* и *контента*.\n\n## Типы отношений элементов в CSS\n\nПодраздел относится скорее к *DOM* (представлению документа в виде дерева). Просто краткое напоминание, что даже у элементов есть отношения:\n* **Элемент-предок (ancestor) и элемент-потомок  (descendant)**.  Элемент-потомок находится в элементе-предке, но при этом предок может не быть его родителем (потомок может быть вложен в другой элемент, также являющийся потомком для рассматриваемого предка). В примере ниже такая связь наблюдается только между `div` и `span`, `div` и `img`.\n\n* **Родительский (parent) и дочерний элементы (child)**.  Дочерний элемент находится в родительском и других уровней вложенности между ними нет. Ниже такая связь только между  `div` и `p`, `div` и `a`, `p` и `span`, `a` и `img`.\n\n* **Элементы-братья (sibling)**.  Должны иметь *одного* и *того же* *родителя*. Называются **смежными** (adjacent), если *следуют прямо друг за другом*. Ниже братьями (причём смежными) являются только `p` и `a`. Элемент `a` называется **следующим** (following) **за элементом** `p`, `p` — **предшествующий** (preceding) **элементу** `a` (понятия *следования* и *предшествия* определены *только* для *смежных* элементов).\n\n* **Остальные элементы**. Хоть они и являются *дальними родственниками* (как *минимум один предок* — *корень дерева*), считаются *безотносительными друг к другу*. Ниже такими являются `span` и `img`, `p` и `img`, `span` и `a`.\n\n```html\n<div>\n  <p>\n    <span></span>\n  </p>\n  <a>\n    <img />\n  </a>\n</div>\n```\n\n## Способы разметки\n\n**Способ разметки в CSS** (CSS layout mode, layout) — алгоритм, определяющий позицию и размер блоков на основании того, как они взаимодействуют с их родственными блоками (ancestor, sibling).\n\n**Содержащий блок** (containing block) — *предок рассматриваемого элемента*, который может *влиять* на его *размер* и *позицию*.  Чаще всего *содержащим блоком* является *элемент-родитель*, но *не всегда*.\n  \n**Начальный содержащий блок** (initial) — `<html>`. *Размеры начального блока зависят* от *viewport*.  \n\nТипы разметки:\n* **Нормальная, обычная** (normal flow) — блочная разметка для блочных элементов (`<div>`, `dispalay: block`) и линейная разметка (`<span>`, `display: inline`) для строчных элементов. Используется по умолчанию.\n* **Табличная** (table) для построения таблиц (`<table>`, `<tr>`, `<th>`, `display: table`).\n* **Float-разметка** для размещения элемента слева или справа при том, что весь остальной контент оборачивает этот элемент в порядке норамальной разметки. (`float: left`, `float: right`).\n* **Позиционированная**.  \n**Позиционированный элемент** (positioned) — элемент, имеющий вычисленное значение `relative`, `absolute`, `fixed` и прочие (но не `static`) в свойстве `position`.  \nЛюбой элемент по умолчанию имеет `position: static`, не считается позиционированным и не реагирует на свойства `top`, `right`, `bottom`, `left`, `z-index`.  \n**Относительно позиционированный элемент** (relatively) — элемент, значения свойств `top`, `bottom` и `left`, `right` которого определяют вертикальное и горизонтальное смещения (offset) от нормального позиционирования соответственно (`position: relative`).  \n**Абсолютно позиционированный элемент** (absolutely) — элемент, значения свойств `top`, `right` `bottom`, `left` определяют смещения от границ содержащего блока (при `position: absolute` это ближайший позиционированный элемент-предок, при `position: fixed` — начальный содержащий блок). Абсолютно позиционированный элемент удаляется из нормальной разметки документа и не занимает в ней никакого места. Margins добавляются к смещению.\n* **Flexbox-разметка** (flexible box).\n* **Сеточная** (grid).\n\n## Селекторы в СSS\n\n*Селектор* (selector) используется для *выбора* (select) *элементов страницы*, к которым мы хотим *применить стили*.\n\nСелекторы *подразделяются* на *простые* и *составные*. В них также включаются *псевдоклассы* и *псевдоэлементы*.\n\n### Простые селекторы\n\n**Селектор по типу** выбирает элементы по *типу* узла.\n```css\ndiv, p, span, a { }\n```\n**Селектор по классу** выбирает элементы, соответствующие *атрибуту  class*.\n```css\n.classname {}\n```\n**Селектор по id** выбирает элементы, соответствующие *атрибуту  id*.\n```css\n#idname {}\n```\n**Универсальные селекторы**  выбирают элементы *всех типов*.\n```css\n* {}\n```\n**Селекторы атрибутов**  выбирают элементы на основании *наличия* указанного *атрибута*, а также *соответствия значения атрибута*, если оно указано.\n```css\n[attr operator value i] {}\n\na[href] {} /* <a> элементы с атрибутом href */\n\na[href=\"https://qq.com\"] {}  /* ...точно соответствующим \"https://qq.com\" */\n\na[class^=\"qq\"] {} /* ...начинающимся с подстроки 'qq' */\n\na[href*=\"qq\"] {} /* ...содержащим подстроку 'qq' */\n\na[href*=\"qq\" i] {} /* ...содержащим подстроку 'qq' (case insensitive благодаря флагу 'i') */\n\na[href~=\"qq\" i] {} /* ...содержащим целое слово 'qq' (значение атрибута состоит из слов и пробелов) */\n\na[href$=\".com\"] {} /* ...заканчивающимся подстрокой '.com' */\n```\n\n### Составные селекторы и комбинаторы\n\n**Составной селектор** *состоит* из *двух* или *более простых селекторов*, между которыми могут быть *комбинирующие операторы* — **комбинаторы** (combinators).\n\nВ *комбинаторах приоритеты* и *правила группировки операторов* при выборке элементов *отсутствуют*, поэтому *составление выборки* осуществляется *строго справа налево*, от одного простого селектора к другому. \n\n**Комбинатор потомков (descendant) A B**  выбирает элементы, соответствующие *селектору B* и имеющие предка, соответствующего *селектору A*. \n\n*Комбинатор потомков* представлен *одним* или *несколькими пробельными символами* (возможно, с переходом на новую строку или даже с комментариями). \n```css\n.grandfather .son {}\n.father .son {}\n```\n\n*Комбинатор потомков сработает* лишь в том случае, если между селекторами *нет других комбинаторов*. В следующем *примере* он *не применяется*, поэтому строки *эквивалентны*.\n```css\n .father>.son {}\n .father > .son {}\n```\n\nСлучай, когда *селекторов больше двух* (например, *C A B*), означает лишь наличие *дополнительного условия*. Помимо сказанного выше про *A B*, у искомых элементов должен также имееться *элемент-предок*, соответствующий *селектору C*.\n```css\n.grandfather .father .son {}\n```\nЭто точно *так же работает* как для *большего числа селекторов* (по индукции), так и для *комбинаторов других типов*.\n\n**Комбинатор детей (child) A > B** является *частным случаем комбинатора потомков*. Он выбирает *элементы*, соответствующие *селектору B* и имеющие *родительский элемент*, соответствующий *селектору A*.\n```css\n.grandfather > .father > .son {}\n.father > .son {}\n```\n**Комбинатор смежных братьев (adjacent sibling, next sibling) A + B** выбирает элементы, соответствующие *селектору B*, являющиеся *смежными братьями* с элементами, соответсвующими *селектору A*, и *следующие прямо за ними*.\n\n\n**Комбинатор общих братьев (general sibling, subsequent sibling) A ~ B** выбирает элементы, соответствующие *селектору B*, являющиеся *братьями* с элементами, соответсвующими *селектору A*, и *следующие за ними* (*не обязательно прямо*, *между ними* могут быть и *другие братья*).\n\nРассмотрим несколько *примеров*, чтобы понять, *как* работают *комбинаторы братьев*:\n```html\n<div class=\"row\">\n   <div class=\"cell white\"></div>\n   <div class=\"cell\"></div>\n   <div class=\"cell white\"></div>\n   <div class=\"cell\"></div>\n</div>\n<style>\n  .row { display: flex; width: fit-content; border: 1px solid; }\n  .cell { width: 50px; height: 50px; }\n</style>\n<style>\n  .white + .cell { background: black; } /* Рис. 1 */\n  .cell + .cell { background: black; } /* Рис. 2 */\n  .white + .white { background: black; } /* Рис. 3 */\n\n  .white ~ .cell { background: black; } /* Рис. 4 */\n  .cell ~ .cell { background: black; } /* Рис. 5 */\n  .white ~ .white { background: black; } /* Рис. 6 */\n</style>\n```\n\n![sibling-combinators](/assets/sibling-combinators.png)\n\n*Рис. 1* Каждое *поле*, *следующее* за *белым* (*.white*), окрашивается в *чёрный*.  \n*Рис. 2* Каждое *поле*, *следующее* за *другим полем*, окрашивается в *чёрный* (все, кроме первого).  \n*Рис. 3* *Не найдено белых полей*, *следующих* за *белыми*.  \n*Рис. 4* Каждое *поле*, если *ранее* встречалось хоть *одно белое*, окрашивается в *чёрный*.  \n*Рис. 5* Каждое *поле*, если *ранее* встречалось хоть *одно поле*, окрашивается в *чёрный*.  \n*Рис. 6* Каждое *белое поле*, если *ранее* встречалось *хоть одно белое поле*, окрашивается в *чёрный*.  \nВсё это *работает* так, потому что *все поля имеют* один *класс* `cell` и у них *нет братьев другого класса*.  \n\nСтоит отметить, что *комбинаторы* (не считая *комбинатора потомков*) используются *крайне редко*, так как могут стать *причиной* *довольно непредсказуемого поведения* кода, поскольку при их использовании *добавление новых блоков* будет *сказываеться* на стилях *других блоков*, что может быть особенно заметно, когда в ход вступает *специфичность*.\n\nЕсли между селекторами *ничего не стоит* **AB**, то происходит их **объединение**: выбираются элементы, *соответствующие обоим* селекторам *одновременно*.\n```css\ndiv.white {}\n.white.cell {}\ndiv#body {}\np.title {}\n```\n\nЕсли *хотя бы одна часть селектора* написана *неправильно*, то этот *селектор полностью игнорируется*, на другие селекторы это никак не влияет.\n\nЕсли между селекторами *стоит запятая* **A, B** то они *не связаны* между собой *выбором элементов* (в этом случае выборка осуществляется для каждого селектора в отдельности), но ко всем найденным элементам применется *один набор стилей*. Такой тип связи называется **группой селекторов**.\n\n### Псевдоклассы и псевдоэлементы\n\n**Псевдоэлемент** — *ключевое слово*, добавляемое к селектору, позволяющее *стилизовать определённую часть* выбранного *элемента* (например, первоя строка). \n\n*Псевдоэлементы* также позволяют контролировать элементы, находящиеся *за пределами документа*; позволяют ссылаться на *недоступную* для получения *иным путём* информацию (например, полоса прокрутки). Они должны всегда *начинаться с двух двоеточий*, хоть в большинство браузеров их разрешено использовать и с одним для обратной совместимости:\n```css\n.field::placeholder { color: #8ab7e5 } /* изменение цвета замещающего текста для input и textarea */\np::before, p::after { content: \"♥\"; } /* вставка сердечка перед контентом каждого блока p и после него */\np::first-line { color: #8ab7e5 } /* изменение первой строки блочного элемента (в данном случае p) */\np::first-letter { font-size: 24px; } /* изменение первой буквы блочного элемента (в данном случае p) */\n*::selection { background: #8ab7e5 } /* изменение цвета выделения текста */\n*::-webkit-scrollbar-thumb { background: #8ab7e5 } /* изменение цвета ползунка полосы прокрутки в браузерах на движке webkit */\n```\n\n**Псевдокласс** — *ключевое слово*, добавленное к селектору и *определяющее его состояние*. \n\n*Псевдоклассы* всегда *начинаются с двоеточия*. Некоторые из них могут принимать *параметры*.\n```css\ninput:focus {} /* любое поле ввода, находящееся в фокусе */\noption:checked {} /* каждая отмеченная опция */\nbutton:disabled {} /* каждая недоступная кнопка */\nlink:not(:visited) {} /* каждая непосещённая ссылка */\n*:hover {} /* любой элемент при наведении на него */\n```\n\nЕсли *псевдокласс* или *псевдоэлемент* применяются *ко всем элементам*, то не рекомендуется пропускать *универсальный селектор* **\\***, иначе может возникнуть путаница.  \nСелекторы ниже *похожи*, но выбирают *совершенно разные* элементы:  \n```css\n div:hover {} /* любой блок div, на который наведён курсор */\n div :hover {} /* ~ div *:hover (любой элемент, на который наведён курсор, располагающийся в блоке div) */\n```\n\nС помощью *псевдоклассов* также можно работать с *родительскими и дочерними* элементами:\n```css\ndiv:empty {} /* каждый блок без дочерних элементов */\ndiv:not(:empty) {} /* каждый блок, имеющий дочерние элементы */\ndiv:first-child {} /* каждый блок, являющийся первым ребёнком */\ndiv:nth-child(3) {} /* каждый блок, являющийся третьим по счёту ребёнком */\ndiv:last-child {} /* каждый блок, являющийся последним ребёнком */\ndiv:only-child {} /* каждый блок, являющийся единственным ребёнком */\n```\n\n*Псевдоклассы* могут идти *последовательно* друг за другом:\n```css\ndiv:first-child:last-child /* эквивалентно :only-child */\n```\n\nСтоит также отметить, что некоторые *псевдоклассы* совпадают с *атрибутами* элементов, поэтому их можно заменить *селектором атрибутов*. Следующие строки *эквивалентны*:\n```css\nbutton:disabled {}\nbutton[disabled] {}\n```\n\n### Применение CSS-селекторов в JavaScript (Selectors API)\n\n*Selectors API* предоставляет методы, с помощью которых можно быстро и просто получить список узлов документа путем сопоставления с *группой селекторов*.\n```javascript\n// возвращает первый найденный Element или null, если совпадений не найдено\nconst element = parentNode.querySelector('selectors'); \n// возвращает NodeList, содержащий все найденные элементы, или пустой NodeList\nconst elementList = parentNode.querySelectorAll('selectors'); \n\nconst root = document.querySelector('#root');\nconst articleTitles = root.querySelectorAll('.article > .title');\n```\nПараметр *selectors*  всегда  должен быть *валидной строкой CSS-селекторов*, в противном случае выдаётся исключение *SyntaxError*.\n\n## Специфичность селекторов\n**Специфичность селекторов** (selector's specificity) определяет *приоритетность селекторов* в таблице стилей. Чем *специфичнее* селектор, тем *выше* его *приоритет*. \n\nEсли в выборку *несколькими селекторами* попадают одни и *те же элементы* и затрагиваются одни и *те же свойства в блоках объявлений*, в конечном счёте *применяются значения свойств* из объявлений *более специфичного селектора*.  \n\n*Специфичность* представлена *четырьмя числами*, записанными *через запятую* `A,B,C,D`.\n\n### Правила специфичности\n* *Атрибут style* (*inline-стиль*). Специфичность: `1,0,0,0`.\n* *Атбрибут id*. Специфичность: `0,1,0,0`.\n* *Атбрибут class*, *все остальные атрибуты* (кроме `id`) и *псевдоклассы* (кроме `:not`). Специфичность: `0,0,1,0`.\n* *Типы элементов* и *псевдоэлементы*. Специфичность: `0,0,0,1`.\n* *Универсальные селекторы* и *псевдокласс* `:not`. Специфичность: `0,0,0,0` (*нулевая специфичность*).\n\nВ *составных селекторах* *каждое* из *четырёх чисел* специфичности *суммируется отдельно*. \n\n*Комбинирующие операторы не влияют* на *специфичность*.\n```css\n * body #root .container ul > li:last-child::after { content: \".\"; }\n/* 0,1,2,4 = 0,0,0,0 + 0,0,0,1 + 0,1,0,0 + 0,0,1,0 + 0,0,0,1 + 0,0,0,1 + 0,0,1,0 + 0,0,0,1 */\n```\n\nВ *группе селекторов специфичность* для *каждого селектора* считается *отдельно*.\n```css\n.cell.white, /* 0,0,2,0 = 0,0,1,0 + 0,0,1,0 */\ndiv /* 0,0,0,1 */\n{ background: #000; } \n```\n\nСтоит отметить, что *порядок расположения стилей в class-атрибуте не важен*. В примере ниже *результат одинаковый*:\n```html\n<p class=\"title text\"></p>\n<p class=\"text title\"></p>\n```\n\n### Почему специфичность нельзя представить одним числом\n\nВ *примере* ниже *элемент* `div` *выбирается двумя селекторами*, *первых* из которых представлен *одним атрибутом* `id`, а *второй* — *11 атрибутами* `class`. \n```html\n<div id=\"identifier\" class=\"a b c d e f g h i j k\"></div>\n```\n```css\ndiv {\n  width: 100px;\n  height: 100px;\n}\n\n#id {\n  background: red; /* 0, 1, 0, 0 (применится это правило) */\n}\n\n.a.b.c.d.e.f.g.h.i.j.k {\n  background: green; /* 0, 0, 11, 0 */\n}\n```\n*Если бы специфичность суммировалась* в *одно число*, то *11 атрибутов* `class` *переопределяли* бы стили *одного* `id` (`11 * 10 > 100`), *11 атбитуров* `id` *переопределяли* бы *inline-стиль* (`11 * 100 > 1000`), *11 элементов* переопределяли бы один `class`, но на самом деле *этого не происходит* и *не должно происходить*. Поэтому *числа специфичности должны суммируются отдельно*.\n\n## Наследование\n\n**Наследование** (Inheritance) — *автоматическая передача некоторых свойств (объявлений) элемента-предка* его *потомкам*. В этом случае *потомок наследует некоторые свойства предка*.\n\nМеханизм *наследования* позволяет разработчикам писать *меньше кода*. *Наследуются не все свойства*, поскольку *иначе наследование* бы только *усложнило написание CSS*.\n\n*Наследуются свойства*, задающие *параметры отображения текста* (все `font-*`, большинство `text-*`), *произношения текста вслух* (`speak-*`) и *отображения списка* (`list-*`), также *наследуются* `color`, `visibility`, `cursor`, `letter-spacing`, `line-height`, `white-space`, `border-collapse`, `border-spacing` и другие, менее популярные свойства.\n\n*Почти все остальные* свойства *не наследуются*. Среди них: *свойста блочной модели* (`border-*`, `margin-*`, `padding-*`, `*-width`, `*-height`), *позиционирования* (`position`, `top`, `left`,`right`, `bottom`, `float`), `background-*`, `transform`, `display`, `z-index`, `text-decoration` и другие. \n\n```html\n<div>\n  <p>Notes</p>\n</div>\n```\n```css\ndiv {\n  font-size: 24px;\n  color: red;\n}\n```\nТекст в примере выше, располагающийся в элементе `<p>`, будет *красным*, поскольку *свойства* `font-size` и `color` *наследуются* от `div`.\n\n*Наследуемое объявление не имеет специфичности* (specificity) и *важности* (importance), о которой будет рассказываться дальше, поэтому его может *перекрыть* даже *правило* с *универсальным селектором* `*`, имеющим *нулевую специфичность*, или *любое объявление*, *по умолчанию* указанное в *стилях браузера* (user-agent declaration).\n\nЕсли добавить *следующее правило* в примере выше, то текст станет *чёрным* несмотря на то, что *специфичность* селектора `*` — `0,0,0,0`, а селектора `div` — `0,0,0,1`.\n```css\n* {\n  color: #000;\n}\n```\n\n*Наследуются не объявленные значения* (declared values), а *вычисленные значения* (computed values).\n```css\n.parent {\n  font-size: 24px;\n  line-height: 2em; /* 48px (считается от font-size текущего элемента) */\n}\n\n.child {\n  font-size: 36px;\n  /* в line-height унаследуется не объявленное значение 2em,  \n  а вычисленное 2em от 24px = 48px */\n}\n```\n\n*Наследуемое свойство* от *ближнего предка перекрывает* наследуемое от *дальнего*. Если бы мы вместо `*` использовали `body`, то цвет текста остался бы *красным*, поскольку `div` находится ближе к `p`, чем `body`, и тоже определяет свойство `color`.\n```css\nbody {\n  color: #000;\n}\n```\n\n*Наследование тесно связано* с *древовидной структурой* *объектной модели CSS*.\n\n*Наследования обусловлено внутренней реализацией языка*. Оно заложено *не конкретными свойствами*, заданными *по умолчанию* в стилях, а самим *поведением наследуемых свойств*.\n\nЧтобы *явно унаследовать значение родительского свойства*, можно использовать *значение* `inherit`.\n```css\np {\n  color: inherit;\n}\n```\nТакже есть *значения* `initial` и `unset`. *Первое* устанавливает *значение*, *по умолчанию* заданное *браузером*, а в случае его *отсутствия* работает как `inherit`, если *свойство наследуемое* (naturally inherited). *Второе* — *наоборот*: *наследует*, если *свойство наследуемое*, в противном случае устанавливает *значение браузера*.\n\nИспользование `unset` в примере выше установит *красный* цвет, `initial` — *чёрный*.\n\n## Каскад\n\n*Cascading Style Sheets* (CSS) переводится как *каскадные таблицы стилей*. *Ключевым словом* выступает *каскад*.\n\nЕсли несколько селекторов ссылаются на один и тот же DOM-элемент, а в CSS-правилах, привязанных к этим селекторам, затрагивается одно и то же свойство (например, `width`, `color`), то возникает конфликт: браузеру нужно решить, какое из правил следует применить к элементу, какое из них важнее (приоритетнее). Для этого существует специальный алгоритм, с которым мы познакомимся далее.\n\nПример конфликта свойства `color`:\n```html\n<div id=\"foo\" class=\"bar\" />\n```\n```css\n.bar {\n  color: white;\n}\n#foo {\n  color: red;\n}\ndiv {\n  color: white;\n}\n```\nКонкретно в данном примере после всех рассчётов стилей будет применён цвет `red`. Будем разбираться, почему так.\n\n**Каскадом** (англ. `cascade`) или **каскадированием** (англ. `cascading`) называют *процесс группировки всех *таблиц стилей*, полученных из разных источников и применяемых к элементу, и *разрешения конфликтов* между *CSS-правилами* (как в примере выше.)\n\n\nВсе объявления *трёх селекторов* ниже будут применены к блоку с классом `element`.\n```html\n<div class=\"element\"></div>\n<div class=\"another-element\"></div>\n```\n```css\ndiv.element {\n  width: 200px;\n  background: #000;\n}\n.element {\n  width: 100px;\n}\n.another-element, .element {\n  color: #fff;\n}\n```\nОбъявления `width: 100px` и `width: 200px` *конфликтуют* друг с другом. *Решение* о том, какое из них *применится* в конечном счёте, зависит от *приоритета*, об *определении* которого рассказывается *ниже*.\n\n### Возможные источники стилей (браузер, автор и пользователь)\n\n*Таблицы стилей* (англ. `style sheets`) могут иметь *3* разных **источника** (англ. `origins`): *стили браузера*, *автора* и *пользователя*.\n\n#### Стили браузера\n\n**Стили браузера**, **объявления браузера** (англ. `user-agent declarations`, `browser declarations`) — это *стили*, которые *задаются по умолчанию браузером*.\n\nВы можете воспринимать *стили браузера* как *стили по умолчанию*, поскольку они *работают даже тогда*, когда *ваш HTML-документ не содержит CSS-объявлений*.\n\n![image](https://user-images.githubusercontent.com/22237384/164483287-d34c5695-09ab-4483-9494-8f27df1e1f85.png)\n\nЭти *стили невозможно отключить*, но их очень *просто перезаписать*.\n\n\nНекоторые *стили браузера* можно найти в *папке* с *файлами браузера*.\n![image](https://user-images.githubusercontent.com/22237384/164482291-c97f48cb-69eb-4bd7-9d5c-c0621030946a.png)\n\n<!-- Что чаще всего отличается стилями между браузерами, так это псевдоэлементы по типу `::scrollbar`, `::selection` и так далее. -->\n\nПример *стилей браузера*: *ссылки* `<a>` имеют *синий цвет* и *подчёркнуты* (англ. `underlined`).\n\n#### Как добиться консистентности в разных браузерах\n\n*Каждый браузер* задаёт *свои стандартные стили*, поэтому *страница* может *отображаться* в браузерах *по-разному* и разработчики часто *подключают файлы* вроде `reset.css` или `normalize.css`, чтобы *сбросить стили браузера*.  \n\n#### Стили автора\n\n**Стили автора**, **объявления автора** (англ. `author declarations`) — стили, *подключенные к HTML-документу* через тэг `<style>` и указанные в этом тэге либо напрямую, либо по ссылке на CSS-файл.\n\nОчевидно, что в данном случае под *автором* подразумевается *разработчик* (англ. `developer`) данного *сайта*, который писал код *HTML-документа*.\n\n#### Самописные стили в Chrome Dev Tools\n\n*Стили*, которые вы самостоятельно *добавляете* в *Chrome Dev Tools*, также относятся к *стилям автора* и *имеют одинаковый приортитет* с теми стилями, что написал *разработчик HTML-документа*. \n*Ваши рукописные стили добавляются* в *новый файл* `inspector-stylesheet`, который *создаёт* сам *Chrome*.\n![image](https://user-images.githubusercontent.com/22237384/164485009-991603ce-d03c-49e8-9316-8a7dabe21700.png)\n\n*Стили*, *добавленные* в *блок* `element.style {}` в *Chrome Dev Tools*, также считаются *стилями автора* и *задаются напрямую* в *атрибуте* `style` *выбранного HTML-элемента*.  \n![image](https://user-images.githubusercontent.com/22237384/164488974-8ec1189f-01e4-41e7-bdf2-c2ff47a7e2ff.png)\n\n\n#### Стили пользователя\n\n**Стили пользователя**, **объявления пользователя** (англ. `user declarations`, `user styles`) — стили, которые *пользователь* указывает в *настройках браузера*.\n\nРанее *пользовательские стили* можно было объявить в файле `Custom.css`, который можно было найти в папке с файлами браузера, но разработчики решили отключить эту возможность. \n![image](https://user-images.githubusercontent.com/22237384/164477719-f222881e-adba-45c7-ad10-c6d98d2ee7fb.png)\n\nВ наше время в качетве *альтернативы файлу Custom.css* сейчас могут *использоваться* различные *расширения Chrome* (англ. `Chrome extensions`). Например, [*расширение User CSS*](https://chrome.google.com/webstore/detail/user-css/okpjlejfhacmgjkmknjhadmkdbcldfcb?hl=en).\n\n<!-- Поскольку *стили пользователя *настраивались** довольно *редко*, к тому же делать это было *не* слишком *удобно*, *разработчики Chrome отключили эту возможность* очень много лет назад. -->\n\nРаньше же пользовательские стили можно было объявить следующим образом:\n1) Заходим сюда: chrome://version/\n2) Копируем `Путь к профилю` и открываем в проводнике.\n![image](https://user-images.githubusercontent.com/22237384/164478524-130fceff-7982-444d-9fb0-0f8b8c978be9.png)\n3) Находим папку `User StyleSheets` и в ней файл `Custom.css` (сейчас вы его уже не найдёте, а если добавите, то он учитываться не будет).\n4) Изменяем или добавляем новые CSS-объявления в файл.\n\n*Пример файла* [Custom.css](https://gist.github.com/colinangusmackay/5273896)\n\n\n### Алгоритм (порядок) каскада\n- [1) Важность (Importance)](#1-важность-importance)\n- [2) Специфичность (Specificity)](#2-специфичность-specificity)\n- [3) Порядок источника (Source Order)](#3-порядок-источника-source-order)\n\nБудем *рассматривать каскад* относительно *одного HTML-элемента*.\n\n1) Сперва *находятся все CSS-объявления* (англ. `declarations`), принадлежащие *рассматриваемому элементу*.\n2) Если *несколько правил конфликтуют* друг с другом, то *сравнивается* их *источник* (*важность*). *Применяется объявление из более приоритетного источника*.\n3) Если *важность совпадает*, то *сравнивается специфичность*. *Применяется объявление с более специфичным селектором*.\n4) Если *важность* и *специфичность совпадают*, то *сравнивается порядок источника*. *Применяется объявление*, которое *объявлено позже* в *коде*.\n\n\n\n#### 1) Важность (Importance)\nПроизводится *упорядочивание объявлений* по **важности** (англ. `importance`), зависящей от *источника* (origin).\nЧем *ниже по списку ниже*, тем *выше приоритет*.\n  1) Объявления *браузера* (user-agent declarations).\n  2) Объявления *пользователя* (user normal declarations).\n  3) Объявления *автора* (author normal declarations).\n  4) *CSS Animations*\n  5) Объявления *автора* с добавлением *ключевого слова* `!important` (author important declarations).\n  6) Объявления *пользователя* с добавлением *ключевого слова* `!important` (user important declarations).\n  7) Объявления *браузера* с добавлением *ключевого слова* `!important` (user-agent important declarations).\n  8) *CSS Transitions*  \n\n*Важность повышается* за счёт *ключевого слова* `!important`.\n```css\n* {\n  box-sizing: border-box !important;\n}\n```\nЕсли *два конфликтующих правила* содержат `!important`, то *применится более специфичное* из них.\n```css\n#article { padding: 24px !important; } /* применится это правило: id специфичнее класса */\n.article { padding: 16px !important; } \n```\n\nЕсли к одному правилу ключевое слово `!important` применен дважды (`!important!important`), то важность данного правила не только не изменится, но произойдёт синтаксическая ошибка и правило просто не применится.\n```css\n#article { padding: 24px !important; }\n#article { padding: 24px !important!important; } // синтаксическая ошибка \"semi-colon expected\" (ожидается \";\")\n```\n\n#### 2) Специфичность (Specificity)\nЕсли *важность* и *источник совпадают*, то сравнивается **специфичность селекторов** (selector's specificity), которым принадлежат *объявления*.  \nВ примере ниже *применится* объявление `width: 200px`, поскольку его *селектор специфичнее* (подробнее об этом будет дальше).\n```css\ndiv.element { /* специфичность: 0,0,1,1 */\n  width: 200px; /* применится это объявление */\n  background: #000;\n}\n.element { /* специфичность: 0,0,1,0 */\n  width: 100px;\n}\n```\n\n#### 3) Порядок источника (Source Order)\nЕсли *важность*, *источник* и *специфичность совпадают*, приоритет объявлений зависит от их *расположения в коде*, **порядка источника** (source order). *Объявленный позже блок кода* (правило, объявление) имеет *больший приоритет*. Это *работает так же*, если *правила* находятся в *разных CSS-файлах*: *приортитетнее* считается то *правило*, чей *файл подключен позже*.\n```css\n/* два правила */\np {\n  color: green;\n}\n\np {\n  color: red; /* применится это объявление (важность, источник, специфичность совпадают и объявлено позже) */\n}\n```\n```css\n/* два объявления */\n.article {\n  color: green;\n  color: red; /* применится это объявление */\n}\n```\n```html\n<!-- два файла -->\n<link href=\"styles.css\" rel=\"stylesheet\" />\n<!-- правила (и их объявления) в файле ниже приоритетнее -->\n<link href=\"styles2.css\" rel=\"stylesheet />\n```\n\n*Значение*, полученное в *результате каскада*, называется **каскадным значением** (англ. `cascaded value`).\n\n\n#### Хороший пример проверки знаний каскада\n\nПри вычислении каскада сохраняется не только последнее значение, но вся цепочка значений по их приоритету. Таким образом, если самое приоритетное правило будет отключено (например, при помощи JavaScript или Dev Tools в браузере), то вместо него применяется второе по значимости правило. Сейчас разберём большой пример и отсортируем правила в порядке их приоритетности для браузера.\n```html\n<div id=\"a\" class=\"a\" style=\"color: pink\">\n  Notes\n</div>\n```\n```css\n/* файл со стилями автора */\n#a {\n  color: red;\n}\n\n.a {\n color: orange;\n}\n\n#a#a#a {\n  color: yellow;\n}\n\n#a#a#a {\n  color: green !important!important;\n}\n\n#a {\n  color: lightblue !important;\n}\n\n.a {\n  color: blue !important;\n}\n\n#a {\n  color: purple !important;\n}\n\ndiv {\n  color: blue;\n}\n\nhtml {\n  color: yellow;\n}\n```\nНачнём с атрибута `style`, далее пойдём по порядку. Будем записывать селекторы в порядке их приоритетов в массив и сортировать его по убыванию приоритета правил.\n\nДля начала предположим, что атрибут `style` и весь `css` в примере располагаются в коде программы, то есть они написаны программистом (автором) и имеют одинаковый источник, если к правилам не применяется ключевое слово `!important`. \n\n1) Атрибут `style` имеет специфичность `1, 0, 0, 0`. Пока не с чем его сравнивать, просто добавляем в массив: [`style`].\n2) Селектор `#a` имеет тот же источник, что и `style`. Переходим к рассмотрению (вычислению) специфичности (второй этап алгоритма): `0, 1, 0, 0`. Специфичность ниже, чем у `style`. Значит массив приоритетов: [`#a`, `style`].\n3) Селектор `.a` имеет тот же источник, а его специфичность: `0, 0, 1, 0`, что ещё ниже, значит имеем массив: [`style`, `#a`, `.a`].\n4) Селектор `#a#a#a` имеет тот же источник, а его специфичность: `0, 0, 3, 0`, что ещё выше `#a` и ниже `style`: [`style`, `#a#a#a`, `#a`, `.a`].\n5) Селектор `#a#a#a!important!important` имеет неправильный синтаксис для правила (поскольку нельзя ключевое слово `!important` применить дважды к одному правилу), поэтому правило просто не применится и в массив его добавлять нет смысла. \n6) Селектор `#a!important` имеет источник `author + important`, что выше всех предыдуших правил, его специфичность: `0, 1, 0, 0`. Имеем: [`#a!important`, `style`, `#a#a#a`, `#a`, `.a`].\n7) Селектор `.a!important` имеет источник `author + important`, поэтому он приоритетнее, чем 1)-5). При этом его специфичность: `0, 0, 1, 0`, что меньше, чем у 6), а значит массив примет вид: [`#a!important`, `.a!important`, `style`, `#a#a#a`, `#a`, `.a`].\n8) Селектор `#a!important-2` совпадает с селектором 6), поэтому имеет тот же источник и ту же специфичность, то при этом позже объявлен в коде, а значит считается более приоритетным. Тогда имеем: [`#a!important-2`, `#a!important`, `.a!important`, `style`, `#a#a#a`, `#a`, `.a`].\n9) Селектор `div` имеет источник автора и его специфичность `0, 0, 0, 1`, значит он ниже по приоритету, чем все предыдущие селекторы: [`#a!important-2`, `#a!important`, `.a!important`, `style`, `#a#a#a`, `#a`, `.a`, `div`]\n10) Селектор `html` является родителем всех элементов, а значит и элемента, который мы рассматриваем, при этом свойство `color` относится к свойствам текстовых полей, а значит является наследуемым. Наследование не имеет специфичности, поэтому любой из селекторов выше его перебивает по приоритету. Унаследование свойства происходит лишь в том случае, если нет ниодного правила, которое задаёт это свойство напрямую элементу. Таким образом, наследование можно добавить в самый конец нашего массива: [`#a!important-2`, `#a!important`, `.a!important`, `style`, `#a#a#a`, `#a`, `.a`, `div`, `html`].\n\nКак можно увидеть, к элементу применится правило `#a!important-2`, то есть фиолетовый цвет (`color: purple`).\n\n\n### Скорость выполнения селекторов браузерами\n\nОбработка некоторых селекторов занимает в несколько раз больше (в 5-6 раз) времени, чем других селекторов.  \nТем не менее, сейчас в браузерах настолько всё оптимизировано, что данная проблема является одной из наименьших из существующих.  \n\nВ интернете можно найти множество тестов, которые показывают, что при тысячах элементов на странице даже достаточно сложные селекторы обрабатываются считанные миллисекунды.\n\n# Подходы к написанию CSS\n\n- [Проблемы CSS](#проблемы-css)\n- [Некоторые рекомендации](#некоторые-рекомендации)\n- [БЭМ](#бэм)\n- [OOCSS](#oocss)\n- [SMACSS](#smacss)\n- [Atomic CSS](#atomic-css)\n- [AMCSS](#amcss)\n- [Enduring CSS](#enduring-css)\n\n## Проблемы CSS\n\n*CSS* изначально создавался для стилизации элементов HTML-разметки и ни для чего более. Он обладает огромной гибкостью, позволяя решить любую задачу множеством способов. \n\n* Обращение к элементам происходит при помощи селекторов. Существует *множество способов обратиться* к одному и *тому же элементу*.\n```html\n<div class=\"header\">\n  <p id=\"header-title\" class=\"title\"></p>\n</div>\n```\n```css\n#header-title {}\n.title {}\n[class=\"title\"]\np {}\n* {}\n```\n* Многие из этих *способов* можно *комбинировать*.\n```css\n.title#header-title\np[class=\"title\"]\np.title {}\n.header > * {}\n.header #header-title {}\n```\n* Один селектор может выбирать несколько различных элементов на странице.\n```css\n<div class=\"header\">\n  <p class=\"title\"></p>\n</div>\n\n<div class=\"article\"></div>\n  <span class=\"title article-title\"></span>\n</div> \n```\n```css\n.title {} /* выберет оба элемента span и p */\ndiv {} /* выберет и header, и article */\n```\n* *CSS-правила* могут *конфликтовать* друг с другом и эти *конфликты решаются каскадом*, но *разработчик* всё время должен *знать важность*, *специфичность* и *место в коде* для *каждого объявления*.\n\n* Всё это усугубляется *отсутствием модульности кода*. Имеется *только глобальная область видимости*. \n\nУчитывая всё сказанное выше, возможны случаи, когда *стили нового* элемента страницы *влияют* на *стили* уже *существующего* элемента, или, наоборот, *новый* элемент *зависит* от стилей *существующего*. \n\nТакой код *трудно поддерживать, расширять* и над ним *практически невозможно работать* в *команде*. \n\nВсё это стало основной *предпосылкой к появлению методологий* в CSS. Каждая из них по-своему *накладывает* некоторые *ограничения*, *уменьшая допустимую сложность селекторов*, вносит *подобие модульности* или *разделения ответственности* (Separation of Concerns).\n\n## Некоторые рекомендации\n\n*Правила специфичности* не так сложны при *конкретных* селекторах. Но когда селекторов *сотни* в одном файле, *трудно* слёту *рассчитать* (а потом держать это в уме), *какой стиль* будет *применён в итоге* или *как* отельное *правило* (rule) *повлияет* на *элементы* страницы.\n\nЕсть некоторые *рекомендации*, соблюдение которых поможет не только значительно *улучшить читабильность* кода, но иногда тем или иным образом *оптимизировать* его:\n* Стараться использовать *только самые распространённые простые селекторы*, покрывающие практически любые случаи - *селекторы по классу*.\n```css\n.content p {} /* не очень хорошо */\n.content #title {} /* лучше, но есть недочёты (будет сказано дальше) */\n.content .title {} /* хорошо */\n```\n* То же самое касается и *комбинаторов*: в большинстве случаев лучше *ограничиться комбинатором потомков*.\n```css\n.content ~ .title {} /* результат не очень то предсказуем */\n.content > .title {} /* можно иногда использовать, но не все о нём помнят, что сказывается на читаемости кода */\n.content .title {} /* хорошо */\n```\n* *Малая вложенность селекторов*. Желательно, *не более двух селекторов* по классу (`.parent .child`). Это *упрощает разбор селектора* и *ускоряет поиск* соответствующих ему *элементов* как для разработчика, так и для браузера.\n```css\n.page .content .text-block .title {} /* плохо */\n.text-block .title {} /* хорошо */\n.text-block .title:hover {} /* тоже хорошо */\n```\n* Как можно *меньше использовать inline-стили* (ведь их практически *невозможно переопределить*). Вместо этого *лучше выносить CSS-код* в *классы*.\n```css\n<p class=\"title\" style=\"color:red\">Text</p> /* плохо */\n<p class=\"title title--red\">Test</p> /* хорошо */\n```\n* Стараться *избегать* использования *ключевого слова* `!important`, предпочитая ему решение проблемы перекрытия стилей при помощи *специфичности*. Лишь иногда (но очень редко) `!important` является единственным решением проблемы (например, если перекрываются чрезвычайно специфичные стили сторонней библиотеки).\n\nОдной из интересных особенностей CSS является то, что простой селектор можно использовать дважды и специфичность увеличится, а выборка элементов не изменится. Это может помочь, если стили сторонней библиотеки по какой-то причине импортируются позже, чем стили проекта.\n```html\n<div class=\"article\"></div>\n```\n```css\n/* перекрыть стили сторонней библиотеки ниже можно специфичностью дублированием селектора: */\n.article.article {/* ... */} /* специфичность: 0,0,2,0 */\n/* можно и так: */\ndiv.article {/* ... */} /* специфичность: 0,0,1,1 */\n/* но лучше не делать вот так: */\ndiv.article {/* ... */ !important} /* важность: author important declaration */\n\n/* стили из сторонней библиотеки */\n.article {/* ... */} /* специфичность: 0,0,1,0 */\n```\n\n* При *переопределении CSS-правил* в *своём коде* следует *больше полагаться на специфичность* и *меньше на порядок в коде*. Тогда приоритет стилей не изменится, если блоки кода в одном файле поменяются местами или CSS-файлы подключатся не в том порядке. \n\nВ *примере* ниже *специфичность двух селекторов одинакова* и *цвет* текста *зависит* от *порядка объявления правил* в *коде*. Если *поменять* правила *местами*, то применится *другой цвет*.\n```html\n<div class=\"page\">\n  <div class=\"header\">\n    <p class=\"title\">Notes</p> \n  </div>\n</div>\n```\n```css\n.page .title {\n  color: red;\n}\n\n.header .title {\n  color: green; /* применится этот цвет */\n}\n```\n* При использовании *сторонних библиотек* стоит *обращать внимание* именно на *порядок подключения CSS-файлов*: нужно *подключать свою таблицу стилей последней*.\n```html\n<link rel=\"stylesheet\" href=\"external-lib.min.css>\n<link rel=\"stylesheet\" href=\"styles.css>\n```\n\n* Удалять неиспользуемые стили и не подключать большие библиотеки стилей (по типу bootstrap) целиком, если планируется использование лишь малой части (меньше 20%) их функциональности. Это уменьшит время загрузки CSS-файла, его парсинга и построения CSSOM.\n\n### Почему лучше избегать использования селекторов по id и использовать селекторы по классу\n\n* Один *элемент не может иметь два идентификатора*, но *может иметь сколь угодно классов*.\n```html\n<!-- это не будет работать! -->\n<p id=\"title header-title\"></p>\n<!-- это тем более! игнорирование или ошибка --> \n<p id=\"title\" id=\"header-title\"></p> \n<!-- а это работает -->\n<p class=\"title header-title\"></p>\n```\n\n* *Селектор по id* выбирает *лишь первый элемент* на *странице*.\n```html\n<div>\n    <p id=\"title\"></p>\n    <!-- игнорирование или ошибка (идентификатор должен быть уникален) -->\n    <p id=\"title\"></p>\n</div>\n```\n* Если есть *только селекторы по классу*, *специфичность вычисляется проще*.\n```css\n.page {} /* 0,0,1,0 */\n.content .title {} /* 0,0,2,0 */\n.content .title:hover {} /* 0,0,3,0 */\n```\n\nВ случае же использования атрибутов `id` и `class`, количество возможных комбинаций возрастает. Больше комбинаций — больше сложность.\n```css\n#page #title {} /* 0,2,0,0 (может перезаписать все правила ниже) */\n.page #title {} /* 0,1,1,0 */\n #page .title {} /* 0,1,1,0 */\n.page .title {} /* 0,0,2,0 */\n```\n\n## БЭМ\n\n**Блок-Элемент-Модификатор** (БЭМ, BEM) — самая популярная *методология CSS* на данный момент.\n\n* **Блок** (Block) — *переиспользуемый элемент* сайта.\n* **Элемент** (Element) — некоторая *часть блока*, *не* имеющая *функционального смысла вне* блока.\n* **Модификатор** (Modifier) — *свойство блока* или *элемента*, меняющие его *внешний вид* или *поведение*.\n\nВозможный *синтаксис БЭМ*\n```css\n.block_element-modifier {}\n/* или */\n.block__element--modifier {}\n```\n\n### БЭМ в CSS\n```css\n.page {} /* блок */\n.page__content {} /* элемент */\n\n.content {} /* блок */\n\n.articles {} /* блок */\n\n.article {} /* блок */\n.article__title {} /* элемент */\n.article__title--bold {} /* модификатор */\n.article__title--red {} /* модификатор */\n.article__image {} /* элемент */\n.article__image--small {} /* модификатор */\n.article__image--large {} /* модификатор */\n```\n### БЭМ в SCSS\n```scss\n.page {\n  /* any css for .page */\n  \n  &__content {\n    /* any css for .page__content */\n  }\n}\n\n.content {}\n\n.articles {}\n\n.article {\n  /* any css for .article */\n\n  &__title {\n    /* any css for .article__title */\n\n    &--bold {\n      /* any css for .article__title--bold */\n    }\n\n    &--red {\n      /* any css for .article__title--red */\n    }\n  }\n}\n```\n\nСтоит отметить, что *любой DOM-элемент* в рамках БЭМ может быть и *блоком*, и *элементом* *одновременно*.  \nВ примере ниже *div* является *блоком article* и *элементом item блока content*\n```html\n<div class=\"content\">\n  <div class=\"content__item article\">\n    <p class=\"article__title\"></p>\n  </div>\n</div>\n```\n\n### Преимущества и недостатки БЭМ\n*Преимущества БЭМ*\n* *Модульность* кода и *изолированность модулей* друг от друга.  \n* *Простая специфичность*.  \n\n*Недостатки БЭМ*\n* Слишком *длинные названия классов*.\n\n### Пример использования *БЭМ* с *React*\n```js\nimport React from 'react';\n\nconst articles = [{ title: 'BEM' }, { title: 'React' }];\n\nconst renderArticle = (item, index) => (\n  <div className=\"article\" key={index}>\n    <p className=\"article__title\">\n      <span className=\"article__title--red\">{`Article #${index}:`}</span>\n      {item.title}\n    </p>\n    <img className=\"article__image\" />\n  </div>\n);\n\nconst Articles = () => (\n  <div className=\"articles\">\n    {articles.map(renderArticle)}\n  </div>\n);\n\nconst render = () => (\n  <div className=\"page\">\n    <div className=\"page__content content\">\n      <Articles />\n    </div>\n  </div>\n);\n```\n\n## OOCSS\n\n**Объектно-Ориентированный CSS** (Object Oriented CSS, OOCSS) — *подход*, использующий *преимущества ООП* в *CSS*.  \n\nОсновная *идея* заключается в *переиспользуемости кода*: *принцип Don't Repeat Yourself* (DRY).\n\n**Объектом** в этом подходе выступает *любой визуально повторяющийся шаблон*, который можно выделить как *фрагмент кода*.\n\n*Элементы* страницы задаются *объектными классами*, являющиеся *отдельными объектами* в таблицах стилей.\n\nВ отличие от многих, подход *OOCSS не устанавливает правил наименования* классов; *не запрещает* использовать тэги, id и прочее, что позволяет *сочетать* его с *другими подходами*.\n\n### Первое правило подхода OOCSS: разделение Структуры и Оформления\n\n**Структура** (Structure) — совокупность *невидимых* для пользователя *свойств* элемента.  \nПример *структурных свойств*: height, width, margin, padding, overflow (размер, позиционирование и прочее).\n\n**Оформление** (Skin) — совокупность *видимых свойств* элемента.  \nПример *свойств оформления*: color, font, shadow, gradient.  \n\nДругими словами: *структура* состоит из инструкций о том, *как* все *расположено*, а *оформление* определяет, *как выглядит* макет.\n\nТакое *разделение* позволяет *размещать* копию объекта в *любое место* сайта, *не переопределяя* при этом *существующие стили*.  \nЭта копия *расширяется дополнительными стилями* через *class-атрибут* (часто сразу *несколькими классами*).\n\n### Второе правило подхода OOCSS: разделение Контейнера и Контента\n\n**Контентом** является любой элемент, расположенный в каком-то другом элементе — **контейнере**.  \n```css\n.container .content\n```\nСуть заключается в том, чтобы *использовать комбинатор потомков* как можно *реже*.  \nЕсли *контент не зависит* от конкретного *контейнера*, мы можем *переиспользовать код чаще*.\n\nНапример, вместо\n```css\n.sidebar {}\n.sidebar .menu {}\n.sidebar .menu .menu-item {}\n```\nможно *разделить контейнер и контент* следующим образом\n```css\n.sidebar {}\n.menu {}\n.menu-item {}\n```\nчто даёт возможность использовать *меню* где-нибудь ещё.\n\n### OOCSS в CSS\nИщем *повторяющиеся блоки кода*:\n```css\n.button {\n  width: 100px;\n  height: 40px;\n  padding: 4px 8px;\n  background-color: #000;\n  border-radius: 4px;\n  color: #fff;\n}\n\n.button-wide {\n  width: 200px;\n  height: 40px;\n  padding: 4px 8px;\n  background-color: #000;\n  border-radius: 4px;\n  color: #fff;\n}\n```\n```html\n<div class=\"button\"></div>\n<div class=\"button-wide\"></div>\n```\n*Выносим* их в *классы*:\n```css\n.skin-button {\n  background-color: #000;\n  border-radius: 4px;\n  color: #fff;\n}\n\n.structure-button {\n  height: 40px;\n  padding: 4px 8px;\n}\n\n.button {\n  width: 100px;\n}\n\n.button-wide {\n  width: 200px;\n}\n```\n```html\n<div class=\"button skin-button structure-button\"></div>\n<div class=\"button-wide skin-button structure-button\"></div>\n```\n### OOCSS в SCSS\nПри использовании *OOCSS* с *чистым CSS*, *атрибут class* у DOM-элементов сильно *разрастается*, но что, если...\n```scss\n@mixin skin-button {\n  background-color: #000;\n  border-radius: 4px;\n  color: #fff;\n}\n\n@mixin structure-button {\n  height: 40px;\n  padding: 4px 8px;\n}\n\n.button {\n  @include skin-button;\n  @include structure-button;\n  width: 100px;\n}\n\n.button-wide {\n  @include skin-button;\n  @include structure-button;\n  width: 200px;\n}\n```\n```html\n<div class=\"button\"></div>\n<div class=\"button-wide\"></div>\n```\nХотя, имея в арсенале *миксины*, можно сделать *ещё лучше* (не совсем OOCSS) в этом примере:\n```scss\n@mixin button-defaults {\n  background-color: #000;\n  border-radius: 4px;\n  color: #fff;\n  height: 40px;\n  padding: 4px 8px;\n}\n\n.button {\n  @include button-defaults;\n  width: 100px;\n}\n\n.button {\n  @include button-defaults;\n  width: 200px;\n}\n```\n\n### Преимущества и недостатки OOCSS\n*Преимущества OOCSS*\n* Хорошая *переиспользуемость кода*.  \n\n*Недостатки OOCSS*\n* *Сильная связанность кода ухудшает* его *поддержку*: *классы* достаточно *общие*, могут использоваться *повсеместно* (нельзя просто изменить их, скорее всего придётся менять разметку).\n\n## SMACSS\n\n**SMACSS** — *масштабируемая и модульная архитектура для CSS* (Scalable and Modular Architecture for CSS).\n\nИдея заключается в *разбиении стилей на слои* и тесно связана с *принципом разделения ответственности* (SoC). Каждый слой выполняет *только свои обязательства*. Это позволяет улучшить поддержку кода.\n\n*Стили разбиваются* на *5 слоёв*:\n* **Базовые стили** (base rules) — стили *основных элементов* страницы. Обычно *тэги* (body, div, input, ...), *псевдоклассы* и *псевдоэлементы*, *атрибуты кроме class и id* (изредка class: например, стилизация custom select).\n```css\n/* базовые стили */\ninput {}\ninput:focus {}\n*:hover {}\n*::selection {}\n```\n* **Стили макета** (layout rules) — стили *глобальных элементов* страницы (header, footer, sidebar, ...), *разбивающих страницу* на *блоки с контентом*, состоящие из *модулей*. Автор подхода предлагает *использовать id*, чтобы подчеркнуть *уникальность* элементов, но можно этого *не делать*. *Префикс*: `layout-`, `l-` или `grid-`.\n```css\n#main { width: 40%; } /* глобальный элемент */\n#header { width: 100%; } /* глобальный элемент */\n\n/* стили макета ниже навешивается на предков глобальных элементов (например, на body) */\n.l-fixed #main { width: 320px; }\n.l-fixed #header { width: 320px; }\n```\n* **Стили модулей** (modules rules) — стили *переиспользуемых блоков* страницы. Автор подхода советует *избегать* здесь *тэгов* и *id*. *Префикс*: `module-`, где *module* — *название модуля*.\n```css\n/* стили модуля */\n.article {}\n.article-title {}\n.article-img\n```\n```html\n<div class=\"article\">\n  <p class=\"article-title\"></p>\n  <img class=\"article-img\" />\n  <div></div>\n</div>\n```\n* **Стили состояния** (state rules) — различные *состояния модулей* и *структуры* сайта. Автор *допускает* использование `!important` только в этом разделе. *Префикс*: `is-`.\n```css\n/* стили состояния */\n.button.is-disabled {}\n.tab.is-active {}\n#sidebar.is-mobile {}\n.is-hidden {}\n```\n* **Стили темы** (theme rules) — некоторые *дополнительные стили*, описывающие, *как модули* и *макет* могут *выглядеть*. Обычно *меняются* время от времени. *Необязательная категория* (стили могут быть уже учтены в других категориях). \n```css\n/* article.css */\n.article-title { color: #000; }\n\n/* theme.css */\n.article-title { color: #ff0000; } /* меняем цвет с чёрного на красный в честь какого-то события */\n```\nОбычно на *каждый слой* отводится *отдельный файл*: `base.scss`, `layout.scss`, `module-name.scss`, `state.scss`, `theme.scss`.\n\n## Atomic CSS\n\n**Атом** является *мельчайшей частицей вещества*.\n\n**Атомный CSS** (Atomic CSS) — подход, похожий на *OOCSS*, где в качестве объекта выступает *одно объявление* (свойство: значение), *отражающееся в названии класса*.\n```css\n.Mt-4 { margin-top: 8px; }\n.Fs-16 { font-size: 16px; }\n.W-320 { width: 320px; }\n```\n```html\n<p class=\"mt-4 fs-16 w-320\"></p>\n```\n*Атомный CSS* хорошо подходит для тех, кто хочет *писать макет* и *стили в одном месте*.\n\nТем не менее, *вручную* писать код с таким подходом *не* очень *удобно*, поэтому существует *инструмент* [Atomizer](https://github.com/acss-io/atomizer), который *рекурсивно обходит html* файлы и *генерирует* весь *необходимый css*.\n\n*Классы*: [reference](https://acss.io/reference)  \n*Псевдоклассы*: `a` — `:active`, `c` — `:checked`, `f` — `:focus`, `h` — `:hover`.  \n*Псевдоэлементы*: `a` — `::after`, `b` — `::before`, `fl` — `::first-letter`, `fli` — `::first-line`, `ph` — `::placeholder`.  \n*Комбинаторы*: `_` — комбинатор *потомков*, `>` — комбинатор *детей*, `+` — комбинатор *братьев*.  \n\n### Atomic CSS и CSS\n\n*Синтаксис html* в случае использования *Atomizer* следующий\n```html\n<div class=\"D(f) Jc(c) Op(0.8):h\"></div>\n<div class=\"article\">\n    <p class=\"article_C(red)\"></p>\n</div>\n```\n*Atomizer* при запуске просмотрит html и автоматически *сгенерирует* css\n```css\n.D\\(f\\) {\n  display: flex;\n}\n\n.Jc\\(c\\) {\n  justify-content: center;\n}\n\n.Op\\(0\\.8\\)\\:h:hover {\n  opacity: 0.8;\n}\n\n.article .C\\(red\\) {\n  color: red;\n}\n```\n\n### Преимущества и недостатки Atomic CSS\n*Преимущества OOCSS*\n* Относительно хорошая *переиспользуемость кода* (изменил значение в одном месте, изменилось везде).  \n* *Специфичность*: лучше, чем использование inline стилей, поскольку здесь стили хранятся в сгенерированных css файлах.\n* Можно настроить *пользовательские переменные* в Atomizer:\n```JSON\n{\n  \"1\": \"1px solid #000\",\n  \"foo\": \"2px dotted #f00\",\n}\n```\n\n\n*Недостатки OOCSS*\n* *Названия классов* содержат *описание объявления*, а не *суть элемента*, что усложняет разработку.\n* К пункту выше можно добавить, что атрибуты *class* могут достигать *невероятных размеров* с *увеличением количества объявлений*.\n* *Управление отображением* элемента находится в *HTML* (отвечающим за разметку), а должно оставаться в *CSS*.\n* *Нет встроенной поддержки* некоторых вещей, в том числе и *grid* (нужно либо подключать дополнение или писать самому).\n\n## AMCSS\n\n**Модули атрибутов для CSS** (Attribute Modules for CSS, AMCSS) — подход, использующий *атрибуты и их значения вместо классов*.\n\nКак и в многих других методологиях, идея AMCSS заключается в *логической группировке CSS* кода.  \n* **Модули** (Modules) — замена классам; описываются атрибутами; похожи на блоки и элементы в БЭМ.\n* **Вариации** (Variations) — различные состояния модулей; представлены значениями атрибутов; похожи на модификаторы в БЭМ. \n* **Черты** (Traits) — коллекция значений, имеющих одну цель; похожи на SUIT utils.\n\nОдной из *целей* подхода является исправление проблемы БЭМ со слишком длинными названиями классов.\n```html\n<!-- /* БЭМ * / -->\n<div class=\"button button--large button--primary\"></div>\n<!-- /* AMCSS * / -->\n<div am-Button=\"large primary\"></div>\n```\nПрефикс `am-` добавляется для того, чтобы не было конфликтов с другими атрибутами.\n\n### Синтаксис AMCSS\n\n*Черты* пишутся в *camelCase*, *модули* — в *PascalCase*.\n\nОтношение *родитель-ребёнок* обозначаются *дефисом*.\n\nВ качестве *селектора* используется *селектор по атрибуту* `~=`, выбирающий элементы, *содержащие* необходимый *атрибут* и *указанные слова* (разделённые пробелами) в *значении атрибута*.  \nЭто позволяет создать поведение, аналогичное классам.\n\n*Значения атрибутов*, подобно классам, *разделяются пробелами*, но при этом имеют *более широкий спектр допустимых символов*.\n\n```html\n<div am-traitName=\"name name2 mobile:name3\"></div>\n<div am-ModuleName></div>\n<div am-ModuleName-ChildElement></div>\n<div am-ModuleName=\"variation\"></div>\n```\n```css\n[am-traitName~=\"name\"] {}\n[am-traitName~=\"name2\"] {}\n[am-traitName~=\"name3\"], .breakpoint-mobile [am-traitName~=\"mobile:name3\"] {}\n\n[am-ModuleName] {\n  /* Стили модуля (блока) */\n}\n[am-ModuleName~=\"variation\"] {\n  /* Стили вариации модуля ModuleName */\n}\n\n[am-ModuleName-ChildElement] {\n  /* Стили дочернего элемента модуля ModuleName */\n}\n```\n\nСтоит обратить внимание, что *вариация не может существовать без базовых стилей модуля*.  \n*Любой* элемент, удовлетворающий второму селектору в примере ниже, удовлетворяет и первому тоже.\n```css\n[am-ModuleName] {} /* базовый атрибут и его стили */\n[am-ModuleName~=\"variation\"] {}\n```\n*Черта* же *не имеет стилей в базовом атрибуте*, но зато *черты* можно *смешивать* и *сочетать* в *любом* месте кода.\n```css\n[am-traitName~=\"name\"] {}\n```\n\nСуществует так же *другой синтаксис AMCSS*, более *близкий к БЭМ*, но *теряющий* некоторые *преимущества*:\n```html\n<div am-ModuleName am-ModuleName-variation></div>\n```\n```css\n[am-ModuleName] {}\n[am-ModuleName-variation] {}\n```\n### AMCSS и CSS\nМодули и вариации\n```html\n<div am-Article>\n  <p am-Article-Title=\"red translucent\"></p>\n</div>\n```\n```css\n[am-Article] {\n  display: flex;\n}\n\n[am-Article-Title] {\n  margin: 0;\n}\n\n[am-Article-Title~=\"red\"] {\n  color: #ff0000;\n}\n\n[am-Article-Title~=\"translucent\"] {\n  opacity: 0.5;\n}\n```\nЧерты\n```html\n<p am-font=\"primary\" am-color=\"red\"></p>\n```\n```css\n[am-font~=\"primary\"] {\n  font-size: 18px;\n  font-weight: bold;\n}\n\n[am-color~=\"red\"] {\n  color: #c20606;\n}\n```\n### Преимущества и недостатки AMCSS\nПодход *похож на БЭМ*, поэтому обладает теми же *преимуществами*:\n\n*Преимущества AMCSS*\n* *Модульность* кода и *изолированность модулей* друг от друга.  \n* *Простая специфичность*. (*специфичность* у *всех атрибутов*, в том числе и классов, одинаковая: *0,0,1,0*).  \n* Решена проблема с *длинными названиями* классов, свойственная БЭМ.\n```html\n<div class=\"select__option select__option--first select__option--selected\">\n<div am-Select-Option=\"first selected\">\n```\n* Информация о *разнородных стилях* не хранится в *одной строке*, теперь она *разбита по атрибутам*:\n```html\n<div class=\"list__item list__item--even article\">\n<div\n  am-List-Item=\"even\"\n  am-Article\n>\n```\n* Более *широкий спектр допустимых символов* в названиях вариаций.\n\n*Недостатки AMCSS*\n* Большая часть всего, что создавалось *ранее*, делалось на *классах*.  \nПринципиально новый подход требует *других методов работы* не только с *CSS*, но и с *DOM* тоже.  \nМожет *пострадать поддержка* некоторых *библиотек*.  \n* В случае использования *валидатора* для *проверки корректности кода*, нужно также добавлять *приставку* `data-`, что делает код длиннее.\n```html\n<div data-am-Article>\n```\n\nЕсли *рост количества классов* элемента *расширяет HTML в ширину*, то *рост количества атрибутов расширяет* его *в высоту*.  \nЭто *не является однозначным минусом или плюсом*, но код может сильно разрастить в случае активного использования черт, что может быть не просто исправить.  \n\n## Enduring CSS\n\n**Enduring CSS** (eCSS) — *выносливый CSS*.\n\nОсновная концепция: **изоляция**. \n\nКод *Enduring CSS* состоит из *компонент*. \n\n**Компонента** — изолированная переиспользуемая единица кода, не имеющая зависимостей и контекста. Её можно удалить без риска утечки стилей.\n\nЕсли *нужен компонент*, *похожий* на уже *существующий*, то всё равно *создаётся абсолютно новый компонент*. *Написанный* для одного компонента *код* *не может быть переиспользован* в другом, даже если различия компонент незначительны.\n\n# Динамический CSS\n\nДавно прошло то время, когда страницы были статическими.  \nСейчас на большинстве сайтов элементы появляются и исчезают, меняют свои цвета, размеры и прочие характеристики, реагируя на действия пользователя.  \n\nЧтобы добиться динамичности, нужно менять стили.  \nСуществует несколько способов это делать.\n\n## Добавление и удаление классов по условию\n\nМногие *элементы DOM* могут иметь *больше одного класса*, причём некоторые из этих классов могут быть *динамическими* (появляются и исчезают из атрибутов *по условию*).  \n\n### Чистый JavaScript (работа с DOM)\n\nВ примере ниже чёрная кнопка становится красной при наведении мыши (поведение, аналогичное `:hover`).\n```html\n<button class=\"button\"></div>\n<style>\n  .button {\n    width: 100px;\n    height: 40px;\n    background: #000;\n  }\n  .button--red {\n    background: #f00;\n  }\n</style>\n<script>\n  const button = document.querySelector(\".button\");\n  button.onmouseenter = () => {\n    button.classList.add('button--red');\n  }\n\n  button.onmouseleave = () => {\n    button.classList.remove('button--red');\n  }\n</script>\n```\n\n### React и библиотека classnames\n\nВ React мы не работаем с HTML и DOM напрямую, их заменяют JSX и Virtual DOM.  \nЭто даёт нам возможность проводить вычисления атрибутов, в том числе и атрибута className.  \n```jsx\n<div className={`article__image ${isLarge ? 'article__image--large' : ''} ${isInverted ? 'article__image--inverted' : ''}`}>\n```\nВ примере выше картинка статьи может становиться больше или переворачиваться в зависимости от значения переменных `isLarge` и `isInverted`.  \nТакой код писать не только не удобно, но нужно ещё и не допусктить появления лишних значений, пробельных символов и переносов строк.\n\nВ таких случаях можно прибегнуть к использованию *[библиотеки classnames](https://github.com/JedWatson/classnames)*\n```js\nimport cx from 'classnames';\n/* ... */\nconst articleImageClasses = cx({\n  article__image: true,\n  'article__image--large': isLarge,\n  'article__image--inverted': isInverted,\n});\n/* или */\nconst articleImageClasses = cx('article__image', {\n  'article__image--large': isLarge,\n  'article__image--inverted': isInverted,\n});\n```\n```jsx\n<div className={articleImageClasses} />\n```\n*Функция cx* принимает *сколь угодно параметров*.  \nЕсли в качестве *параметра* выступает *объект*, рассматриваются *его ключи и их значения*.  \n*Ложные* (falsy) значения *игнорируются*.  \n*Лишние пробельные символы убираются*.\n\n## CSS-in-JS\n\nCSS-in-JS — подход написания CSS при помощи JavaScript, имеющий множество реализаций.  \nКак и другие подходы, он вносит идеи о решении проблем в CSS.  \n\nВ большинстве CSS-in-JS библиотеках есть две функции:\n* **css** — функция, которая принимает CSS в виде текста, генерирует название класса, создаёт новое правило на основании сгенерированного названия и переданных ей стилей, добавляет правило в `<head/>`, возвращает название класса.\n* **styled** — функция, генерирующая конкретные DOM-элементы с переданными в неё стилями (стили добавляются при помощи функции `css`).\n\n\n### Встроенные стили\n\nВстроенные стили (inline styles) — стили, которые явно указываются в HTML или JSX.\n\nВстроенные стили в HTML (не относятся к CSS-in-JS, поскольку не могут быть вычислены с помощью JavaScript).\n```html\n<p style=\"color:#000;\">\n```\n\nВстроенные стили в React (атрибут style).\n```jsx\n/* isHovered, activeColor - переменные */\n<p style={{ color: isHovered ? '#f00' : '#000' }} />\n<p style={{ color: activeColor }} />\n```\n\nВстроенные стили во Vue (атрибут style с привязкой данных):\n```jsx\n/* isHovered, activeColor - переменные */\n<p v-bind:style=\"{ color: isHovered ? '#f00' : '#000' }\" />\n/* или короче */\n<p :style=\"{ color: isHovered ? '#f00' : '#000' }\" />\n<p :style=\"{ color: activeColor }\" />\n```\n\n### Преимущества и недостатки CSS-in-JS\n\nИзначально в JavaScript и в CSS не было модулей. Со временем в них появилась необходимость, тогда в JavaScript появилась первая модульная система CommonJS, а вслед за ней и стандартизированная версия ECMAScript Modules.  \n\nCSS изначально создан для стилизации документов, поэтому в нём так и не появились встроенные модули.  \nОдна глоабльная область видимости (scope): любой класс на сайте может быть применён к любому элементу.  \nПо мере роста приложения это приводит к проблемам, поэтому и появились методологии.\n\nС помощью этой функциональности решается проблема с названиями.  \n\n*Преимущества CSS-in-JS*:\n* Модульность кода. \n* Внедрение области видимости в CSS (нужные стили импортируются).\n* Явные зависимости.\n\n### Как функции css и styled работают внутри\n\nЗамечание: в JavaScript есть возможность *вызвать функцию* следующим образом.  \n```js\nconst css = (strings, ...vars) => `{ ${strings} }`;\ncss`magic` // вернёт '{ magic }'\n```\nЭто называется **тэговым шаблоном** и работает *только с шаблонными строками* \\`\\`.  \n*Первым параметром* приходит *массив из подстрок*, который получается в результате *разбиения* шаблонной строки её *переменными*, *остальные параметры* — сами *переменные*.\n```js\n`magic` // 0 переменных, массив подстрок: ['magic'], массив переменных: []\n`${2*2} > ${+true}` // 2 переменные, массив подстрок: ['', ' > ', ''], массов переменных: [4, 1]\n```\nСделано это для того, чтобы можно было *валидировать* и *заменять переменные*, а затем собрать и *вернуть новую строку*.\n\nКак работает функция `css`:\n```jsx\n// произвольная функция генерации хэша\nconst generateRandomHash = () => Math.random().toString(36).substring(7).slice(0, 5); \n\n// функция создания правила\nconst createRuleset = (className = '', styles = '') => `\n  .${className} {\n    ${styles}\n  }\n`;\n\n// получаем строку стилей из параметров тэгового шаблона\nconst getStyles = (strings = [], vars = []) =>\n  strings.map((item, index) => `${item}${vars[index] || ''}`).join('');\n  \nconst css = (strings, ...vars) => {\n  // название класса создаётся на основании хэша, обеспечивая уникальность правила\n  const className = `css-${generateRandomHash()}`;\n  // объединяем параметры тэгового шаблона в строку без изменений (здесь могла быть валидация и правка значений)\n  const styles = getStyles(strings, vars);\n  // генерируется правило\n  const ruleset = createRuleset(className, styles);\n\n  // создаётся <style /> и помещается в <head>\n  const styleElement = document.createElement('style');\n  styleElement.textContent = ruleset;\n  document.head.appendChild(styleElement);\n\n  // возвращиется сгенерированное название класса\n  return className;\n};\n\n// пример использования функции css\nconst className = css`\n  display: flex;\n  justify-content: center;\n  align-items: center;\n`;\nconst renderBlock = () => (<div className={className} />);\n```\nКак работает функция `styled`.\n```jsx\nconst styled = {\n  div: (...cssParams) => props => (<div className={css(...cssParams)} {...props} />),\n  span: (...cssParams) => props => (<span className={css(...cssParams)} {...props} />),\n  /* ... */\n};\n\n// пример использования функции styled.div\nconst Button = styled.div`\n  width: 50px;\n  height: 50px;\n  background: red;\n`;\nconst renderRedButton = props => (<Button {...props} />);\n```\n\n# Вёрстка под различные девайсы\n\n## Viewport\n\n**Виртуальное окно** (virtual window), **вьюпорт** (viewport) — *область окна*, в которой *пользователю виден контент*.  \nОбычно она *не совпадает* с *отрендеренной страницей*, поэтому появляются *полосы прокрутки* (scrollbars).\n```html\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n```\n\n*Свойство* `width` контролирует *размер вьюпорта*.  \n*Значение свойства* может быть как *фиксированным* `width=700` (от 200 до 10000), так и *специальным* `width=device-width`, которое *адаптируется под экран пользователя*.  \nПо умолчанию, если *метатег отсутствует*, *браузеры* устанавливают *своё фиксированное значение* (например, `width=980`). Это характерно только для мобильных устройств. \n\nВ случае фиксированного вьюпорта, медиазапросы `@media` перестают работать.\n```css\n@media (max-width: 320px) {} /* никогда не сработает, если вьюпорт больше 320px  */\n```\n\nСвойство `initial-scale` отвечает за уровень масштабирования (zoom level), когда страница загружена впервые.  \nЗначение `initial-scale=1` означает, что один CSS-пиксель (px) равен одному вьюпорт-пикселю.\n\nСвойства `minimum-scale` (0.0 - 10.0), `maximum-scale` (0.0 - 10.0), `user-scalable` (yes, no) отвечают за возможности масштабирования страницы пользователем. Их рекомендуется не использовать.\n\n## Виды вёрстки\n\n### Фиксированная, статическая\n\n**Фиксированная, статическая** (fixed, static) вёрстка *постоянна вне зависимости от размеров устройства*.  \n\n*Все элементы* занимают строго *определённую величину пикселей* на странице.  \nЕсли элементы *не вмещаются*, то появлявляются *полосы прокрутки*.  \nЕсли страница *слишком велика*, то она *заполняется элементами не полностью*.  \n\nДля *фиксированной вёрстки* используются **абсолютные единицы измерения** (absolute length units): `px` - пиксель (для вёрстки веб-страниц обычно используются они), `cm` - сантиметр, `in` - дюйм, и другие.\n```css\n.block {\n  width: 200px;\n}\n```\n\n### Адаптивная\n\n**Адаптивная** (adaptive) вёрстка подстраивается под *конкретные размеры экранов*, *не уделяя внимания промежуточным состояниям*.  \nТо есть при *изменении ширины* окна браузера вёрстка сайта *перестраивается только в определенные моменты* (breakpoints).\n\nРеализуется при помощи *медиазапросов* `@media` одним из *двух способов*, которые желательно *не комбинировать*:\n* Задание *минимальной ширины* \n```css\n@media (min-width: 320px) {\n  .block {\n    width: 200px;\n  }\n}\n\n@media (min-width: 480px) {\n  /* ... */\n}\n```\n* Задание *максимальной ширины*\n```css\n@media (max-width: 1024px) {\n  .block {\n    width: 400px;\n  }\n}\n\n@media (min-width: 960px) {\n  /* ... */\n}\n```\n### Резиновая, Жидкая\n\n**Резиновая, Жидкая** (Fluid, Liquid) вёрстка *использует* в стилях **относительные единицы измерения** (relative length units) и *проценты*.  \n* `%` - процент от значения свойства родителя\n* `em` - размер шрифта родительского элемента\n* `rem` - размер шрифта корневого элемента\n* `vw`\t- 1% ширины вьюпорта.\n* `vh`\t- 1% высоты вьюпорта.\n* `vmin` - меньшее из 1vh и 1vw (сравнение после приведения в пиксели)\n* `vmax` - большее из 1vh и 1vw\n\n```css\n.block {\n  width: 50%;\n}\n```\n### Отзывчивая\n\n**Отзывчивая** (responsive) вёрстка *подстраивается* под *все возможные размеры* экранов, *сочетая* в себя возможности *резиновой* и *адаптивной* вёрсток.\n```css\n@media (min-width: 320px) {\n  .block {\n    width: 200vw;\n  }\n}\n```\n\n# Препроцессоры и постпроцессоры CSS\n- [Препроцессоры](#препроцессоры)\n- [Постпроцессоры](#постпроцессоры)\n  - [Проблемы, которые помогают решить препроцессоры](#проблемы-которые-помогают-решить-препроцесоры)\n- [Возможности постпроцессоров в деталях](#возможности-постпроцессоров-в-деталях)\n  - [Сравнение SCSS и PostSCSS](#сравнение-scss-и-postcss)\n  - [Сравнение переменных в SCSS и CSS](#сравнение-переменных-в-scss-и-css)\n  - [Шаблонный селектор, миксин и их различия](#шаблонный-селектор-миксин-и-их-различия)\n  - [Плагины PostCSS](#плагины-postcss)\n\n## Препроцессоры\n\n**Препроцессор** — компьютерная программа, принимающая данные на входе и выдающая данные, предназначенные для входа другой программы.\n\n**CSS препроцессор** (CSS preprocessor) — программа, принимающая на входе свой собственный синтаксис (syntax) и генерирующая из него чистый CSS на выходе.  \n\nСинтаксический сахар (от англ. syntactic sugar) — это дополнения синтаксиса языка программирования, которые не вносят каких-то существенных изменений или новых возможностей, но делают этот язык более читабельным для человека (альтернативное удобное использование того, что было доступно ранее).\n\nПрепроцессоры расширяют функционал чистого CSS, добавляя такие опции как: миксины, вложенные правила, селекторы наследования, переменные, функции, циклы и многое другие.  \nПодобный дополнительный функционал упрощает написание, чтение и поддержку CSS кода.\n\nПопулярные препроцессоры:\n* SASS\n* LESS\n* PostCSS\n* Stylus\n\nДля использования какого-либо CSS препроцессора нужно установить соответствующего CSS компилятор.\n\n### Проблемы, которые помогают решить препроцессоры\n\n* **Модульность**. Страндартное at-правило `@import` делает дополнительный запрос к серверу, что является дорогостоящей операцией. Препроцессоры могут переопределять стандартный импорт таким образом, чтобы импортируемый файл вставлялся в исходный без лишних запросов.\n\n* **Переиспользование кода**. С помощью шаблонных селекторов и миксинов (mixin) можно переиспользовать существующий код.\n\n## Постпроцессоры\n\n**CSS-постпроцессор** делает практически то же самое, что и препроцессор, только *на вход* вместо специального синтаксиса (например, SASS-подобного) *поступает обычный, чистый CSS*, а *на выход* — его *изменённая по каким-то правилам версия*.\n\nНа данный момент *PostCSS* является *единственным постпроцессором*. \n\nДля *изменения CSS* в *постпроцессинге* используются **плагины**, у каждого из которого стоит *определённая задача* по *изменению CSS*. *Каждый плагин* представлен *сторонним пакетом* и *подключается отдельно*. *По умолчанию плагинов нет* и *PostCSS ничего не делает*: лишь *возвращает данный* ему *CSS файл* обратно *без изменений*.  \n\n### Алгоритм работы PostCSS\n* *CSS-файл поступает* на вход и *парсится*.\n* *Первый плагин* как-то *изменяет CSS*.\n* ...*n-ый плагин* как-то *изменяет CSS*...\n* *Последний плагин* как-то *изменяет CSS*.\n* Результат работы последнего плагина преобразуется в строку и записывается в выходной CSS-файл.\n\nПример: пусть у постпроцессора есть только плагин для *минификации CSS*. Если отдадим постпроцессору обычный *CSS-файл*, он его спарсит, минифицирует при помощи плагина и вернёт изменённую версию.\n\n## Возможности постпроцессоров в деталях\n\n### Сравнение SCSS и PostSCSS\n\nSASS содержит некоторый определённый набор возможностей, которые устанавливаются в виде модуля. Вне зависимости от того, собираетесь вы использовать их все или лишь малую часть из них (например, только переменные или вложенные селекторы), будут установлены все возможности языка.\n\nPostCSS по умолчанию не содержит ничего кроме API, который позволяет подключать плагины, написанные на JavaScript. \n\nМожно ли настроить в PostCSS всё то, что доступно в SCSS? Можно, но каждая возможность будет установлена в виде отдельного плагина (или набора плагинов).\n\nТаким образом, PostCSS даёт вам большую гибкость: у вас появляется возможность установливать только то, что вы собираетесь использовать. Вы можете установить любые плагины из сотен доступных или даже написать свои. Записываем в плюсы PostCSS эффективное использование памяти проекта и гибкость (возможность кастомизации), SCSS не кастомизируется вообще.\n\nС другой стороны, использование PostCSS усложняет настройку проекта, а также приводит к ситуации, что каждый проект содержит свою конфигурацию, что заставляет разработчиков каждый раз привыкать к новому набору плагинов переходя с проекта на проект. Записываем в плюсы SCSS лёгкость настройки и низкий порог вхождения для новых разработчиков на проекте.\n\nЧто же выбрать? Выбирайте то, что вам больше подходит на данном проекте в соответствии с плюсами и минусами выше. \n\n\n### Сравнение переменных в SCSS и CSS\n\nSCSS is a preprocessor. That means it is not CSS, but is converted into CSS at 'compile time'. In the resulting CSS code there is no resemblance to the original SCSS code. Hence you cannot change the variable values at CSS 'runtime'.\n\nHistorically SCSS is a fairly old technique. Actually it dates back to as far as 2007. It was invented by the motivation that CSS lacks certain features amongst which are variables (and nesting and loops and mixins etc.).\n\nCSS variables are a quite recent addition to the CSS standard (The last call working draft seams to be from 2014). They didn't render SCSS variables useless, because you can do things to SCSS variables which you can't do with CSS variables (to my knowledge) like color calculations.\n\nOn the other hand you can do things with CSS variables that you can't do with SCSS variables like changing at runtime.\n\n### Шаблонный селектор, миксин и их различия\n\n**Шаблонный селектор** (placeholder selector).\n```scss\n/* шаблонный селектор */\n%primary-button-skin {\n  background: #24292e;\n  color: #fff;\n}\n.button {\n  @extend %primary-button-skin;\n  width: 200px;\n}\n.another-button {\n  @extend %primary-button-skin;\n  width: 160px;\n}\n```\n**Миксин** (mixin).\n```scss\n@mixin primary-button-skin {\n  background: #24292e;\n  color: #fff;\n}\n.button {\n  @include primary-button-skin;\n  width: 200px;\n}\n.another-button {\n  @include primary-button-skin;\n  width: 160px;\n}\n```\n* *Определения шаблонных селекторов* и *миксинов* *не попадают в CSS* после компиляции.  \n* В *шаблонные селекторы* *нельзя* передавать *параметры*, *в миксины* — *можно*.\n```scss\n@mixin primary-button-skin($color) {\n  background: #24292e;\n  color: $color;\n}\n.button {\n  @include primary-button-skin(#fff);\n}\n```\n* *Миксины* создают *повторяющийся код*, поскольку просто *вставляют своё содержимое*. (включаются) \n```css\n.button {\n  background: #24292e;\n  color: #fff;\n  width: 200px;\n}\n.another-button {\n  background: #24292e;\n  color: #fff;\n  width: 160px;\n}\n```\n*Шаблонные селекторы* выносят код в отдельную *группу селекторов*, *не создавая дубликатов* кода. (наследуются)\n```scss\n.button, .another-button {\n  background: #24292e;\n  color: #fff;\n}\n.button {\n  width: 200px;\n}\n.another-button {\n  width: 160px;\n}\n```\n\n### Плагины PostCSS\n\n**Браузер** — *прикладное ПО*, созданное для *просмотра веб-страниц*, *веб-документов*, *файлов* и их *каталогов*; для *управления веб-приложениями* и многого другого.\n\n**Поставщики браузеров** (browser vendors), **вендоры** — *создатели веб-браузеров*.\n\n**Вендорные префиксы** (vendor prefixes), **браузерные префиксы** *добавляют поддержку экспериментальных* браузерных *технологий*, которые на данный момент *не входят в стандарт*.\n\nСамые *популярные браузеры* используют следующие *префиксы*:\n* `-webkit-` — Chrome, Safari, новые версии Opera.  \n* `-moz-` — Firefox.  \n* `-o-` — старые версии Opera.\n* `-ms-` — IE, Edge.\n\nСтоит так же отметить, что каждый браузер реализует свойства с префиксами *по-своему*, поэтому *поведение* этих свойств в *различных* браузерах может *не совпадать*.\n\n*Плагин* **Autoprefixer** добавляет *браузерные префиксы* к правилам CSS, используя значения сайта [Can I Use](https://caniuse.com/).\n```css\n.animated-element {\n  -o-transition: all 4s ease;\n  -webkit-transition: all 4s ease;\n  -ms-transition: all 4s ease;\n  -moz-transition: all 4s ease;\n  transition: all 4s ease;\n}\n```\n*Плагин* **Preset Env** позволяет конвертировать современный, ещё не поддерживаемый целевыми браузерами CSS код в то, что они смогут понять, определяя необходимые полифиллы на основании целевых браузеров или сред выполнения.\n\nНиже представлены возможности, которые ещё не попали в стандарт, но уже могут использоваться с плагином:\n```css\na {\n  all: initial; /* сбрасывает все свойства элементов a */\n}\n\n/* псевдонимы (aliases) для at-rules */\n@custom-media --mobile (max-width: 420px);\n@media (--narrow-window) {}\n@custom-selector :--heading h1, h2, h3, h4, h5, h6;\narticle :--heading {}\n\n/* пользовательские свойства */\n.title {\n  --size: 20px;\n  font-size: var(--size);\n}\n```\n\n*Плагин* **cssnano** позволяет значительно *оптимизировать CSS* код (иногда в несколько раз).\nДо работы плагина\n```scss\nh1::before, h1:before { /* normalize selectors */\n  margin: 10px 20px 10px 20px; /* reduce shorthand even further */\n  color: #ff0000; /* reduce color values */\n  font-weight: 400;\n  font-weight: 400; /* remove duplicated properties */\n  background-position: bottom right; /* reduce position values */\n  background: linear-gradient(to bottom, #ffe500 0%, #ffe500 50%, #121 50%, #121 100%); /* reduce gradient parameters */\n  min-width: initial; /* replace initial values */\n}\n@charset \"utf-8\"; /* correct invalid placement */\n```\nПосле\n```css\n@charset \"utf-8\";h1:before{margin:10px 20px;color:red;font-weight:400;background-position:100% 100%;background:linear-gradient(180deg,#ffe500,#ffe500 50%,#121 0,#121);min-width:0}\n```\n\n*Плагин* **Pre CSS** — *пак PostCSS плагинов*, позволяющий использовать *SASS-подобный синтаксис*.  \n```scss\n$gray: #24292e;\n\n.menu {\n  &__bg {\n    background: $gray;\n  }\n}\n```\n\n**Stylelint** — мощный *CSS линтер*, который предупреждает об *ошибках в CSS*, делает *замечания* о том, что можно сделать лучше, вносит *соглашения по стилю кода* внутри проекта.  \n*Stylelint* использует под капотом *PostCSS* для *парсинга кода* и существует в виде *PostCSS плагина*.  \n\nТаким образом, *PostCSS* изначально *ближе* к *чистому CSS*, но с помощью определённых *плагинов* может обладать *той же* *функциональностью*, что и *препроцессоры*.\n\n## CSS Modules\n\n**CSS Modules** помогают решить *проблему наименования классов* в проекте.\n\nПусть есть несколько разных компонент, которые имеют элементы одинакового назначения. Например, название (title).  \n```css\n/* Article.css */\n.title { /* ... */ }\n.description { /* ... */ }\n```\n```css\n/* MenuItem.css */\n.title { /* ... */ }\n.icon { /* ... */ }\n```\nЕсли мы просто добавим эти стили, то они сольются друг с другом, поскольку названия классов совпадают.  \nСитуация усугубляется, если проект огромный, и мы просто не знаем всех существующих в нём классов.  \nЧтобы избежать конфликтов, приходится писать код одним из следующих способов.\n```css\n.article-title {}\n/* или */\n.article .title {}\n/* или */\n.article__title {}\n```\nНо есть и другое решение.\n\n**CSS-модуль** (CSS Module) — CSS-файл, в котором все имена классов и анимаций по умолчанию ограничены локально, то есть в файле содержатся стили какого-то конкретного компонента (блока, модуля).\n\nCSS-модуль поступает на вход компилятору CSS-модулей (CSS Modules Compiler), который по заданному шаблону изменяет все названия классов и возвращает CSS-файл с изменениями.\n```css\n/* Article.css */\n/* было */\n.title {} /* [local] */\n/* стало */\n.Article__title___gt5k {} /* [name]__[local]___[hash:base64:4] */\n\n/* Header.css */\n/* было */\n.title {}\n/* стало */\n.MenuItem__title___r3vw {}\n```\nИспользовать сгенерированные названия в коде вручную не нужно.  \nПри импорте CSS файла, импортируется объект, содержащий соответствия между оригинальными именами классов и их модифицированными версиями:\n```js\nexport default {\n  'title': 'Article__title___gt5k',\n  /* ... */\n};\n```\nТогда на примере React\n```JSX\nimport styles from './Article.css'\n\nconst renderTitle = () => (\n  <p className={styles.title}>Title</p>\n);\n```\nВ результате в HTML будет\n```html\n<p class=\"Article__title___gt5k\">Title</p>\n```\nЕсли использовать такой подход во всём проекте, то имена классов в разных файлах не будут конфликтовать друг с другом и это сильно упростит написание CSS кода.\n\nCSS-модули позволяют композицию классов (в том числе и множественную) с помощью свойства `composes`.\n```css\n.classA { /* ... */ }\n.classB { /* ... */ }\n.classC {\n  composes: classA classB;\n}\n.classD {\n  composes: classC;\n}\n```\nКомпозиция из других CSS-модулей также доступна:\n```css\n.classB {\n  composes: classA from \"./style.css\";\n}\n```\n\n\n"
  },
  {
    "path": "CyberSecurityFundamentals.md",
    "content": "- [Переферия и устройства ввода-вывода](#переферия-и-устройства-ввода-вывода)\n- [Компьютерная сеть](#компьютерная-сеть)\n  - [Масштаб компьютерной сети (`LAN`, `WAN`)](#масштаб-компьютерной-сети-lan-wan)\n- [Конечная точка (`endpoint`)](#конечная-точка-endpoint)\n  - [Разница между компьютером, сервером и рабочей станцией](#разница-между-компьютером-сервером-и-рабочей-станцией)\n  - [Устройства интернета вещей (`IoT devices`)](#устройства-интернета-вещей-iot-devices)\n- [Защита конечных точек (`endpoint protection`)](#защита-конечных-точек-endpoint-protection)\n  - [Антивирусные программы (`anti-virus software`)](#антивирусные-программы-anti-virus-software)\n  - [`Endpoint Protection Platform` (`EPP`)](#endpoint-protection-platform-epp)\n  - [`Endpoint Detection and Response` (`EDR`)](#endpoint-detection-and-response-EDR)\n  - [`Managed Detection and Remediation`, `Managed EDR` (`MDR`)](#)\n  - XDR\n\n\n# Переферия и устройства ввода-вывода\n\nВ широком смысле слова, **переферией** (англ. `peripheral`, греч. `περιφέρεια` — *окружность*) называют *внешнюю часть чего-либо*, другими словами, *часть*, *противопоставляющуются центру*.\n\nЕсли говорить о компьютере, то **переферией** называют *совокупность всех переферических устройств*, *подключенных* к *компьютеру* в данный момент.\n\n**Переферийным устройством** (англ. `peripheral device`) или **устройством ввода-вывода** (англ. `input-output device`) называют любое устройство, которое\nпозволяет вводить информацию в компьютер или выводить из него. \n\nТаким образом, устройство ввода-вывода позволяет компьютеру взаимодействовать с внешним миром (в том числе и с человеком, например, посредством графичекого изображения или звукового сопровождения).\n\nНапример, к **устройствам ввода** (англ. `input devices`) относят:\n* *комьютерную мышь* (англ. `computer mouse`)\n* *клавиатуру* (англ. `keyboard`)\n* *тачпад* (англ. `touchpad`)\n* *сенсор экрана* (англ. `screen sensor`)\n* *микрофон* (англ. `microphone`)*\n* *веб-камеру* (англ. `webcab`)\n* *графических планшет* (англ. `graphic tablet`)\n* *джойстик* (англ. `joystick`)\n\nК **устройствам вывода** (англ. `output devices`) относят:\n* *принтер* (англ. `printer`)\n* *монитор* (англ. `monitor`)\n* *динамики* (англ. `speakers`)\n\nНу и к **устройствам ввода-вывода** (англ. `input-output devices`, `I/O devices`) обычно относят любые съёмные накопители, которые можно подключить к компьютеру и с которых можно передавать информацию в обе стороны.\n\n\n# Компьютерная сеть\n\n**Компьютерной сетью** (англ. `computer network`) называют *группу* из *двух и более компьютеров*, *соединённых* между собой *для* электронной *передачи данных*.\n\n## Масштаб компьютерной сети (`LAN`, `WAN`)\n\n<!-- По способу передачи данных -->\n\nПо своему *масштабу компьютерные сети делятся* на *локальные* и *глобальные*.\n\n### Локальная компьютерная сеть\n\n**Локальная компьютерная сеть** (англ. `locan-area network`, `LAN`) объединяет компьютеры и периферические устройства на какой-то небольшой, физически ограниченной территории: обычно в рамках одного здания (квартира или небольшой жилой дом, рабочий офис, лабаратория, университет, военная база и так далее).\n\n### Глобальная компьютерная сеть\n**Глобальная компьютерная сеть** (ангд. `wide-area network`, `WAN`)\n\n# Конечная точка (endpoint)\n\n**Конечной точкой** (англ. `endpoint`, `entry point`) или **устройством конечного пользователя** (англ. `end-user device`) называет *удалённое вычислительное устройство*, которое подключено к сети Интернет, *производит по этой сети двухсторонее взаимодействие* (англ. `communicates back and forth`), то есть и *отправляет данные* на другие устройства, и *получает данные* от *подобных устройств*. \n\nЧаще всего *конечными точками* в *сети Интернет* *выступают*:\n1) *Персональный компьютер* (англ. `PC`, `desktop`),\n2) *Ноутбук* (англ. `laptop`),\n3) *Планшет* (англ. `tablet`),\n4) *Смартфон* (англ. `smartphone`)\n5) *Сетевой сервер* (англ. `network server`). Например, сервер `Amazon`, `Digital Ocean`.\n6) *Рабочая станция* (англ. `workstation`).\n7) *Устройство интернета вещей* (англ. `internet-of-things device`, `IoT device`).\n\n## Разница между компьютером, сервером и рабочей станцией\n\n## Устройства интернета вещей (`IoT devices`)\n\nInternet-of-things (IoT) devices\n\n# Защита конечных точек (`endpoint protection`)\n\n**Защитой конечных точек** (англ `endpoint security`, `endpoint protection`) называют *процесс обезапашивания конечных точек*, *подключенных* к *сети Интернет*. \n\nВсё, что имеет доступ к сети интернет (например, по кабелю или по беспроводному Wi-Fi подключению), а это телефоны, компьютеры, телевизоры, уйстройства умного дома и так далее, является потенциальной мишенью для кибератаки (атаки по сети), то есть находится под угрозой безопасности. \n\nДля защиты конечных точек необходимо регулярно (а лучше в режиме реального времени) проводить проверки на то, что конечные устройства в текущий момент соблюдают допустимый уровень соответствия стандартам компьютерной безопасности.\n\nСтандартов и способов защитить своё устройство существует много, поэтому далее познакомимся отдельно с каждым из них.\n\n# Антивирусные программы (`anti-virus software`)\n\n**Антивирусные программы** или **антивирусы** (англ. `anti-virus software`, `AV`) является самым распространённым и широкоизвестных среди решением проблем кибербезопасности для пользователей сети интернет. *Антивирное ПО* появилось ещё в 1988 году и помогло устранить многие проблемы на устройствам пользователей, но с момента создания технология антивирусов почти не изменилась.\n\nНиже перечислены одни из самых известных и сильных антивирусов:\n- Norton 360 Deluxe.\n- Bitdefender Antivirus Plus.\n- Kaspersky Total Security.\n- McAfee Internet Security.\n- ESET Smart Security Premium.\n- Windows Defender Antivirus.\n\n## Сканирование - основное оружие антивируса\n\nПринцип работы антивируса заключатся в регуляром сканировании девайса. Обычно имеется возможность запустить сканирование в любое время, либо можно настроить планирование сканирований с какой-то периодичностью (например, раз в неделю).\n\n\n# `Endpoint Protection Platform` (`EPP`)\n\n**Платформы защиты конечных точек** (англ. `Endpoint Protection Platform`, `EPP`) разворачиваются (устанавливаются) на конечных устройствах (англ. `endpoint devices`) для предотвращения вредоносных атак (англ. `malware attacks`) на основе файлов, а также для обнаружения вредоносной активности с возможностью расследования и исправления тех или иных динамических инцедентов безопасности, а также оповещения об этих инцедентах.\n\nЧаще всего `EPP` распознаёт некоторые извесные и неизвестные вредоносные программы и блокирует им доступ к ресурсу (или спрашивает пользователя, что нужно с ними делать).\n \n<!-- предотвращают угрозы безопасности конечных точек, такие как известные и неизвестные вредоносные программы.\n -->\n# `Endpoint Detection and Response` (`EDR`)\n\n**Обноружение конечной точки и ответ**\nТехнология **EDR** (англ. `Endpoint detection and response`), дословно *переводящаяяся* как **обнаружение конечной точки и ответ**, предоставляет видимость активности конечной точки в режиме реального времени (англ. `real time`) посредством обнаружения злонамеренного поведения (англ. `malicious behavior`), мониторинга (слежения, отслеживанию) подобных случаев, записью данных конечной точки и ответом на угрозы.\n\nЧаще всего это работает таким образом, что `EDR` мониторит и записывает данные об активности конечной точки, а киберспециалисты уже сами вручную анализируют записанные данные и предпринимают решения, как поступить с угрозой.\n\n\nEndpoint detection and response is a type of security solution that provides real-time visibility into endpoint activities. This is done by detecting malicious behavior, monitoring and recording endpoint data, and responding to threats. Security teams proactively prevent threats by manually analyzing endpoint data received from EDR solutions.\n\n\n\n# `Managed Detection and Remediation`, `Managed EDR` (`MDR`)\n\n"
  },
  {
    "path": "Data.md",
    "content": "# Оглавление\n- [Информация и данные](#информация-и-данные)\n- [Кодирование информации](#кодирование-информации)\n- [Метаданные](#метаданные)\n\n\n<!--\nПлан: \n2) метод деления пополам\n3) сжатие данных\n4) logical unit\n5) traversal\n-->\n\n\n\n# Информация и данные\n- [Об информации и данных](#об-информации-и-данных)\n- [Измерение информации](#измерение-информации)\n- [Символ, алфавит и сообщение](#символ-алфавит-и-сообщение)\n- [Количество информации (формула Хартли)](#количество-информации-формула-хартли)\n\n## Об информации и данных\n\n<!--\nУ *информации* существует *множество определений* в зависимости от *области применения* этого *термина*. Я *решил остановиться* на приведённом *ниже*.\n-->\n\n**Информация** (англ. information) - это *сведения* (*знания*) о некотором *объекте*, которые *не зависят от формы* их *представления*. Одна и та же *информация* может быть *представлена текстом, голосом, графически* и другими способами.\n\n*Информация* отвечает на *вопросы*: “*Чем является* рассматриваемый *объект*? *Какие* он имеет *свойства*?”.\n\n<!--\nНапример, в *теории информации* *информацией* называют *упорядоченную последовательность символов* некоторого *алфавита*.\n-->\n\n**Данные** (англ. data) - это *форма представления информации* в *цифровом виде*. \n\nФактически, *данные* представляют собой *последовательность символов*, а каждый *символ* имеет *цифровое представление* в виде *последовательности нулей и единиц*, которая *задаётся кодировкой символа*.\n\n<!-- Пример предложения и его цифрового представления в некоторой кодировке -->\n\nНад *данными* можно *производить операции* на *ЭВМ*, в том числе *хранить*, *считывать*, *изменять*, *передавать*, *анализировать*, *шифровать* и многие другие.\n\n*Информация* - это *смысл*, который *содержится* в *данных*. По сути говоря, *информация* является *абстракцией*, а *данные* являются её *реализацией*, хотя достаточно *часто* понятия “*данные*” и “*информация*” используют как *синонимы*.\n\n## Измерение информации\n- [Единицы измерения информации](#единицы-измерения-информации)\n- [Системы измерения информации](#системы-измерения-информации)\n\n\n### Единицы измерения информации\n- [Бит](#бит)\n- [Двоичная строка](#двоичная-строка)\n- [Машинное слово](#машинное-слово)\n- [Октет и байт](#октет-и-байт)\n\n#### Бит\n\n**Битом** (англ. **bi**nary digi**t**, bit) или **двоичным разрядом** называют *минимальную, неделимую более единицу измерения информации*.\n\n*Один бит* может *хранить* в себе *одно* из *двух возможных логических значений*. Чаще всего в *качестве значений* используются `1` (*единица*) и `0` (*ноль*) из *двоичной системы счисления*, но так же *допустимы значения*: `истина` и `ложь`, `да` и `нет`, `включено` и `выключено` и другие.\n\n#### Двоичная строка\n\n**Двоичной строкой** (англ. bit string), **двоичной последовательностью** называют *упорядоченную последовательность бит* (двоичных разрядов).\n\n**Длиной двоичной строки** называют *количество бит* этой *строки*.\n\n#### Машинное слово\n\n**Машинным словом** или просто **словом** (англ. word) называют *небольшую группу бит*, которая может *единовременно обрабатываться процессором компьютера* (CPU).\n\n**Длиной слова** (англ. word length) называют *количество бит* в этом *слове*. \n\n*Длина слова фиксирована* и *определяется архитектурой компьютера*. Например, *слово* может *состоять* из `8`, `16` и `32` *бит* в *рвзличных архитектурах*.\n\nДля удобства часто *вводятся вспомогательные единицы измерения*, которые *напрямую зависят* от *длины слова*. \n\nСлово (англ. **word**) | Пол слова (англ. **halfword**) | Два слова (англ. **doubleword**) | Четыре слова (англ. **quadword**)\n:--: | :--: | :--: | :--:\n`8 бит` | `4 бит` | `16 бит` | `32 бит`\n`16 бит` | `8 бит` | `32 бит` | `64 бит`\n`32 бит` | `16 бит` | `64 бит` | `128 бит`\n\n\n#### Октет и байт\n\n**Октетом** (англ. octet) называют *последовательность* из *8 бит*. Например, *октетом* является *последовательность* `10111001`.\n\n*Понятие “октет”* чаще всего встречается при изучении *компьютерных сетей*.\n\n**Байтом** (англ. byte) называют *минимально адресуемую единицу памяти*. \n\nВ наши дни *байт почти всегда содержит* `8 бит` (*остальные размеры устарели*), поэтому *понятие “байт”* часто используют как *синоним понятию “октет”*.\n\n*Далее всегда* будем *использовать понятие “байт”*.\n\n\nВсего *существует* `2^8 = 256` *комбинаций битов* в *одном байте*, что *несложно доказывается комбинаторно* (*доказательство ниже*). \n```\n00000000\n00000001\n00000010\n...\n11111101\n11111110\n11111111\n```\n\n##### Доказательство\n*Байт* можно рассматривать как *упорядоченный набор с повторениями* *длины* `8`, *состоящий* из *элементов множества* `A = { 0, 1 }`. *Мощность множества* `A` *равна* `2`. Тогда *число всевозможных* *наборов соответствует числу размещений с повторениями*: `A(8, 2) = 2^8 = 256`.\n\n### Системы измерения информации\n\nСуществует *две системы измерения информации* в *битах* (*байтах*): **десятичная** (англ. decimal) и **двоичная**, **бинарная** (англ. binary).\n\n*Десятичная система измерения* использует **десятичные префиксы** (англ. decimal prefixes, SI prefixes), *степени десятки*. Например, `kilo` - это `10^3`.\n\n#### Десятичная система измерения информации (англ.)\nНазвание | Обозначение | Значение\n:--: | :--: | :--:\nbit | b | 1 b\nbyte | B | 8 b = 1 B\nkilobyte | KB | 10^3 B = 1000 B\nmegabyte | MB | 10^6 B = 1000 KB\ngigabyte | GB | 10^9 B = 1000 MB\nterabyte | TB | 10^12 B = 1000 GB\npetabyte | PB | 10^15 B = 1000 TB\n\n*Двоичная система измерения* использует **двоичные префиксы** (англ. binary prefixes, IEC prefixes), *степени двойки*. Например, `kibi` (`kilo binary`) - это `2^10`.\n\n#### Двоичная система измерения информации (англ.)\n\nНазвание | Обозначение | Значение\n:--: | :--: | :--:\nbit | b | 1 b\nbyte | B | 8 b = 1 B\nkibibyte | KiB | 2^10 B = 1024 B\nmebibyte | MiB | 2^20 B = 1024 KiB\ngibibyte | GiB | 2^30 B = 1024 MiB\ntebibyte | TiB | 2^40 B = 1024 GiB\npebibyte | PiB | 2^50 B = 1024 TiB\n\nМожно составить *аналогичные таблицы*, используя *биты вместо байт* (`kilobit`, `megabit` и так далее).\n\n*Большинство оборудования* в *наши дни* использует *десятичную систему измерения информации*.\n\n#### Исключение для измерения RAM\nПри *измерении оперативной памяти (RAM)* всегда *используется бинарная система измерения информации*, но при этом *названия* обычно берутся из *десятичной системы измерения*.\n\nТаким образом, при *измерении оперативной памяти*: `1 GB = 1024 MB = 2^10 B`.\n\n## Символ, алфавит и сообщение\n- [Символ](#символ)\n- [Алфавит](#алфавит)\n- [Сообщение](#сообщение)\n\n### Символ\n\n**Символом** (англ. character) называют *условный знак*, которым *обозначают* одно или несколько *понятий*.\n\nНапример, *символом* `“+”` *обозначают начало номера телефона* и *оператор сложения*, *символ* `“z”` *представляет* собой *определённую букву английского алфавита* с её *уникальным звучанием*, а *символ* `“7”` *представляет* собой *определённую арабскую цифру*.\n\n### Алфавит\n*Рекомендуется* прочитать: [Множество](), [Мощность множества](), [Набор элементов]().\n\nНапомним, что *множество* - это *неупорядоченный набор уникальных элементов*.\n\n**Алфавитом** (англ. alphabet) называют *конечное непустое множество символов*. \n\nПример *алфавита* `A`, *каждый символ* которого *обозначает арифметическую операцию*: `{ “+”, “-”, “•”, “÷” }`.\n\n**Мощностью |A| алфавита `A`** называют *количество символов алфавита*.\n\nНапример, *алфавит* `A` из *примера выше* имеет *мощность* `|A| = 4`.\n\n### Сообщение\nОдним из самых распространённых *способов передачи информации* является *сообщение*.\n\n**Сообщением** (англ. message) называют *упорядоченный набор символов* (*элементов*) *с повторениями*, *составленный* из *символов* некоторого *алфавита* `A`.\n\nПод *определение выше* также подходят *понятия* “**текст**” и “**строка**”.\n\nПримеры *сообщений*, *составленных* из *символов алфавита* `A = { a, e, p, r }`: `pear`, `are`, `rap`, `prrr`.\n\n**Длиной сообщения** называют *количество символов* этого *сообщения*.\n\n<!-- удалить раздел ниже? -->\n\n#### Число сообщений фиксированной длины\n\n<!--\n*Рекомендуется* прочитать: [Размещения с повторениями (комбинаторика)]().\n-->\n\n**Число сообщений длины `k` из символов алфавита мощности `n`** *соответствует* **числу размещений с повторениями из `n` по `k`**, а именно **`A(n, k) = n^k`**.\n\nНапример, *число сообщений длины* `k = 3` из символов *алфавита мощности* `n = 4` *равно* `4^3 = 64`.\n\n## Количество информации (формула Хартли)\n- [Формула Хартли](#формула-хартли)\n\n\n### Формула Хартли\n*Рекомендуется* прочитать: [Логарифм]().\n\nПусть *имеется* некоторый **алфавит `A`** **мощности `n`**, *символы* которого *используются* при *составлении сообщений*. \n\n**Формулой Хартли** называют *формулу*, по которой *вычисляется* **количество информации `I` в** некотором **сообщении длины `k`**, *составленном* из *символов алфавита* `A` *мощности* `n`:  \n**`I = k • log2(n)`**.\n\nТогда **количество информации `i` в одном символе** *алфавита* `A` вычисляется по *формуле*:  \n**`i = I ÷ k = log2(n)`**.\n\nВ качестве *единиц измерения* для `I` и `i` используются **биты**.\n\nИз *последней формулы* и *свойств логарифма* *следует*, что *мощность алфавита* `A`:  \n**`n = 2^i`**.\n\nПоскольку *бит* является *минимальной* (*неделимой*) величиной, *количество информации округляется* до *целого числа*. Причём, происходит *округление вверх* (*к большему целому*, *математическое округление*), чтобы *не было потерь информации*:  \n**`i = ⌈ log2(n) ⌉`**, **`I = k • i`**.\n\nНапример, в *английским языке* `26` *букв*, то есть `52` *символа*, если *учитывать* и *строчные*, и *прописные буквы*. Возьмём *английский алфавит в качестве алфавита* `A`, тогда `|A| = n = 52` и *количество информации* в *одном символе алфавита* `A` *равно* `i = ⌈ log2(52) ⌉ = log2(64) = 6` *бит*. В таком случае, *любое сообщение длины* `k = 7`, *составленное* из *символов алфавита* `A`, *займёт* `I = k • i = 42` *бит*.\n\n\n# Кодирование информации\n- [Кодирование и декодирование](#кодирование-и-декодирование)\n- [Набор символов и кодировка](#набор-символов-и-кодировка)\n- [Код](#код)\n- [Разновидности кодировок](#разновидности-кодировок)\n- [О кодировке ASCII](#о-кодировке-ascii)\n- [О стандарте Unicode](#о-стандарте-unicode)\n\n## Кодирование и декодирование\n\n**Кодированием** (англ. encoding) называют *процесс преобразования последовательности символов некоторого алфавита* `A` в *последовательность символов другого алфавита* `B`. \n\n**Декодированием** (англ. decoding) называют *процесс обратного преобразования* из *последовательности символов алфавита* `B` в *последовательность символов алфавита* `A`. \n\nФактически, можно рассматривать *кодирование* как *преобразование информации* в *данные*, а *декодирование* как *преобразование данных* в *информацию*.\n\n## Набор символов и кодировка\n\n**Набором символов** (англ. character set, charset) называют некоторый *алфавит*, *символы* которого *могут* быть *использованы* во многих *других алфавитах*.\n\nНапример, некоторый *набор символов* может *содержать символы латинского алфавита* `{ ..., a, b, ..., z, ... }`, а *многие* из этих *символов используются* в *английском*, *немецком*, *французском*, *итальянском* и других алфавитах.\n\n**Кодировкой символов** (англ. character encoding) или просто **кодировкой** (англ. encoding) называют *закодированный набор символов*, то есть такую *таблицу*, в которой *каждому символу* из *набора символов* *ставится* в *соответствие последовательность из одного или нескольких символов* некоторого *другого алфавита*. \n\nОбычно в *кодировке* *каждому символу* из *набора символов* ставится в *соответствие* некоторое *целое число* *аналогично* тому, как *каждому элементу массива* (*коллекции*) ставится в *соответствие* его *индекс*. Это *целое число* обычно *представляют* в *одной* из *трёх форм*:\n1) В **десятичной системе счисления** (10 с/с), которая *привычна человеку*.\n2) В **двоичной системе счисления** (2 с/с), чтобы показать, *как компьютер видит* данное *число*.\n3) В **шестнадцатиричной системе счисления** (16 с/с) для *краткости записи длинных чисел*.\n\nНапример, в некотором *закодированном наборе символов* `A` *символу* `'#'` *может соответствовать десятичное число* `124` (в *2 с/с* - `01111100`, в *16 с/с* - `7c`), а *символу* `'$'` - *десятичное число* `255` (в *2 с/с* - `11111111`, в *16 с/с* - `ff`).\n\n*Одному набору символов может соответствовать несколько кодировок*.\n\n\n<!-- набор символов - коллекция символов -->\n\n## Код\n- [О коде](#о-коде)\n- [Формальное определение кода и его расширения](#формальное-определение-кода-и-его-расширения)\n\n### О коде\n\n**Кодом** (англ. code) называют некоторый *алгоритм* (*последовательность действий*, *набор инструкций*), который *преобразует символ одного алфавита* `A` в *определённую последовательность символов* другого *алфавита* `B`. \n\n*Алфавит* `A` называют **исходным алфавитом** (англ. source alphabet), а *алфавит* `B` - **целевым алфавитом** (англ. target alphabet). \n\n*Если целевой алфавит* `B` *содержит лишь два символа* (*может закодировать лишь два состояния*), то *код* называют **двоичным** или **бинарным кодом** (англ. binary code). Чаще всего *целевой алфавит бинарного кода состоит* из *нуля и единицы*, то есть `B = { 0, 1 }`, что приводит нас к *использованию двоичной системы счисления* (англ. the binary number system).\n\n**Кодом символа** будем называть *закодированное представление* этого *символа*.\n\n### Формальное определение кода и его расширения\n\nПусть *имеются* *исходный* и *целевой алфавиты* `A` и `B`. Пусть также *имеются бесконечные множества* `A*` и `B*`, являющиеся *множествами всевозможных последовательностей символов алфавитов* `A` и `B` *соответствено*.\n\n**Кодом** называют такую *функцию* (*отображение*) **`c: A → B*`**, которая *преобразует* (англ. map) *каждый символ алфавита* `A` в некоторую *последовательность символов алфавита* `B`. Такая *последовательность однозначно определяется заданным алгоритмом* (*набором символов*).\n\n**Расширением кода `c`** (англ. extension) называют *функцию* **`c*: A* → B*`**, которая *преобразует любую последовательность символов алфавита* `A` в *определённую последовательность символов алфавита* `B`. \n\n<!--\n`f(x) = y`, где `x` - *упорядоченный набор (кортеж) символов алфавита* `A`, а `y` - *упорядоченный набор (кортеж) символов алфавита* `B`. Тогда *пример выше* можно *записать* как `f('a') = 01000001`.\n-->\n\n#### Пример кода и его расширения\n\nПусть *имеется исходный алфавит* `A = { r, s, t }` и *целевой алфавит* `B = { 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 8, 9 }`.\n\n\nПо определению, *кодом* является *функция* **`c: A → B*`**, которая *принимает символ алфавита* `A` и *возвращает последовательность символов алфавита* `B` (*десятичное число*) по *заданному правилу*.\n\n\nПусть теперь *последовательность символов алфавита* `B` задаётся *кодировкой ANSII*.\nСимвол | Десятичный код символа\n:--: | :--:\nr | 162\ns | 163\nt | 164\n\nВ таком случае *справедливо следующее*\n```\nс('r') = 162,\nc('s') = 163,\nc('t') = 164,\n```\nчто так же *можно записать*\n```\nc = { r ↦ 162, s ↦ 163, t ↦ 164 },\n```\nгде `r, s, t ∈ A` и `162, 163, 163 ∈ B*`.\n\nРассмотрим теперь *расширение кода* в виде *функции* **`c*: A* → B*`**, которая *принимает последовательность символов исходного алфавита* `A` и *преобразует* её в *последовательность символов целевого алфавита* `B` по тому же *правилу*, что и *код* `c: A → B*` .\n\nНапример, пусть `ts, ssr ∈ A*`, тогда\n```\nc* = { ts ↦ 164162, ssr ↦ 16316312 },\n```\nгде `164162, 16316312 ∈ B*`.\n\n<!--\nИз примера выше понятно, что для *декодирования* необходимо знать фиксированную длину кода символа, иначе разделить последовательность не получится\nНапример, можно установить длину = 3, тогда в случае двухзначного числа впереди добавится 0\n-->\n\n\n<!--\n\nСо временем пришли к тому, что 256 символов не достаточно, поэтому в один символ строки теперь выделяют 2-4 байта. Например, выделение 2 байт на один символ позволяет использовать алфавит, содержащий до 2^16 = 65536 символов.\n-->\n\n\n## Разновидности кодировок\n\n- [Однобайтовые кодировки](#однобайтовые-кодировки)\n- [Преимущества и недостатки однобайтовых кодировок](#преимущества-и-недостатки-однобайтовых-кодировок)\n\n\n*Кодировки* можно *разделить* на *несколько типов* в *зависимости* от *затрачиваемой* ими *памяти* на *каждый символ*:  \n1) *Фиксированный размер* для *всех символов*. *Кодировки* с *фиксированным размером подразделяются* на *однобайтовые* и *многобайтовые* (обычно, *2-4 байта*).\n2) *Динамический размер символа*.\n\n### Однобайтовые кодировки\n\n*Размер представления* любого *символа* в самых *первых кодировках не превышал одного байта*. Наиболее яркими *примерами таких кодировок выступают* **семибитная кодировка ASCII** и **восьмибитная** (*однобайтовая*) **расширенная кодировка ASCII**. *Кодировка ASCII* является *стандартом* в *языке C* и часто *встречается* при работе с языком *C++*.\n\nИспользуя *формулу* *количества информации* `i` в *одном символе* *алфавита* `A` *мощности* `n`: `i = log2(n)`, *приходим* к тому, что **`n = 2^i`**, откуда *следует*, что **семибитная кодировка** позволяет *закодировать* лишь *набор* из **`2^7 = 128`** *и менее* **символов**, а **однобайтовая кодировка** - *набор* из **`2^8 = 256`** *и менее* **символов**.\n\nПоскольку *минимально адресуемая единица памяти* на *большинстве современных компьютеров* равна *одному байту*, для *двоичного представления символов ASCII* чаще всего используют *8-битные последовательности вместо 7-битных*. Как следствие, *кодировку ASCII* так же можно считать *однобайтовой*.\n\n### Преимущества и недостатки однобайтовых кодировок\n\n*Однобайтовая кодировка* *проста* в *реализации*, *закодированный* с её помощью *текст* имеет *наименьший размер* и этот *размер* можно довольно *просто рассчитать* (`1 символ = 1 байт`). \n\n*С другой стороны*, *однобайтовая кодировка* сильно *ограничена*, поскольку *такая кодировка* может *вместить* очень *мало языков*. \n\n*Когда это* может быть *важно*? Например, в *тексте* с *цитатами* из *оригинальных источников* на *иностранных языках*. Если *кодировка не поддерживает* некоторый *язык*, определённые *фрагменты текста* будут *представлять собой* нечто вроде `\"Ñ†ÑƒÐ°Ñ‹Ð²\"`, что *свидетельствует* о *неправильном декодировании* и что *невозможно разобрать*. *Можно*, конечно, *переключить кодировку*, но тогда уже *другая часть текста станет неразборчивой*. Более того, в *однобайтовую кодировку не вмещаются* даже *все символы китайского языка*.\n\n\nКак *итог*, *однобайтовая кодировка идеально подходит* для *небольшой локальной системы*, в которой *представление всей информации не выходит* за *пределы ограниченного множества символов*. Когда *две и более* такие *системы* с *различными кодировками* начинают *взаимодействовать друг с другом*, что и *подразумевает* под собой *интернет*, *появляются проблемы*.\n\n\nОчевидным *решением проблемы* стало *расширение кодировки ASCII*. Так *появилась* **расширенная кодировка ASCII** (англ. Extended ASCII), которая может *закодировать* до `256` *символов*, но и *её* вскоре оказалось *не достаточно* из-за *быстрого развития компьютерных сетей*. Это *привело* к *созданию* **стандарта Unicode**, который *поддерживает* почти *все существующие* в мире *символы*.\n\n\n<!--\nно это *не работает*, когда *две* и более *различные системы взаимодействуют* друг с другом, а именно *такое взаимодействие* и *подразумевает* под собой *интернет*. \n-->\n\n## О кодировке ASCII\n\n**Кодировка ASCII** (англ. American Standard Code for Information Interchange) была *разработана* для *управления средствами обмена информацией* (например, *телетайп*) ещё *до появления компьютера*.\n\nБольшинство *современных кодировок обратно совместимы* с *кодировкой ASCII*.\n\n*Кодировка ASCII содержит*:\n1) **Управляющие символы** (англ. Control characters). Например, `Backspace`, `Space`, `Delete`, `\\n`.\n2) **Печатные символы** (англ. Printable characters), *включающие* в себя *строчные* и *прописные буквы латинского алфавита*, *цифры*, *знаки препинания* и некоторые *другие наиболее распространённые символы* (`#`, `&`, `@`, `$`, `=` и *так далее*).\n\n*Каждому символу кодировки ASCII ставится* в *соответствие целое число* (*код символа*) *от* `0` *до* `127`, которое может быть *представлено 7-битной строкой*. \n\nТем не менее, поскольку *минимально адресуемая единица памяти* на *большинстве современных компьютеров* равна *одному байту*, для *двоичного представления символов ASCII* чаще всего используют *8-битные последовательности вместо 7-битных*. Для этого в *начало двоичной последовательности* *добавляется один нулевой бит*. Например, *вместо* `1000110` *записывают* `01000110`\n\n*Ниже выборочно представлены символы* и *их коды* в *кодировке ASCII*:\nКод символа | Двоичный код символа | Символ\n:--: | :--: | :--:\n0 | 00000000 | \"\\0\" (Null)\n8 | 00001000 | \"\\b\" (Backspace, BS)\n9 | 00001001 | \"\\t\" (Horizontal Tab, HT)\n10 | 00001010 | \"\\n\" (Line Feed, LF)\n13 | 00001101 | \"\\r\" (Carriage Return, CR)\n40 | 00100000 | Space\n49 | 00110001 | \"1\"\n50 | 00110010 | \"2\"\n57 | 00111001 | \"9\"\n65 | 01000001 | \"A\"\n66 | 01000010 | \"B\"\n90 | 01011010 | \"Z\"\n97 | 01100001 | \"a\"\n98 | 01100010 | \"b\"\n122 | 01111010 | \"z\"\n127 | 01111111 | Delete\n\n\n## О стандарте Unicode\n- [Универсальный набор символов (UCS)](#универсальный-набор-символов-ucs)\n- [Форматы Unicode (UTF)](#форматы-unicode-utf)\n\n### Универсальный набор символов (UCS)\n\nВ наши дни *самым* широко *используемым набором символов* является **универсальный набор символов** (англ. Universal character set, UCS), *являющийся частью стандарта Unicode*. *Этот набор символов содержит* почти все *зарегистрированные символы мира*, в том числе *символы всех существующих разговорных языков*.\n\n*Благодаря* такой *широкой поддержке символов*, в *одном файле* с *кодировкой* стандарта *Unicode может использоваться сколько угодно языков*. В случае же *использования однобайтовой кодировки*, *пришлось* бы *переключать кодовые страницы* (*кодировки*), чтобы *распознать* (*декодировать*) отдельные *фрагменты текста*.\n\n### Форматы Unicode (UTF)\n\n*Стандарт Unicode предоставляет* целое **семейство кодировок UTF** (англ. Unicode transformation format), *каждая* из *которых* имеет *свой алгоритм кодирования символов* *универсального набора*.\n\n*Символы Unicode представляются целыми неотрицательными числами*, которые обычно *записываются* в *шестнадцатиричной системе счисления* (16 с/с) для *компактности*.\n\n*Основными\n\n\n\n<!--\nпозволяющих *ограничить диапазон используемых символов набора* (*уменшить количество символов* в *наборе*) и таким образом *уменьшить затраты памяти* на *один символ*.\n-->\n\n\n\n\n\n\n\n\n\n\n\n# Метаданные\n\n**Метаданными** (англ. metadata) называют *данные*, которые *несут* в себе *информацию* о *других данных*. Иначе говоря, *метаданные* - это *“данные о данных”*.\n\nНапример, *текстовый файл* может *иметь* следующие *метаданные*:\n* *имя пользователя*, *создавшего* файл,\n* *дата создания* файла,\n* *дата последнего изменения* файла,\n* *размер файла* (например, `73 KB`),\n* *полная расшифровка формата* файла (например, `.pdf` - *Portable Document Format*),\n* *количество символов* (строк) в файле*,\n* *кодировка файла* (например, `UTF-8`),\n* *возможность чтения* файла *и записи* в файл.\n\nНапример, *реляционная база данных хранит информацию* (*метаданные*) о том, *какие* *таблицы* в ней *имеются*, *какой размер* они *занимают* и *сколько строк содержится* в *каждой* из них.\n\n\n\n\n\n\n\n;\n"
  },
  {
    "path": "DataModels-Databases.md",
    "content": "# Оглавление\n- [Общие понятия баз данных](#общие-понятия-баз-данных)\n- [Реляционная модель данных](#реляционная-модель-данных)\n- [Масштабирование баз данных](#масштабирование-баз-данных)\n- [Паттерны баз данных](#паттерны-баз-данных)\n- [Движки баз данных](#движки-баз-данных)\n- [Проблема n + 1](#проблема-n-1)\n- [Оптимизация SQL-запросов](#оптимизация-sql-запросов)\n\n<!--\n- [Резидентные базы данных](#резидентные-базы-данных)\n  - [Преимущества и недостатки резидентной базы данных](#преимущества-и-недостатки-резидентной-базы-данных)\n  - [О-Redis](#о-redis)\n\n-->\n\n# Общие понятия баз данных\n- [О базе данных](#о-базе-данных)\n- [Нормализация и денормализация базы данных](#нормализация-и-денормализация-базы-данных)\n- [Транзакция](#транзакция)\n- [Индекс базы данных](#индекс-базы-данных)\n- [Курсор](#курсор)\n\n\n\n## О базе данных\n\n**Базой данных** (англ. `database`), **БД** (англ. `DB`) называют *организованную структуру*, которая *предназначена* для *хранения*, *чтения* и *изменения данных*.\n\n*Самой простой базой данных* может *послужить обычный текстовый файл*, однако *искать* и *изменять данные* в такой *\"базе\"* было бы как минимум *не удобно*.\n\n<!--\n\n# О базах данных\n\n**База данных** (Database) — *организованная структура*, предназначенная для *хранения*, *чтения* и *изменения данных*.\n\n**Система управления базами данных** (СУБД, DBMS, Database Management System) — *программное обеспечение*, которое *управляет созданием* и *использованием баз данных*. \n\nСУБД отвечает за всё, что связано с данными: формат, структура, правила размещения данных и управления над ними.\n\nПримеры СУБД: MySQL, Oracle.\n\n## Реляционные и нереляционные базы данных\n\n**Отношения** (relations) дают возможность *группировки данных* как *связанных наборов*, представленных в виде *таблиц*, которые содержат *упорядоченную информацию* и соотносят атрибуты и значения.\n\nДля *работы* с *реляционными базами данных* используется *язык запросов SQL*.\n\n## Типы баз данных\n* **Реляционная** (Relational). Данные организованы как логически независимые таблицы. \n* **Плоская** (Flat).\n* **Объектно-ориентированная** (Object-Oriented). Использует концепции ООП.\n* **Иерархическая** (Hierarchical). Данные организованны в виде иерархии.\n* **Документная** (Document). Тип нереляционных баз данных, предназначенный для хранения и запроса данных в виде документов в формате, подобном JSON. \n\n\n## ORM и ODM\n\n**ORM** (Object Relational Mapper) связывает реляционную базу данных с объектной моделью, позволяя переводить данные из объектов в коде в реляционное представление и наоборот. (sequelize) \n\n**ODM** (Object Document Mapper) связывает документную базу данных с объектной моделью. (mongoose)\n\n-->\n\n## Нормализация и денормализация базы данных\n- [О нормальной форме и нормализации](#о-нормальной-форме-и-нормализации)\n- [О денормализации](#о-денормализации)\n\n\n## Транзакция\n- [О транзакции](#о-транзакции)\n- [Свойства транзакции (ACID)](#свойства-транзакции-acid)\n- [Пример транзакции](#пример-транзакции)\n\n### О транзакции\n**Транзакция** (англ. transaction) — это *логическая единица* (logical unit), которая *осуществляет доступ* к *базе данных* и, возможно, *изменяет данные* в ней. \n\n*Транзакции работают* с *данными*, используя *операции чтения* и *записи*.\n\n### Свойства транзакции (ACID)\n* **Атомарность** (англ. **A**tomicity) гарантирует, что транзакция не может быть зафиксирована частично: либо выполняются все подоперации транзакции, либо ни одна. Если какая-то операция из последовательности не проходит, то происходит откат (англ. `rollback`) всей последовательности.\n* **Согласованность, консистентность** (англ. **C**onsistency). Каждая успешная транзакция фиксирует только допустимый для системы результат. База данных должна быть согласованна до и после транзакции, во время проведения транзакции согласованность не требуется.\n* **Изолированность** (англ. **I**solation). Cпособность базы данных гарантировать, что *параллельно выполняющиеся транзакции не мешают друг другу*. Во время выполнения транзакции другие параллельные транзакции не должны оказывать влияния на её результат. Требование \"дорогое\", поэтому в реальных базах транзакции изолируются не полностью: создаются *уровни изолированности*.\n* **Долговечность** (англ. **D**urability). Если пользователь получил подтверждение от системы, что транзакция выполнена, он может быть уверен, что сделанные им изменения не будут отменены из-за какого-либо сбоя. Изменения, сделанные успешно завершённой транзакцией, должны остаться сохранёнными после возвращения системы в работу.\n\n### Пример транзакции\n\nПусть деньги переводятся с одного счёта на другой. Используются две основные операции: вывод денег с одного счёта `1000$ - 100$ = 400$`, зачисление их на другой счёт `500$ + 100$ = 600$`. Если первая операция удалась, а вторая нет, то происходит откат, поскольку в противном случае имеем несогласованность (inconsistency) данных до и после транзакции: до транзакции на счетах в сумме было `1500$`, после — `1400$`.\n\n### Пример транзакции на SQL\n\n```sql\nBEGIN TRANSACTION;\n\n-- Операция 1\nINSERT INTO Employees (EmployeeID, FirstName, LastName, Position, Salary)\nVALUES (104, 'Петр', 'Петров', 'Разработчик', 80000);\n\n-- Установка точки сохранения\nSAVEPOINT AfterInsert;\n\n-- Операция 2\nUPDATE Employees\nSET Salary = Salary * 1.05\nWHERE EmployeeID = 104;\n\n-- Возникла ошибка, откат к точке сохранения\nROLLBACK TO SAVEPOINT AfterInsert;\n\n-- Фиксация транзакции\nCOMMIT;\n```\n\n### Уровни изолированности транзакций (Transaction Isolation Levels)\n**Уровни изолированности транзакций** (англ. `Transaction Isolation Levels`) — фундаментальная концепция в реляционных базах данных, которая определяет, как транзакции изолированы друг от друга и какие “аномалии” могут или не могут возникнуть при одновременном доступе к данным.\n\nУровни:\n* **Чтение неподтверждённых данных** (англ. `Read Uncommitted`) - можно читать неподтвержненные данные (грязное чтение). Грязные чтения, неповторяющиеся чтения, фантомные чтения возможны.\n* **Чтение подтверждённых данных** (англ. `Read Committed`) - можно читать лишь подтверждённые данные (грязное чтение невозможно). Неповторяющиеся чтения, фантомные чтения возможны.\n* **Повторяемое чтение** (англ. `Repeatable Read`) - все данные, прочитанные в транзакции, будут *одинаковыми при повторном чтении*. Грязное и неповторяющиеся чтения невозможны. Возможны фантомные чтения.\n* **Сериализуемость** (англ. `Serializable`) - полная изолированность, максимальная защита от всех аномалий.\n\nПримеры применения:\n```sql\n-- PostgreSQL\nBEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;\n\n-- MySQL\nSET TRANSACTION ISOLATION LEVEL REPEATABLE READ;\nSTART TRANSACTION;\n```\n\n### Аномалии\n* **Грязное чтение** (англ. `Dirty read`) - транзакции читают данные, которые ещё не подтверждены другой транзакцией.\n* **Неповторяющееся чтение** (англ. `Non-repeatable read`) - повторное чтение тех же данных возвращает различные значения.\n* **Фантомное чтение** (англ. `Phantom read`) - повторное чтение тех же данных возвращает новые строки, добавленные другой транзакцией.\n\n### Выбор уровня изолированности\n* **Read Committed** - максимальная производительность, мало конфликтов.\n* **Repeatable read** - стабильное чтение, терпимы фантомы\n* **Serializable** - критичные данные, важно избегать любых конфликтов\n\n\n## Индекс базы данных\n- [Об индексе базы данных](#об-индексе-базы-данных)\n- [Проблема дублирования данных](#проблема-дублирования-данных)\n- [Основные типы индексов](#основные-типы-индексов)\n\n### Об индексе базы данных\n\n**Индекс базы данных** (англ. `database index`) — это структура данных, ускоряющая операции поиска (англ. `speed up querying`) в заданной таблице (или коллекции) базы данных за счёт хранения дополнительной информации в базе (говоря простыми словами, тех же данных таблицы, но в другом виде, в упорядоченном). \n\n*Индекс* — это дополнительная структура данных, например, в виде дерева, которая позволяет быстро найти нужные строки, не перебирая всю таблицу (англ. `Full Table Scan`). Индексы хранят упорядоченные или быстро доступные ссылки на строки таблицы. Это похоже на оглавление в книге, которое позволяет быстро найти нужную страницу.\n\n\nПо умолчанию записи в таблице (коллекции) могут храниться произвольно (неупорядоченно). И если таблица довольно большая, то последовательный просмотр всех записей может занимать довольно продолжительное время. Индексы приводят данные в такой вид (упорядочивают их), чтобы это ускорило поиск. Например, структура индекса может быть представлена *сбалансированным деревом поиска*, и тогда скорость поиска будет логарифмичной `O(log n)` вместо линейной `O(n)`. \n\nИндекс формируется из значений одного или нескольких столбцов таблицы (полей документов коллекции) и указателя на соответствующие строки таблицы (документы коллекции). Также иногда индексы могут создаваться из выражений.\n\n### Основные типы индексов:\n#### Бинарное дерево (B-дерево, B-tree)\n\nНаиболее распространённый тип индекса (чаще всего используется по умолчанию). Организован в виде сбалансированного дерева (англ. `AVL-Tree`).\nКогда использовать:\n\t•\tПоиск точных значений (например, `WHERE id = 10`)\n\t•\tПоиск диапазонов (`WHERE date BETWEEN x AND y`)\n\t•\tСортировка (`ORDER BY`)\n\t•\t`JOIN` по индексированным полям\nНедостатки:\n\t•\tМенее эффективен при поиске текста по шаблону (например, с `LIKE '%search%'`)\n\t•\tНенужное создание множества таких индексов может снизить производительность при вставке и обновлении записей\n\n#### Хеш-индексы (Hash Index)\nИспользуют хеш-функции для быстрого поиска по точному соответствию ключа.\n\nКогда использовать:\n\t•\tОчень быстрый поиск по точному совпадению (например, `WHERE username = 'user123'`)\nНедостатки:\n\t•\tНе поддерживают поиск по диапазону (`BETWEEN`, `<`, `>`) или сортировку\n\t•\tНе подходят для поиска частичных совпадений\n\t•\tЧаще используются в специализированных БД (например, in-memory хранилищах)\n\n#### Полнотекстовый индекс (Full-Text Index)\n\nИспользуются для поиска слов или фраз в тексте.\nКогда использовать:\n\t•\tПолнотекстовый поиск по большим текстовым полям (`LIKE` недостаточно эффективен)\nПримеры СУБД:\n\t•\tMySQL (MATCH AGAINST)\n\t•\tPostgreSQL (GIN, GIN индексы)\n\n#### Многоколоночные (композитные) индексы (Composite Index)\n\nСостоят из нескольких полей таблицы.\n\nКогда использовать:\n\t•\tЗапросы, включающие несколько полей одновременно (WHERE name = X AND date = Y)\n\t•\tЧастые сложные условия с множеством критериев\nВажно:\n\t•\tПорядок столбцов в индексе очень важен. Первым указывается поле, по которому чаще всего происходит поиск или фильтрация.\n\n#### Уникальный индекс (Unique Index)\nНе допускает повторяющихся значений.\n\nКогда использовать:\n\t•\tПоля, значения которых должны быть уникальными (например, `email`, `phone`, `username`)\n\n#### Частичные индексы (Partial Index)\n\nИндексы, применяемые только к части строк таблицы (по условию).\nКогда использовать:\n\t•\tЕсли часто выбирается лишь небольшая часть данных (например, только активные пользователи)\n\n#### Многомерные индексы (R-Tree, GIN, GiST)\n\nИспользуются для специфических типов данных (пространственные данные, JSON и пр.).\nКогда использовать:\n\t•\tГеоданные (например, геолокации, поиск по карте)\n\t•\tJSON-данные с поиском по внутренним полям\n\n### Преимущества и недостатки индексов\n\nКоличество индексов прямо влияет на производительность\n* Много индексов:\n➕ Ускоряет многие запросы на чтение\n➖ Значительно замедляет операции записи (`INSERT, UPDATE, DELETE`), увеличивает размер базы данных, повышает нагрузку на сервер.\n*\tНедостаточно индексов:\n➕ Быстрая запись данных\n➖ Медленные запросы на чтение, полное сканирование таблиц, неоптимальная нагрузка на сервер.\n\nТаблица с 5 индексами при добавлении новой строки база данных должна обновить все 5 индексов, что занимает больше времени, чем без индексов или с одним индексом.\n\nСоздавайте индексы только на те поля, которые часто используются в запросах.\nИзбегайте индексов на полях с низкой селективностью (например, пол (мужчина/женщина) — плохой кандидат для индекса, так как пользы почти не принесёт).\n\nПравильное использование индексов — это баланс между скоростью чтения, скоростью вставки и обновления данных, а также объёмом используемого места.\n\nИндексы ускоряют чтение, но замедляют операции записи (INSERT, UPDATE, DELETE).\n\t•\tИспользуйте индексы осознанно: больше индексов ≠ всегда лучше.\n\t•\tРегулярно проверяйте производительность запросов (с помощью EXPLAIN) и оптимизируйте набор индексов.\n\n## Курсор\n- [О курсоре](#о-курсоре)\n- [Пример курсора в MongoDB](#пример-курсора-в-mongodb)\n- [Пример курсора в SQL](#пример-курсора-в-sql)\n\n### О курсоре\n\n**Курсор** (англ. `cursor`) — *управляющая структура*, которая *производит* **обход записей** (англ. `traversal`) в *базе данных*. \n\n<!--\nТакже *курсором* называют *область памяти*, которая *хранит результат поискового запроса* к *базе данных*.\n-->\n\n*Принцип работы* с *курсором похож* на работу с *итератором* (англ. `iterator`), поскольку *курсор позволяет последовательно обработать каждую строку* (каждый документ) из *выбранного набора строк* (документов).\n\n*Курсор* также можно *рассматривать* как *указатель* (`pointer`) на *одну строку таблицы* (на один документ). *Курсор* может *ссылаться* только на *одну строку* (один документ) в *один момент времени*, но может *перемещаться* к *другим строкам* при необходимости.\n\n*Курсоры подразделяются* на *явные* и *неявные*.\n\n**Явный курсор** *создаётся разработчиком вручную*, **неявный курсор** *создаётся системой автоматически*.\n\n### Пример курсора в MongoDB\n```js\nconst usersCursor = db.users.find({ role: 'admin' });\n/* последовательный перебор всех документов результирующего набора в цикле while */\nwhile (usersCursor.hasNext()) {\n   const user = usersCursor.next();\n   console.log(user);\n}\n```\n\n### Пример курсора в SQL\n```SQL\n# объявление временных переменных, которые будут использованы для записи данных курсора\nDECLARE @id VARCHAR(50);\nDECLARE @role VARCHAR(50);\n# объявление явного курсора, который будет брать данные столбцов id, role из таблицы users\nDECLARE @users_cursor CURSOR FOR\n  SELECT id, role\n  FROM users\n# открытие курсор (наполнение его данными)\nOPEN @users_cursor\n# последовательное извлечение строк из результирующего набора в цикле WHILTE,\n# запись значений в переменные и вывод этих переменных \nWHILE @@FETCH_STATUS = 0\nBEGIN\n  FETCH NEXT FROM @users_cursor INTO @id, @role\n  PRINT @id + ' ' + @role\nEND\n# после извлечения всех строк следует закрыть курсор и освободить занимаемые им ресурсы\nCLOSE @users_cursor\n# иногда нужно вручную освобождать память курсора (зависит от реализации SQL)\nDEALLOCATE @users_cursor\n```\nВозможные варианты перемещений с `FETCH` по результирующему набору строк.\n* `FIRST` — первая строка .\n* `NEXT` — следующая строка после текущей.\n* `PRIOR` — строка, находящаяся перед текущей.\n* `LAST` — последняя строка.\n* `ABSOLUTE int` — строка по её абсолютному порядковому номеру `int` при отсчёте с начала (конца), если перед `int` знак + (-). Если указать 0, то ничего не вернётся.\n* `RELATIVE int` — перемещение на `int` строк вперёд (назад) от текущей, если перед `int` знак + (-). Если указать 0, то вернётся текущая строка.\n\nИзменение данных таблицы при помощи курсора и `CURRENT`. Курсор должен быть открыт для операции.\n```SQL\nUPDATE new_users\nSET ...\nWHERE CURRENT OF @sers_cursor\n```\nАналогично можно делать с `DELETE`.\n\n\n# Масштабирование баз данных\n- [Репликация](#репликация)\n- [Шардинг](#шардинг)\n- [Партиционирование](#партиционирование)\n\n## Репликация\n\n**Репликация** (англ. replication) — это *постоянное синхронное* или *асинхронное копирование* (реплицирование) *данных между* несколькими *серверами*.\n\n*Сервер*, *копирующий данные другого сервера*, называют **репликой** (replica).\n\n*Асинхронное копирование* подразумевает, что *копирование данных на другие сервера* может происходить с *некоторой задержкой*.\n\nКогда появляется несколько серверов, нужно выбрать *основной*, *ведущий* сервер — **мастер** (Master). Он будет отвечать за *все изменения данных* (запись).\n\n*Остальные сервера* называют **слейвами** (Slaves). Они *постоянно копируют данные* с *мастера* и *предоставляют их для чтения*. *Мастер* тоже *доступен для чтения* данных.\n\nВ *больших системах* может быть *несколько мастеров*, но обычно достаточно одного мастера и нескольких слейвов.\n\n### О выходе из строя\n\nЕсли слейв выходит из строя, нагрузка распределяется между мастером и оставшимися слейвами. Нерабочий слейв восстанавливается и снова подключается.\n\nЕсли мастер выходит из строя, слейв становится новым мастером. Если старый мастер восстанавливается, он становится новым слейвом.\n\nНе рекомендуется использовать систему, где есть несколько мастеров и отсутствуют слейвы, так как она гарантированно теряет некоторую часть данных при выходе из строя: каждый мастер отвечает за изменение данных и нет слейвов, хранящих резервные копии этих данных.\n\n### Для чего нужна репликация\n\n*Репликация* позволяет для *обработки запросов* *использовать* *несколько серверов* вместо одного, чтобы *распределить нагрузку* между ними и *повысить* таким образом *производительность системы*.\n\nМожно *организовать систему* таким образом, чтобы *каждый слейв отвечал* за *конкретный тип задач*. Тогда *перегрузка приложения определённым типом задач перегрузит только одного слейва* — *другие функции приложения не будут затронуты*.\n\nРепликация используется не только для масштабирования, но и для резервирования базы данных: если один сервер выходит из строя, другой может его подменить. \n\n## Шардинг \n**Шардинг** (англ. sharding) — приём масштабирования, который позволяет распределять данные между разными физическими серверами. Процесс шардинга предполагает разнесения данных между отдельными шардами на основе некого ключа шардинга. Связанные одинаковым значением ключа шардинга сущности группируются в набор данных по заданному ключу, а этот набор хранится в пределах одного физического шарда. Это существенно облегчает обработку данных.\n\nНапример, в системах типа социальных сетей ключом для шардинга может быть ID пользователя, таким образом все данные пользователя будут храниться и обрабатываться на одном сервере, а не собираться по частям с нескольких.\n\n## Партиционирование\n**Партиционирование** (англ. partitioning) — разбиение таблиц, содержащих большое количество записей, на логические части по некоторым выбранным критериям.\n\nПартиционирование таблиц делит весь объем операций по обработке данных на несколько независимых и параллельно выполняющихся потоков, что существенно ускоряет работу СУБД.\n\nДля правильного конфигурирования параметров партиционирования необходимо, чтобы в каждом потоке было примерно одинаковое количество записей.\n\nНапример, на новостных сайтах имеет смысл партиционировать записи по дате публикации, так как свежие новости на несколько порядков более востребованы и чаще требуется работа именно с ними, а не со всех архивом за годы существования новостного ресурса.\n\n\n# Реляционная модель данных\n\n- [О реляционной модели данных](#о-реляционной-модели-данных)\n- [Коротко о главном из теории множеств](#коротко-о-главном-из-теории-множеств)\n- [Тип данных (домен)](#тип-данных-домен)\n- [Отношение и его структура](#отношение-и-его-структура)\n- [Свойства отношений](#свойства-отношений)\n- [Графическое представление отношения (таблица)](#графическое-представление-отношения-таблица)\n- [Ключи](#ключи)\n- [Связи между отношениями](#связи-между-отношениями)\n- [Операции над отношениями (реляционные операции)](#операции-над-отношениями-реляционные-операции)\n- [Разновидности операций соединения](#разновидности-операций-соединения)\n- [Свойства операций над отношениями](#свойства-операций-над-отношениями)\n- [Функциональная зависимость](#функциональная-зависимость)\n- [Нормализация и нормальные формы](#нормализация-и-нормальные-формы)\n\n\n## О реляционной модели данных\n\n<!--\nКлючевым понятием реляционной модели данных является **отношение**. Это понятие пришло из теории множества, и для его полного понимания рекомендуется прочитать\n-->\n\n*Реляционная модель данных основана* на понятии *отношения*, которое *пришло* из *теории множеств*.\n\n## Коротко о главном из теории множеств\n\n- [Множество](#множество)\n- [Кортеж](#кортеж)\n- [Декартово произведение множеств](#декартово-произведение-множеств)\n- [Отношение в теории множеств](#отношение-в-теории-множеств)\n- [Операция в теории множеств](#операция-в-теории-множеств)\n\n### Множество\n\n**Множество** - это *неупорядоченный набор уникальных объектов*, *обладающих схожими признаками*. Эти *объекты* называются **элементами множества**.\n\nПримеры *множеств*: *множество женских имён*: `N = { Ася, Сара, Рози }`, *множество профессий*: `J = { дизайнер, программист }`, *множество дат*: `D = { 2019, 2020 }`.\n\n*Неупорядоченность множества* означает, что *множества* `{ 2019, 2020 }` и `{ 2020, 2019 }` *считаются одинаковыми*.\n\n*Уникальность элементов множества* означает, что *множество не может содержать* двух и более *одинаковых элементов*. То есть *набор* `{ 3, 2, 2 }` - *не множество*.\n\n### Кортеж\n\n**Кортежем длины n** называют *упорядоченный набор объектов* (*не обязательно уникальных*) `(x1, x2, ..., xn)`, где `x1, x2, ..., xn` *принадлежат* множествам `X1, X2, ... Xn` (не обязательно различным).\n\nПримеры *кортежей*: *кортеж из элементов множеств* `N`, `J`, `D` соответственно: `(Ася, дизайнер, 2019)`, *координаты точки* на плоскости: `(1, 1)`, *набор аргументов функции* `f(x1, x2, x3)`.\n\n*Упорядоченность кортежа* означает, что *кортежи* `(Рози, 2020)` и `(2020, Рози)` *считаются различными*. *Соблюдение порядка элементов кортежа* так же *важно*, как *соблюдение порядка букв в словах* и *цифр в числах*.\n\n*Кортеж длины 2* также называют **упорядоченной парой**, *кортеж длины 3* - **упорядоченной тройкой**.\n\n### Декартово произведение множеств\n\n**Декартово произведение множеств** `X1, X2, ..., Xn` - это операция над множествами `X1, X2, ..., Xn` (не обязательно различными), результатом которой является множество всевозможных кортежей `(x1, x2, ..., xn)`, где элементы `x1, x2, ..., xn` *принадлежат* множествам `X1, X2, ..., Xn` соответственно. Обозначение: `X1 × X2 × ... × Xn`.\n\nНапример, декартово произведение `N × D = { (Ася, 2019), (Ася, 2020), (Сара, 2019), (Сара, 2020), (Рози, 2019), (Рози, 2020) }`, а декартово произведение `N × J × D` состоит из `3 • 2 • 2 = 12` кортежей. по типу `(Ася, дизайнер, 2019)`.\n\n### Отношение в теории множеств\n\n**n-арным отношением** `R` **между множествами** `X1, X2, ..., Xn` называют *подмножество декартового произведения* `X1 × X2 × ... × Xn`, то есть *множество*, *содержащее некоторые элементы* (кортежи) множества `X1 × X2 × ... × Xn`. Число `n` - **арность отношения**.\n\nНапример, *множество* `{ (Сара, 2019), (Рози, 2020) }` является *бинарным отношением между* множествами `N` и `D`.\n\n*Отношения* могут *обладать* некоторыми *свойствами*и *наделять ими* свои *элементы*.\n\nПримеры известных *отношений*: *меньше* (`x < y`), *больше* (`x > y`), *равно* (`x = y`), перпендикулярность, параллельность (`x || y`), `x - отец y` и так далее.\n\n\n### Операция в теории множеств\n\nПусть *имеются множества* `X` и `Y`, причём *множество* `X` является *подмножеством декартова произведения* `n` *непустых множеств*: `X = X1 × X2 × … × Xn`. Тогда **n-арная операцией** называется *бинарное отношение* `f ⊆ X × Y`, такое, что *каждому значению* `x ∈ X` *соответствует единственное значение* `y ∈ Y`. Здесь *элемент* `x` *представляет собой кортеж* `(x1, x2, …, xn)`.\n\nЭлементы `x1, x2, …, xn` называют **операндами**, а элемент `y` - **результатом операции** `f`.\n\n*Общее обозначение n-арной операции* `f`: `f(x1, x2, ..., xn) = y`.\n\n*Операция* с *одним операндом* называется **унарной операцией**, с *двумя* - **бинарной**.\n\n*Операции* обычно *имеют* своё *уникальное символьное обозначение*.\n\nСамыми *широко используемыми операциями* являются *арифметические операции*. Например, *бинарная операция “сложение”* (`x1 + x2 = y`), *бинарная операция “возведение в степень”* (`x1 ^ x2 = y`), *унарная операция “факториал”* (`x! = y`) и так далее.\n\n## Тип данных (домен)\n\n**Тип данных** - это *множество допустимых значений*. *Любое значение* (*переменной*, *константы*, *параметра*, *атрибута* и так далее) *принадлежит* какому-то *типу данных*.\n\nВ *реляционной модели данных тип данных* также *называют* **доменом**.\n\nВообще говоря, *основными типами данных* в *программировании* являются *числовой* (`7`, `3.14`), *строковый* (`\"развитие\"`, `'enginer'`) и *логический* (`истина`, `ложь`).\n\n*На практике* же часто *выделяют больше типов*, чтобы *сузить область допустимых значений* и, таким образом, больше *ограничить пользователя*. Например, *числа подразделяют* на *целые* (`integer`) и *дробные* (`float`).\n\nЕсли мы указываем, что *значение принадлежит какому-то типу* то *другому типу оно* уже *приналежать не может*.\n\n*Типы*, *предопределённые в системе*, называют **базовыми типами**. Многие *системы позволяют* пользователю *на основе базовых типов создавать* новые типы, называемые в таком случае **пользовательскими типами**.\n\nВ *реляционной модели данных обязательным базовым типом* является лишь *логический* (`boolean`): *без него невозможно* рассматривать *операции над отношениями*.\n\n## Отношение и его структура\n- [Об отношении и его структуре](#об-отношении-и-его-структуре)\n- [Атрибуты и их значения](#атрибуты-и-их-значения)\n- [Заголовок отношения](#заголовок-отношения)\n- [Тело отношения](#тело-отношения)\n\n\n### Об отношении и его структуре\n\nПонятие *отношения* в *реляционной модели данных довольно близко по смыслу* к понятию *отношения* в *теории множеств*. При этом *множествами* `X1, X2, ..., Xn` *выступают типы данных* `T1, T2, ..., Tn`, *являющиеся множествами по определению*. \n\n*Ниже* рассмотрим несколько *видоизменённое математическое определение*.\n\nПусть *имеются типы данных* `T1, T2, ..., Tn` (*не обязательно различные*), тогда **n-арным отношением** (англ. relation) `R` называют *подмножество декартового произведения* `T1 × T2 × ... × Tn`. Говоря другими словами, *отношение* - это некоторое *множество* (набор) *кортежей*, имеющих *одинаковую схему*, то есть *кортежей* `(t1, t2, ..., tn)`, *элементы* `t1, t2, ..., tn` которых *принадлежат типам данных* `T1, T2, ..., Tn` *соответственно*.\n\nНа деле же *отношение реляционной модели данных имеет* особую *структуру* и *не является множеством* как таковым. *Отношение* `R` *состоит* из *заголовка* `H` и *тела* `B`. Фактически, его можно *рассматривать* как *упорядоченную пару заголовка и тела*: `R = (H, B)`.\n\n### Атрибуты и их значения\n\n**Атрибут** (лат. attributio - признак) в *философии* - *отличительный, существенный, неотъемлемый признак* (черта, свойство) *предмета* или *явления*. *Атрибуты независимы* друг от друга, то есть *один атрибут не может повлиять на другой*.\n\nВ *программировании* **атрибут** (англ. attribute) представляет собой *свойство* некоторого *объекта*, *элемента* или *файла*.\n\n\n*Объект характеризуется* и *определяется* *значениями своих атрибутов*.\n\n\n*Атрибут объекта* обычно *состоит* из **имени атрибута** (name) и **значения атрибута** (value). \n\nНапример, *“цвет глаз”* можно назвать *атрибутом человека*, тогда *“карий”, “зелёный”, “голубой”* и *“серый”* - некоторые из возможных *значений атрибута*.\n\n\n\nВ *реляционной модели данных* некоторый *класс объектов* *представляется отношением* и *характеризуется конечным множеством атрибутов*, а *информация (данные) о конкретном объекте хранится* в *виде набора значений атрибутов*. В этом и кроется *главное различие между отношением* (из *реляционной модели данных*) и *множеством* (из *теории множеств*): *множество* обычно *представляет* собой *набор наименований объектов* в то время, как *отношение представляет множество характеристик объектов*. \n\n\nВ *реляционной модели данных* *все значения одного атрибута принадлежит одному конкретному типу данных*. Например, *нельзя*, чтобы *атрибут имел числовые* и *строковые значения одновременно*.\n\n\nФактически, *атрибут* в *реляционной модели данных* можно *рассматривать* как *упорядоченную пару* `(a, t)`, где `a` - *название атрибута*, `t` - *название типа данных*, которому *принадлежат значения атрибута*. В таком случае *значение атрибута* можно *рассматривать* как *упорядоченную тройку* `(a, t, v)`, где `v` - *значение атрибута* `a` *типа* `t`.\n\n\n### Заголовок отношения\n\nГоворя простыми словами, *заголовок отношения состоит* из *атрибутов*.\n\n**Заголовком** (*схемой*) `H` (англ. header) **отношения** `R` называют *множество упорядоченных пар* `(a, t)`, где `a` (англ. attribute) - *название атрибута*, `t` (англ. type) - *название типа данных* (*не сам тип данных*, являющийся множеством), которому *принадлежат значения атрибута* `a`.\n\nТаким образом, *заголовок n-арного отношения* имеет вид: `H = { (a1, t2), (a2, t3), ..., (an, tn) }`.\n\nПример *заголовка* для *отношения между множествами* `N`, `J`, `D`: `H = { (Имя, string), (Профессия, string), (Дата, date) }`.\n\n\n*Число атрибутов* `n` называется **степенью отношения** или **арностью отношения**. \n\n*Отношение* с *одним атрибутом* называют **унарным**, с *двумя* — **бинарным**, с *тремя* - **тернарным** и так далее.\n\n\nПоскольку *множество не может содержать одинаковые элементы*, а *типы данных* в заголовке могут *повторяться*, **названия атрибутов** должны быть **уникальными**. *Иначе возможна* была бы *ситуация* `H = { (Дата, date), (Дата, date) }`, что *недопустимо для множеств*.\n\n**Заголовки** двух **отношений совпадают**, если *совпадают* их *количество атрибутов, все названия атрибутов* и *типы данных атрибутов*. \n\n\n### Тело отношения\n\nГоворя простыми словами, *тело отношения* - это *множество кортежей*, *содержащих значения атрибутов*.\n\nИтак, *заголовок представляет* собой *множество атрибутов* `(a, t)`: `H = { (a1, t1), (a2, t2), ..., (an, tn) }`.\n\n\n*Значение атрибута* должно быть *привязано* к самому *атрибуту*, поэтому оно *представляется упорядоченной парой* `((a, t), v)` или, что то же самое, *упорядоченной тройкой* `(a, t, v)`, где `v` (англ. value) - *значение атрибута* `a` *типа* `t`. Но, поскольку *название атрибута уникально*, а *тип данных привязан* к нему *парой* в *заголовке* отношения, *значение атрибута* можно *представить* ещё *проще* - *упорядоченной двойкой* `(a, v)`.\n\nПусть имеется `n` атрибутов `a1, a2, ..., an`. Тогда **тело** `B` (англ. body) **отношения** `R` - это *множество кортежей* `k = ((a1, v1), (a2, v2), ..., (an, vn))`: `B = { k1, k1, ..., km }`. \n\n*Количество упорядоченных пар* `(a, v)` в *кортеже* `k` *должно совпадать* с *количеством атрибутов* (*степенью отношения*) `n`.\n\nЧисло `m` (*число кортежей* `k`) называется **кардинальным числом отношения** или **кардинальностью отношения**.\n\nСтоит отметить, что, *несмотря на математическое определение кортежа* и *начальное определение автора*, *кортеж* `k` *не обязательно должен быть упорядочен*. Действительно, *значения напрямую привязаны к* своим *атрибутам упорядоченными парами* и в *случае перестановки* `((a2, v2), (a1, v1))` эта *связь не нарушается*. Более того, сами *атрибуты в заголовке не упорядочены* по *определению множества*, а значит *упорядоченность значений избыточна*. *Упорядоченность* *имела бы смысл*, если бы *кортежи* `k` *имели вид*: `(v1, v2, ..., vn)`. Далее *при необходимости* в *упорядоченности*, чтобы *не путать упорядоченные математические кортежи* и *неупорядоченные кортежи* `k`, будем *использовать понятия “упорядоченный набор”*, *“упорядоченная пара”* и *“упорядоченная тройка”*.\n\nПусть *имеется заголовок* `H` вида:\n```\nH = { (Имя, string), (Профессия, string), (Дата, date) }\n```\n\nНиже представлен *пример тела* `B`, *соответствующего заголовку* `H`:  \n```\nB = {\n  ((Имя, Сара), (Профессия, программист), (Дата, 2019)),\n  ((Имя, Рози), (Профессия, дизайнер), (Дата, 2019)),\n}\n```\n\n\nГрафически, *упорядоченная пара* `(a, v)` - одна *ячейка таблицы*, а *кортеж* `k` - *строка таблицы*.\n\n## Свойства отношений\n\n*Свойства отношения опираются* на *его структуру*. \n\nПоскольку *заголовок* `H` и *тело* `B` *отношения* `R` *являются множествами*, их *свойства вытекают* из *свойств множеств*:  \n1) *Тело отношения* `B` *не может содержать* двух и более *одинаковых кортежей*.  \n2) *Порядок следования атрибутов* в *заголовке* `H` отношения *не имеет значения*.  \n3) *Порядок следования кортежей* в *теле* `B` отношения *не имеет значения*.  \n\n## Графическое представление отношения (таблица)\n- [Понятие таблицы](#понятие-таблицы)\n- [Сравнение понятий отношения и таблицы](#сравнение-понятий-отношения-и-таблицы)\n\n### Понятие таблицы\n\n*Отношение удобно представлять* в виде **таблицы**, **столбцы** которой *соответствуют атрибутам*, **строки** - *кортежам*, а **ячейки таблицы** (*пересечения строк* и *столбцов*) - *значениям атрибутов*. \n\nОтношение `R` из *последнего примера выше* можно *представить таблицей* следующего вида:\n#### Отношение `R`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\n\n### Сравнение понятий отношения и таблицы\n\n*Отношения* нередко *называют таблицами*, но это *не* совсем *правильно*: *таблица* лишь *отображает структурные элементы отношения* в *удобном формате*, но *не отражает сути понятия отношения* и его *свойства*. \n\nНапример, *понятие таблицы не подразумевает уникальности строк* в то время, как в *отношении требуется уникальность кортежей*.\n\nДля *равнозначности понятий таблицы и отношения* на *понятие таблицы необходимо накладывать* некоторые *ограничения*:  \n1) *Все свойства отношений должны быть верны* для *строк и столбцов таблицы*. \n2) *Таблица не должна содержать дополнительных возможностей*, *выходящих за рамки* понятия *отношения*. (например, *скрытые поля* с *порядком строк* и *скрытые методы* их *сортировки*, *встроенные в строки даты* и так далее).\n\n## Ключи\n- [Об идентификации](#об-идентификации)\n- [Потенциальный ключ](#потенциальный-ключ)\n- [Первичный ключ](#первичный-ключ)\n- [Простые и составные ключи](#простые-и-составные-ключи)\n- [Значение ключа](#значение-ключа)\n- [Естественный ключ](#естественный-ключ)\n- [Суррогатный ключ](#суррогатный-ключ)\n- [Внешний ключ](#внешний-ключ)\n\n### Об идентификации\n\n**Идентификатор**, **ID** (англ. identifier — опознаватель) — это *уникальный признак объекта*.\n\n*Идентификаторы* позволяют **идентифицировать объект**, то есть *выделить* (*найти*) его *среди других объектов*. \n\nНапример, можно по *номеру паспорта определить* (*идентифицировать*) *человека*, по *IP-адресу* - *физический компьютер*, по *координатам* - *точку* на карте, по *шрих-коду* - *товар* в магазине, по *QR-коду* - *ссылку*, по *ссылке* - *сайт* и так далее.\n\nДля *идентификации кортежей отношения* используются *ключи*.\n\n### Потенциальный ключ\n\nКак мы уже знаем, *каждый кортеж отношения уникален*. Поэтому *при необходимости найти конкретный кортеж* операцией *выборки*, *достаточно перечислить все значения* его *атрибутов*, но обычно *так не делают*.\n\nПусть *имеется отношение* `R`, содержащее в заголовке `n` *атрибутов*.\n\n**Потенциальным ключом** (англ. candidate key) называют *подмножество из* `m <= n` *атрибутов отношения* `R`, которое *удовлетворяет условиям* **уникальности** и **минимальности**. \n\n**Условие уникальности** *требует*, чтобы *в отношении* `R` *не могло существовать двух кортежей*, содержащих *одни и те же значения тех атрибутов*, *из которых состоит потенциальный ключ*.\n\nВажно отметить, что `R` именно *“не может содержать двух кортежей”*, а не *“не содержит двух кортежей”*. Например, если в *компании* работает *несколько сотрудников* и *все имеют различный цвет глаз*, это *не означает*, что *цвет глаз* является *уникальным идентификатором*: всегда *может появиться новый сотрудник* с *уже существующим цветом глаз* и *идентификация станет невозможной*. \n\nТаким образом, *уникальность опирается не на наличие объекта* в *текущий момент времени*, а на *особенности (природу) класса объектов*, которые *допускают возможность появления нового объекта* в *любой момент времени*. *Неплохими идентификаторами сотрудника компании* могут стать: *серия и номер паспорта*, *номер трудовой книги*, *рабочий email* или *номер телефона* (все они *не могут повториться у двух сотрудников*).\n \n**Условие минимальности** (*несократимости*) *требует*, чтобы *среди атрибутов потенциального ключа отсутствало меньшее подмножество* из `k <= m` *атрибутов*, которое *удовлетворяет условию уникальности*. \n\nДругими словами, *при исключении любых атрибутов* из *потенциального ключа условие уникальности* должно *перестать выполняться*.\n\n\n\nВ отношении *всегда существует хотя бы один потенциальный ключ* (*множество всех значений атрибутов* кортежа), поскольку *все кортежи отношения уникальны* по определению.\n\nВ отношении может *одновременно существовать несколько потенциальных ключей*.\n\nНа *примере ниже* представлено *отношение* с *двумя потенциальным ключами*, *каждый* из которых *состоит* из *одного элемента*: `{ Номер телефона }` и `{ Email }`.\n\n#### Отношение `R` (пользователи)\nИмя | Фамилия | Номер телефона | Email\n:--: | :--: | :--: | :--:\nДжон | Дрим | 8768 | johndream@example\nФрэнк | Старк | 1442 | frankstark@example\nАлен | Стоун | 3567 | alenstone@example\n\n### Первичный ключ\n\nКак уже отмечалось ранее, *отношение может содержать несколько потенциальных ключей*, *по каждому* из которых *можно идентифицировать объект*. *Один* из этих ключей *выбирается* в качестве *основного* и называется **первичным ключом** (англ. primary key) **отношения**, *оставшиеся потенциальные ключи* называют **альтернативными ключами отношения**.\n\nЕсли *в отношении содержится только один потенциальный ключ*, то он и *является первичным ключом* данного отношения.\n\nЕсли *в отношении содержится несколько потенциальных ключей*, то имеется *два основных критерия выбора первичного ключа*:\n1) **Удобство использования**. Чаще всего *выбирается потенциальный ключ* с *наименьшим физическим размером* (*занимает меньше памяти* на *компьютере*) или *содержит наменьшее количество атрибутов*. \n2) **Сохранение уникальности с течением времени**. Некоторые *идентификаторы могут утрачивать* свою *уникальность* со *временем*.\n\n### Простые и составные ключи\n\n*Первичный ключ*, *состоящий* из *одного атрибута*, называют **простым ключом**, а *состоящий* из *нескольких атрибутов* - **составным ключом**.\n\n*Чаще всего* используются *простые ключи* (`id`, `number`, `username`, `email`, `phone`), поскольку их *проще хранить*, с ними *проще работать*. \n\n*Редко*, но бывают случаи, когда *удобнее* использовать *составной ключ* или *нет возможности* использовать *простой ключ*.\n\nРассмотрим пример *составного ключа*, *разбив телефонный номер* на *группы*. В *системе*, *работающей* с *телефонными номерами*, *разбиение номера* могло бы *оптимизировать поиск* и *фильтрацию*. Из чего *состоит телефонный номер*? *Его схема различна* в *разных странах*. Например, *американский номер состоит* из *кода страны*, *кода региона*, *кода телефонного узла* и *абонентского номера*: `+1 (234) 235 1779`. \n\n#### Отношение `R`\nКод региона | Код узла | Абонентский номер | Фамилия владельца\n:--: | :--: | :--: | :--:\n234 | 145 | 1984 | Ирвинг\n234 | 310 | 7817 | Купер\n235 | 420 | 6168 | Редли\n\nТогда *первичный ключ* `PK` отношения `R` имеет вид:\n```\n{\n  (Код региона, integer),\n  (Код узла, integer),\n  (Абонентский номер, integer)\n}\n```\n\n*Исключение любых атрибутов* из *ключа* `PK` *приведёт* к *утрате уникальности* этого *ключа*.\n\n\n\nДругие *примеры составных ключей*: *серия* и *номер паспорта* (не уникальны по-отдельности), *масть* и *число* на *игровой карте*, *номер ряда* и *номер места* в *кинотеатре*, *составные части URL*, *составные части адреса электронной почты* и так далее.\n\n\n\n<!--\nИдентификаторы могут со временем меняться: меняются паспорт, номер телефона, email и даже отпечаток пальца, поэтому часто в качестве идентификатора используют случайно сгенерированный хэш.\n-->\n\n\n\n### Значение ключа\n\n**Значением ключа** называют *кортеж*, *состоящий* из *значений атрибутов* этого *ключа*.\n\n#### Отношение `R` (места в кинозале)\nНомер ряда | Номер места | Место для влюблённых | VIP |\n:--: | :--: | :--: | :--:\n7 | 18 | true | false\n7 | 19 | true | false\n3 | 22 | false | false\n10 | 3 | false | true\n\nТогда *первичный ключ* `PK` *отношения* `R` имеет вид:\n```\n{\n  (Номер ряда, integer),\n  (Номер места, integer)\n}\n```\n*Кортеж* `((Номер ряда, 7), (Номер места, 18))` - *одно* из доступных *значений ключа* `PK`.\n\n### Естественный ключ\n\n\n*Разделим понятия реального объекта* (дом, машина, человек, дерево) и его *физического представления* в некоторой *базе данных* (например, *набор значений атрибутов*).\n\n\n**Естественный ключ** (англ. natural key, business key, domain key) - это *уникальный ключ*, который *идентифицирует* некоторый *реальный объект*, при этом *полностью зависит* от *свойств* (*природы*) этого *объекта* и *не зависит* от его *физического представления* (реализации) в *конкретной базе данных*.\n\n*Естественный ключ* *подбирается* на основе *реальных наблюдений за объектом*, поэтому его *уникальность опирается исключительно* на *уникальные черты реального объекта*.\n\nВ рамках *реляционной модели данных природа объекта описывается* с помощью *атрибутов*, а поскольку *потенциальный ключ* напрямую *зависит* от них, он и *является естественным ключом*.\n\nКак и в случае с *потенциальным ключами*, *естественных ключей* у *отношения* может быть *несколько*.\n\n*Примеры* *естественных ключей* всё *те же*, что и *примеры* *потенциальных ключей*: *человека* можно *идентифицировать* по *серии* и *номеру паспорта*, *пользователя* - по *email*, *дом* - по *адресу* и так далее. *Всё* это - *натуральные ключи*, *взятые* из *предметных областей* (*доменов*) соответствующих *объектов*.\n\n*Естественные ключи* *являются полноценной частью приложения* и *не скрываются от глаз пользователя* (например, на *сайтах* можно часто *видеть email* или *номер телефона*).\n\n### Суррогатный ключ\n\n**Суррогатный ключ** (англ. surrogate key, pseudokey, synthetic key) - это *уникальный идентификатор* как для *реального объекта*, так и для его *физического представления* в *базе данных*.\n\nВ *отличии* от *естественного ключа*, *суррогатный ключ никак не зависит* от *свойств* *реального объекта*, а следовательно *и от атрибутов*. *Суррогатный ключ генерируется системой* по *заданному алгоритму*, поэтому *не имеет смысла вне системы*.\n\nЧаще всего *суррогатный ключ представляет собой* некоторый *хэш* (*произвольно сгенерированная строка*) или *целочисленное число*.\n\nСамые распространённые *способы генерации суррогатного ключа*:  \n1) **Universally Unique Identifier** (*UUID*). *Пример*: `9fc2e9ad-fae7-4f25-ae48-3f45284f2299`.\n2) **Globally Unique Identifier** (*GUID*). *Пример*: `75f067fa-b95f-405c-a1bd-29164461a65f`.\n3) **Object Identifier** (*ObjectID*). *Пример*: `ObjectId(\"507f1f77bcf86cd899439017\")`.\n4) `AUTO_INCREMENT` (MySQL), `AUTOINCREMENT` (SQLite) `SEQUENCE` (SQL Server, Oracle).\n\n*Суррогатные ключи* относятся к *внутренней логике* (внутренней реализации) *приложения*. Они *не несут* в себе никакой *полезной информации* для *пользователя* и поэтому *скрыты от его глаз*.\n\n\nУ *отношения* может быть *несколько суррогатных ключей*.\n\n\n*Преимущества использования суррогатных ключей*:  \n1) **Неизменность** (*стабильность*). Благодаря *независимости суррогатного ключа* от *атрибутов объекта*, *ключ остаётся неизменным* при *изменении* этого *объекта*.  \n2) **Компактность**. *Суррогатный ключ* всегда *состоит* только из *одного значения*, *размер* которого *фиксирован* и обычно *невелик* (*размер ключа зависит* от *алгоритма генерации*, который *можно выбирать*), что *уменьшает расход памяти* и *увеличивает скорость поиска объектов по ключу*.\n3) **Единообразие**. Если *все ключи* в *системе создаются* по *одному алгоритму* (например, *везде* используется *UUID*), то *логика обработки данных упрощается* и *становится предсказуемой* для *разработчика* в *любом месте системы*.\n4) **Уникальность ключа во всей системе** - *не только* в отдельном *отношении*.\n\nОсновным *недостатком суррогатного ключа* является то, что он *не связан* с самим *объектом по смыслу*: по *такому ключу нельзя судить* как о *содержимом объекта*, так и о *классе*, которому *этот объект принадлежит*.  \n\n\n\n\n### Внешний ключ\n\n\n\n<!--\nГоворя простыми словами, *внешний ключ* - это *подмножество атрибутов* некоторого *отношения* `S`, *значения* которых *совпадают* со *значениями атрибутов потенциального ключа* другого *отношения* `R`. \n-->\n\n*Внешние ключи подобны ссылкам* на *объекты*. *Внешний ключ* позволяет в *кортеже одного отношения сослаться* на *кортеж другого отношения*, поскольку *хранит* *значение* *потенциального ключа* этого отношения. \n\n*Использование внешних ключей лишает* нас *необходимости в дублировании данных*. На эти *данные можно* просто *сослаться* из *другого места*. Такой подход\n\n<!--\n*Связи* позволяют *хранить данные* в *нормализованном виде* (без дублирования).\n(внешние ключи)\n-->\n\nПусть *имеются отношения* `R` и `S` (*не обязательно различные*). **Внешним ключом** (англ. foreign key) `FK` называют *такое подмножество атрибутов* отношения `S`, для которого *выполняются условия*:\n1) Отношение `R` *имеет такой потенциальный ключ* `CK`, что *у ключей* `CK` и `FK` *совпадают количество атрибутов* и их *типы данных* (*при переименовании атрибутов ключи совпадают*).\n2) *Каждое значение* внешнего ключа `FK` в *некотором кортеже* отношения `S` *совпадает* со *значением* потенциального включа `CK` в *некотором кортеже* отношения `R`. Иначе говоря, *множество значений* ключа `FK` *содержится* (является *подмножеством*) в *множестве значений* ключа `CK`.\n\n*Обозначение внешнего ключа* `FK`, *ссылающегося* на *потенциальный ключ* `CK`: `FK → CK`.\n\n\n*Отношение* `R`, *содержащее потенциальный ключ* `CK`, называют **главным** (*родительским*) **отношением**, *отношение* `S`, *содержащее внешний ключ* `FK`, называют **подчинённым** (*дочерним*) **отношением**.\n\n*Ниже* представлен *пример* простенькой реализации *чата*. *Родительское отношение* `R` имеет *потенциальный ключ* `ID`, *дочернее отношение* `S` имеет *два внешних ключа*: `SENDER_ID → ID` (*идентификатор отправителя*) и `RECIPIENT_ID → ID` (*идентификатор получателя*).\n\n\n#### Отношение `R` (пользователи)\nID | Имя пользователя\n:--: | :--:\ncde | Сара\nfkh | Рози\n\n#### Отношение `S` (сообщения)\nID | Текст | SENDER_ID | RECIPIENT_ID\n:--: | :--: | :--: | :--:\nerksh | Доброй ночи | cde | fkh\njhtbe | И тебе | fkh | cde\n\n\n## Связи между отношениями\n- [О связях между отношениями](#о-связях-между-отношениями)\n- [Один к одному (1:1)](#один-к-одному-11)\n- [Один ко многим (1:M)](#один-ко-многим-1m)\n- [Многие к одному (M:1)](#многие-к-одному-m1)\n- [Многие ко многим (M:M)](#многие-ко-многим-mm)\n- [Ссылочная целостность](#ссылочная-целостность)\n\n### О связях между отношениями\n\n*Связи между отношениями похожи* по *смыслу* на *бинарные отношения из теории множеств*. *Связи передают* то, как *объекты соотносятся* (связаны) *друг с другом*. В *терминах реляционной модели данных*: *связь устаналивает соотношение* (соответствие) *между кортежами двух отношений*, причём *не обязательно различных*.\n\n*Связи между* двумя *отношениями устанавливаеются* при помощи *внешних ключей*.\n\n\nКак уже *ранее* отмечалось, *отношение*, *содержащее внешний ключ* `FK → CK`, называют **дочерним**, а *отношение*, *содержащее потенциальный ключ* `CK`, называют **родительским**.\n\n*Между родительским* и *дочерним отношениями* существует *4 вида связей*:\n- [Один к одному (1:1)](#один-к-одному-11)\n- [Один ко многим (1:M)](#один-ко-многим-1m)\n- [Многие к одному (M:1)](#многие-к-одному-m1)\n- [Многие ко многим (M:M)](#многие-ко-многим-mm)\n\n### Один к одному (1:1)\n\n**Один к одному** `1:1` (англ. one-to-one) - такая *связь между отношениями `R` и `S`*, при которой *каждому кортежу* отношения `R` *соответствует только один* (или *ни одного*) *кортеж* отношения `S` и *каждому кортежу* отношения `S` *соответствует только один* (или *ни одного*) *кортеж* отношения `R`. Другими словами, *отношение* `R` *имеет потенциальный ключ* `R.CK` и *внешний ключ* `R.FK → S.CK`, а *отношение* `S` *имеет потенциальный ключ* `S.CK` и *внешний ключ* `S.FK → R.CK`.\n\nПример *связи* `1:1`: *одно королевство* - *один король* (королева). \n\n*Отношение* `R`, представленное *таблицей* ниже, содержит *потенциальный ключ* `R.ID` и *внешний ключ* `R.KINGDOM_ID → S.ID`, *отношение* `S` содержит *потенциальный ключ* `S.ID` и *внешний ключ* `S.KING_ID → R.ID`.\n\n\n#### Отношение `R` (современные короли и королевы)\nID | Имя | KINGDOM_ID\n:--: | :--: | :--:\nval | Виллем-Александр | NL\nel2 | Елизавета II | GB\nk16 | Карл XVI Густав | SE\nma2 | Маргрета II | DK\nfil | Филипп | BE\nfi6 | Филипп VI | ES\nha5 | Харальд V | NO\n\n#### Отношение `S` (страны)\nID | Название | KING_ID\n:--: | :--: | :--:\nBE | Бельгия | fil\nGB | Великобритания | el2\nDK | Дания | ma2\nES | Испания | fi6\nNL | Нидерланды | val\nNO | Норвегия | ha5\nSE | Швеция | k16\n\n<!--\nСтрана и столица\n-->\n\n\n### Один ко многим (1:M)\n\n\n<!--\nОпределение через подмножество\n-->\n\n**Один ко многим** `1:M` (англ. one-to-many) - такая *связь между отношениями `R` и `S`*, при которой *каждому кортежу* отношения `R` *соответствует несколько* (или *ни одного*) *кортежей* отношения `S` и *каждому кортежу* отношения `S` *соответствует не более одного кортежа* отношения `R`. Другими словами, *отношение* `R` *имеет потенциальный ключ* `R.CK`, а *отношение* `S` *имеет внешний ключ* `S.FK → R.CK`.\n\n<!--\n\nВ одном диалоге много сообщений, один автор может написать несколько книг.\n\nВ одной стране много городов (штатов).\n\nОдна страна охватывает несколько часовых и климатических поясов\n\n-->\n\nРассмотрим интересный *пример связи* `1:M`: *один отец* может иметь *нескольких детей*, но *каждый ребёнок* имеет *лишь одного биологического отца*. \n\nКак уже *ранее отмечалось*, *отношения* `R` и `S` *не обязательно различны* (они *могут представлять* собой *одно и то же* отношение). *Отношение* `R`, представленное *таблицей* ниже, содержит *потенциальный ключ* `R.ID` и *внешний ключ* `R.PARENT_ID → R.ID`.\n\n#### Отношение `R` (отцы и дети)\nID | Имя | Возраст | PARENT_ID\n:--: | :--: | :--: | :--:\nP1 | Джон | 65 | NULL\nP2 | Джим | 43 | P1\nP3 | Джек | 20 | P2\nP4 | Джоан | 22 | P2\n\n### Многие к одному (M:1)\n\n**Многие к одному** `M:1` (англ. many-to-one) - *зеркальное отражение связи* `1:M` (в *определении достаточно поменять местами отношения* `R` и `S`).\n\n\nПример *связи* `M:1`: *много городов* может *принадлежать* *одной стране*, но *один город не может принадлежать двум странам одновременно*.\n\n*Отношение* `R`, представленное *таблицей* ниже, имеет *потенциальный ключ* `R.ID` и *внешний ключ* `R.COUNTRY_ID → S.ID`, а *отношение* `S` *содержит только потенциальный ключ* `S.ID`.\n\n#### Отношение `R` (города)\nID | Имя | COUNTRY_ID\n:--: | :--: | :--:\nkiev | Киев | UA\nlviv | Львов | UA\nsydney | Сидней | AU\n\n#### Отношение `S` (страны)\nID | Название\n:--: | :--:\nUA | Украина\nAU | Австралия\n\n<!--\nПоменять пример с примером выше местами\n-->\n\n\n### Многие ко многим (M:M)\n\n**Многие ко многим** `M:M` (англ. many-to-many) - такая *связь между отношениями `R` и `S`*, при которой *каждому кортежу* отношения `R` *соответствует несколько* (или *ни одного*) *кортежей* отношения `S` и *каждому кортежу* отношения `S` *соответствует несколько* (или *ни одного*) *кортежей* отношения `R`. С *помощью двух отношений* такую связь на практике *реализовать нельзя*. \n\nДругими словами, *отношение* `R` *имеет потенциальный ключ* `R.CK`, *отношение* `S` *имеет потенциальный ключ* `S.CK` и *существует* такое *отношение* `T`, *тело* которого *состоит* из *кортежей* `(T.FK1 → S.CK, T.FK2 → R.CK)`, содержащих *внешние ключи*, *ссылающиеся* на *потенциальные ключи отношений* `R` и `S`. *Отношение* `T` имеет *связь* `M:1` (многие к одному) с *каждым из отношений* `R` и `S`.\n\n\nПример *связи* `M:M`: *каждый сотрудник* компании может знать *несколько разговорных языков*, *несколько сотрудников* могут говорить на *одном языке*.\n\n### Отношение `R` (разговорные языки)\nID | Язык\n:--: | :--:\nen | Анлийский\nru | Русский\nfr | Французский\n\n### Отношение `S` (сотрудники)\nID | Имя | Должность\n:--: | :--: | :--:\nsara1 | Сара | программист\ndina7 | Дина | дизайнер\n\nПусть *Сара* знает *английский* и *французский*, а *Дина* - *английский* и *русский*, тогда:\n\n### Отношение `T` (соответствие между языками и сотрудниками)\nID | WORKER_ID | LANGUAGE_ID\n:--: | :--: | :--:\nt1 | sara1 | en\nt1 | sara1 | fr\nt2 | dina7 | en\nt2 | dina7 | ru\n\nВ *таблице выше* можно увидеть, что *отношение* `T` имеет *внешний ключ* `T.LANGUAGE_ID → R.ID` (*связь* `M:1` между `T` и `R`) и *внешний ключ* `T.WORKER_ID → S.ID` (*связь* `M:1` между `T` и `S`).\n\n\nЕщё один пример *связи* `M:M`: у *научной статьи* может быть *несколько авторов*, у *автора* может быть *много научных статей*.\n\n\n### Ссылочная целостность\n\n*Первичные ключи могут изменяться*. Простой пример: *пользователь* хочет *сменить email* или *номер телефона*. Если *существует связь*, *использующая* подобный *потенциальный ключ* `CK`, то *ссылающейся* на него *внешний ключ* `FK → CK` так же *должен измениться*.\n\n**Ссылочной целостностью** (англ. referential integrity) называют *корректность значений всех внешних ключей*, то есть *хранение* *актуального значения потенциального ключа* `CK` в *каждом внешнем ключе* `FK → CK`.\n\nЧтобы *обеспечить ссылочную целостность*, необходимо при *изменении значения потенциального ключа* `CK` *заменить значения всех ссылающихся* на него *внешних ключей* `FK` или *отменить операцию изменения*, если *замена невозможна*. \n\nВ некоторых *системах управления базами данных* *ссылочная целостность поддерживается автоматически*.\n\n\n## Операции над отношениями (реляционные операции)\n- [О реляционных операциях](#о-реляционных-операциях)\n- [Ограничения на применение операций](#ограничения-на-применение-операций)\n- [Объединение отношений](#объединение-отношений)\n- [Пересечение отношений](#пересечение-отношений)\n- [Вычитание отношения](#вычитание-отношения)\n- [Декартово произведение отношений](#декартово-произведение-отношений)\n- [Проекция отношения](#проекция-отношения)\n- [Выборка (ограничение)](#выборка-ограничение)\n- [Соединение](#соединение)\n- [Деление отношений](#деление-отношений)\n- [Переименование атрибутов отношения](#переименование-атрибутов-отношения)\n\n### О реляционных операциях\n\n\n*Большинство операций над отношениями* являются *бинарными*, то есть такими, которые *оперируют двумя отношениями*. При необходимости *произвести бинарную операцию над большим количеством отношений*, её необходимо *применить последовательно несколько раз*: `P • R • S • T = ((P • R) • S) • T`.\n\n\n*Любая операция над отношениями*, *результатом* которой является *отношение*, называется **реляционной операцией**.\n\n*Систему реляционных операций* называют **реляционной алгеброй**.\n\nМожно придумать *бесконечно много реляционных операций*, но *абсолютное большинство* из них будут *выражаться через другие* реляционных операции, то есть *будут являться результатом последовательного применения нескольких* более простых *реляционных операции*.\n\n*Эдгар Кодд*, *создатель реляционной модели данных*, *ввёл* следующий *набор из 8 реляционных операций*:  \n- [Объединение отношений](#объединение-отношений)\n- [Пересечение отношений](#пересечение-отношений)\n- [Вычитание отношения](#вычитание-отношения)\n- [Декартово произведение отношений](#декартово-произведение-отношений)\n- [Проекция отношения](#проекция-отношения)\n- [Выборка (ограничение)](#выборка-ограничение)\n- [Соединение](#соединение)\n- [Деление отношений](#деление-отношений)\n\nОперации *объединения*, *пересечения*, *вычитания* и *декартова произведения* относят к **теоретико-множественным операциям**: они являются *аналогами одноимённых операций над множествами* в *теории множеств*.\n\nОперации *проекции*, *выборки*, *соединения* и *деления* называют **специальными операциями**: они *имеют смысл только* в рамках *реляционной модели данных*.\n\nОперации *объединения*, *вычитания*, *проекции*, *декартова произведения* и *выборки* являются **примитивными реляционными операциями** — такими *операциями*, которые *нельзя выразить друг через друга*. \n\nДалее *в примерах отношения* будут *представляться таблицами* для *наглядности*.\n\n\n### Ограничения на применение операций\n\nОперации *объединения*, *пересечения* и *вычитания требуют совпадения заголовков* у своих *отношений-операндов*. \n\nОперации *декартова произведения* и *соединения* требуют, чтобы *в заголовках* их отношений-операндов *не было совпадающих названий атрибутов*. Если таковые *имеются*, то их *необходимо переименовать до применения операции*.\n\n### Объединение отношений\n\n**Объединения отношений `R` и `S`** - *бинарная операция*, *требующая совпадения заголовков* отношений `R` и `S`, *результатом* которой является *отношение* с *тем же заголовком* и *телом*, *содержащим все кортежи обоих отношений*. Иначе говоря, *каждый кортеж* отношения-результата *принадлежит или* отношению `R`, *или* отношению `S`. Если *отношения* `R` и `S` *имеют между собой совпадающие кортежи* (*пересечения*), то в *отношение-результат* попадает только один из них.\n\n*Обозначение объединения отношений* `R` и `S`: `R UNION S`.\n\n*Объединение отношений* является *аналогом объединения множеств* из *теории множеств*.\n\n*Ниже* представлен *пример объединения отношений* `R` и `S`.\n\n#### Отношение `R`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\n\n#### Отношение `S`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nРози | дизайнер | 2020\nСара | программист | 2020\n\n#### Отношение `R UNION S`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\nРози | дизайнер | 2020\n\n### Пересечение отношений\n\n**Пересечение отношений `R` и `S`** - *бинарная операция*, *требующая совпадения заголовков* отношений `R` и `S`, *результатом* которой является *отношение* с *тем же заголовком* и *телом*, *содержащим только* те *кортежи*, которые *содержатся в обоих отношений*. Иначе говоря, *каждый кортеж* отношения-результата *содержится и в* отношении `R`, *и в* отношении `S`.\n\n\n*Обозначение пересечения отношений* `R` и `S`: `R INTERSECT S`.\n\n*Пересечение отношений* является *аналогом пересечения множеств* из *теории множеств*.\n\nОперация *пересечения отношений не является примитивной*, поскольку её можно *выразить через операцию вычитания отношения*: `R INTERSECT S` = `R MINUS (R MINUS S)`.\n\n*Ниже* представлен *пример пересечения отношений* `R` и `S`.\n\n#### Отношение `R`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\n\n#### Отношение `S`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nРози | дизайнер | 2020\nСара | программист | 2020\n\n#### Отношение `R INTERSECT S`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nСара | программист | 2020\n\n\n### Вычитание отношения\n\n\n**Вычитание отношения `S` из `R`** - *бинарная операция*, *требующая совпадения заголовков* отношений `R` и `S`, *результатом* которой является *отношение* с *тем же заголовком* и *телом*, *содержащим только* те *кортежи*, которые *принадлежат* отношению `R`, но *не принадлежат* отношению `S`. \n\n\n*Обозначение вычитания* отношения `S` из `R`: `R MINUS S`.\n\n*Вычитание отношения* является *аналогом разности множеств* из *теории множеств*.\n\n*Ниже* представлен *пример вычитания отношения* `S` из `R`.\n\n#### Отношение `R`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\n\n#### Отношение `S`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nРози | дизайнер | 2020\nСара | программист | 2020\n\n#### Отношение `R MINUS S`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\n\n### Декартово произведение отношений\n\nПусть *имеются отношение* `R` с *атрибутами* `a1, a2, ..., an` и *значениями атрибутов* `x1, x2, ..., xn` в кортежах и *отношение* `S` с *атрибутами* `b1, b2, ..., bm` и *значениями атрибутов* `y1, y2, ..., ym` в кортежах.\n\n**Декартово произведение отношений `R` и `S`** - это *бинарная операция*, *требующая отсутствия совпадающих названий атрибутов между заголовками отношений* `R` и `S` (между `{ a1, a2, ..., an }` и `{ b1, b2, ..., bm }`), *результатом* которой является *новое отношение* `T`, *заголовок* которого получается в *результате конкатенации* (соединения, сцепления) *заголовков отношений* `R` и `S`, а *тело состоит из упорядоченных пар* `((x1, x2, ..., xn), (y1, y2, ..., ym))`, *содержащих всевозможные комбинации кортежей отношений* `R` и `S` *соответственно*. Для *упрощения* можно *раскрыть скобки* внутри *упорядоченной пары* (*порядок элементов сохранится*): `(x1, x2, ..., xn, y1, y2, ..., ym)`.\n\n*Обозначение декартова произведения* `R` и `S`: `R TIMES S`.\n\n\n*Декартово произведение отношений* является *аналогом декартова произведения множеств* из *теории множеств*.\n\n\n*Ниже* представлен *пример декартова произведения отношений* `R` и `S`.\n\n#### Отношение `R(Имя, Фамилия)`\nИмя | Фамилия\n:--: | :--:\nАся | Тургенева\nРози | Фицджеральд\nСара | Фаулз\n\n#### Отношение `S(Профессия, Дата)`\nПрофессия | Дата\n:--: | :--:\nдизайнер | 2019\nпрограммист | 2020\n\n*Количество атрибутов* в *отношении* `R TIMES S`: `n(R) + n(S) = 2 + 2 = 4`, *количество кортежей*: `m(R) • m(S) = 3 • 2 = 6`.\n\n#### Отношение `R TIMES S = T(Имя, Фамилия, Профессия, Дата)`\nИмя | Фамилия | Профессия | Дата\n:--: | :--: | :--: | :--:\nАся | Тургенева | дизайнер | 2019\nАся | Тургенева | программист | 2020\nРози | Фицджеральд | дизайнер | 2019\nРози | Фицджеральд | программист | 2020\nСара | Фаулз | дизайнер | 2019\nСара | Фаулз | программист | 2020\n\n\n### Проекция отношения\n\n**Проекция отношения `R`** - *унарная операция над отношением* `R`, которая позволяет *составить новое отношение* из *выбранных атрибутов* отношения `R` *и их значений* в *кортежах* отношения `R`.\n\n<!--\n*результатом* которой является *новое отношение* `R'`, содержащее в своём заголовке лишь выбранные атрибуты отношения `R`, а в своём теле - все кортежи отношения `R` с исключением тех значений атрибутов, атрибуты которых не вошли в проекцию. \n-->\n\n*Графически* *проекция отношения* `R` - это *выборка столбцов таблицы*.\n\nЕсли *при проецировании возникают кортежи-дубликаты*, то они *удаляются из результата*.\n\nЕсли *отношение* `R` *имеет* `n` *атрибутов*, то его можно *обозначить*: `R(a1, a2, ..., an)`. Тогда *проекция*, *включающая* в себя *первый* и *третий атрибуты* отношения `R` *обозначается*: `PROJECT R { a1, a3 }` или `R[a1, a3]`.\n\n*Ниже* представлен *пример проекции отношения* `R`.\n\n#### Отношение `R(Имя, Профессия, Дата)`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nРози | дизайнер | 2019\nСара | программист | 2020\nРози | дизайнер | 2020\n\n#### Отношение `R[Имя, Профессия]`\nИмя | Профессия\n:--: | :--: \nАся | дизайнер\nРози | дизайнер\nСара | программист\n~~Рози~~ | ~~дизайнер~~\n\n*Перечёркнутая строка* является *дубликатом второй строки*, поэтому она *не попадает в результат*.\n\n\n### Выборка (ограничение)\n\n**Выборка из** (*ограничение*, *селекция*) **отношения `R`** - *унарная операция* над отношением `R`, *результатом* которой является *отношение* с *тем же заголовком*, что и у `R`, *содержащее* в своём теле те *кортежи* из `R`, которые *удовлетворяют* некоторому *условию* `c`.\n\n*Условие* `c` *задаётся логическим выражением* (его *результатом* являются *истина или ложь*), которое может *содержать названия атрибутов* и *константы* (числа, строки, даты и так далее). Если *результат выполнения выражения* `c` - *истина*, то *кортеж попадает в результат выборки*, *иначе* - *не попадает*.\n\n*Обозначение выборки из отношения* `R`: `R WHERE c`.\n\n*Ниже* представлен *пример выборки из отношения* `R`.\n\n#### Отношение `R`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\nРози | дизайнер | 2020\n\n#### Отношение `R WHERE (Профессия = дизайнер)`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nРози | дизайнер | 2020\n\n#### Отношение `R WHERE (Дата > 2019)`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nСара | программист | 2020\nРози | дизайнер | 2020\n\n#### Отношение `R WHERE (Имя != Рози)`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\n\n### Соединение\n\n**Соединение** отношений `R` и `S` - *бинарная операция*, *результатом* которой является *результат последовательного применения* операций *декартового произведения* `R TIMES S` и *выборки* из *получившегося отношения* с *условием* `c`.\n\nКак и в случае с декартовым произведением, *в заголовках* отношений `R` и `S` *не должно быть совпадающих атрибутов*. *Если* такие *имеются*, то их *необходимо переименовать перед* тем, как производить операцию *соединения*.\n\nОбозначение *соединения* отношений `R` и `S`: `(R TIMES S) WHERE c`.\n\n\nОдним из *самых простых соединений* является то *соединение*, *в условии* которого используются *константа* (например, число или строка) и *один атрибут* одного из отношений-операндов соединения. Такое *соединение отношений* `R` и `S` представлено на *примере ниже*.\n\n\n#### Отношение `R`\nИмя | Фамилия\n:--: | :--:\nАся | Тургенева\nРози | Фицджеральд\nСара | Фаулз\n\n#### Отношение `S`\nПрофессия | Дата\n:--: | :--:\nдизайнер | 2019\nпрограммист | 2020\n\n#### Отношение `R TIMES S`\nИмя | Фамилия | Профессия | Дата\n:--: | :--: | :--: | :--:\nАся | Тургенева | дизайнер | 2019\nАся | Тургенева | программист | 2020\nРози | Фицджеральд | дизайнер | 2019\nРози | Фицджеральд | программист | 2020\nСара | Фаулз | дизайнер | 2019\nСара | Фаулз | программист | 2020\n\n\n#### Отношение `(R TIMES S) WHERE (Дата = 2019)`\nИмя | Фамилия | Профессия | Дата\n:--: | :--: | :--: | :--:\nАся | Тургенева | дизайнер | 2019\nРози | Фицджеральд | дизайнер | 2019\nСара | Фаулз | дизайнер | 2019\n\n### Деление отношений\n\n \nПусть *имеются отношение* `R` с *заголовком* `{ a1, a2, ..., an, b1, b2, ... bm }` и *телом*, *содержащим* множество *кортежей* вида `(x1, x2, ... xn, y1, y2, ... ym)`, и *отношение* `S` с *заголовком* `{ b1, b2, ... bm }` и *телом*, содержащим множество *кортежей* вида `(y1, y2, ... ym)`. \n\n**Делением отношения `R` на отношение `S`** называют *бинарную операцию* над отношениями `R` и `S`, *результатом* которой является *отношение* `T` с *заголовком* `{ a1, a2, …, an }` и *телом*, *содержащим* множество таких *кортежей* `(x1, x2, …, xn)`, что *для каждого кортежа* `(y1, y2, …, ym)` отношения `S` в `R` *существует кортеж* `(x1, x2, …, xn, y1, y2, …, ym)`. В этом случае *отношение* `R` *называют* **делимым**, *отношение* `S` - **делителем**, *отношение* `T` - **частным**.\n\n<!--\nДругими словами, в *результат деления* `R` на `S` *попадают такие* значения `x1, x2, ... xn` атрибутов `{ a1, a2, ..., an }` из отношения `R`, для которых *соответствующие кортежи* `(x1, x2, ..., xn, y1, y2, ... ym)` отношения `R` *включают все без исключения значения* `y1, y2, ... ym` *атрибутов* `{ b1, b2, ... bm }` из отношения `S`.\n-->\n\n\nДругими словами, в *результат деления отношения* `R` *на отношение* `S` *попадают такие* кортежи `x = (x1, x2, ... xn)` *делимого* `R`, c которыми *каждый кортеж* `y = (y1, y2, …, ym)` *делителя* `S` (то есть *все кортежи* отношения `S` *без исключения*) *состоит в упорядоченной паре* `(x, y)`, или, что *то же самое*, в кортеже `(x1, x2, ..., xn, y1, y2, ..., ym)`, *принадлежащим отношению `R`*.\n\n\n*Обозначение деления отношения* `R` *на* `S`: `R DIVIDEBY S`.\n\n\nЧем *деление отношений похоже* на *деление чисел*? При *делении чисел* `x ÷ y` мы считаем, *сколько раз делитель* `y` *целиком поместится* в *делимое* `x`. Пример: `7 ÷ 3 = 3 + 3 + 1 = 2 полных раза`. При *делении отношений* в *результат* попадает *каждый кортеж* `x`, который *связан упорядоченными парами* со *всеми без исключения* кортежами `y`, *заданными* в *отношении-делителе*.\n\n*Деление отношений проще понять на примере*. *Ниже* представлен *пример деления* отношения `R` *на* отношение `S`.\n\n\n#### Отношение `R`\nИмя | Работа | Разговорный язык\n:--: | :--: | :--:\nАся | дизайнер | русский\nАся | дизайнер | немецкий\nСара | программист | английский\nСара | программист | французский\nДина | программист | русский\nДина | программист | итальянский\nДина | программист | английский\nРози | дизайнер | русский\nРози | дизайнер | английский\n\n#### Отношение `S`\nРазговорный язык\n:--:\nрусский\nанглийский\n\n*Кто из сотрудников компании* в отношении `R` *знает все перечисленные* в отношении `S` *языки*?\n\n#### Отношение `R DIVIDEBY S`\nИмя | Работа\n:--: | :--:\nСара | программист\nРози | дизайнер\n\n*Сара* и *Рози знают все перечисленные языки*.\n\nКортежи `x = (x1, x2) = (Сара, программист)` и `x = (x1, x2) = (Рози, дизайнер)` *попали в результат*, поскольку *они состоят* в *упорядоченных парах* с *каждым из кортежей* `y = (y1) = (русский)`, `y = (y1) = (английский)` отношения `S`.\n\nВ *отличие* от *остальных операций*, *предложенных Эдгаром Коддом*, *деление отношений* *не обрело широкой популярности* и *используется крайне редко*. *Такой же результат* можно *получить комбинацией* операций *выборки и проекции* - такой подход будет *гораздо гибче*.\n\n\n### Переименование атрибутов отношения\n\n**Переименование атрибутов отношения `R`** - *унарная операция* над отношением `R`, *результатом* которой является отношение с телом отношения `R` и изменёнными названиями указанных атрибутов в заголовке.\n\nРезультатом применения операции переименования атрибутов является отношение с изменёнными именами атрибутов.\n\n*Обозначение переименования атрибутов* `{ a1, a2, ..., am }` отношения `R` *на новые имена* `{ b1, b2, ..., bm }`: `R RENAME a1, a2, …, an AS b1, b2, …, bn`.\n\n#### Отношение `R`\nИмя | Профессия | Дата\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\nРози | дизайнер | 2020\n\n#### Отношение `R RENAME Профессия, Дата AS Специальность, Год`\nИмя | Специальность | Год\n:--: | :--: | :--:\nАся | дизайнер | 2019\nСара | программист | 2020\nРози | дизайнер | 2020\n\n## Разновидности операций соединения\n- [Тэта-соединение](#тэта-соединение)\n- [Экви-соединение](#экви-соединение)\n- [Естественное соединение](#естественное-соединение)\n\n\nВ *зависимости* от вида *условия* `c` различают *несколько разновидностей соединений*.\n\n\n### Тэта-соединение\n\nПусть `a` - *один из атрибутов* отношения `R`, `b` - *один из атрибутов* отношения `S`. \n\n**Тэта-соединением** (*Θ-соединением*) **отношения `R` по атрибуту `a` с отношением `S` по атрибуту `b`** называют *соединение отношений* `R` и `S` с *условием* `c` вида `a Θ b`, где *символ* `Θ` является *одним из следующих символов сравнения* `{ <, >, ≤ (<=), ≥ (>=), =, ≠ (!=) }`.\n\n*Обозначение Θ-соединения отношения `R` по атрибуту `a` с отношением `S` по атрибуту `b`*: `(R TIMES S) WHERE a Θ b`, или более *кратко* `R(a Θ b)S`.\n\nРассмотрим *Θ-соединение* на примере *продажи* и *покупки* некоторого *предмета*, имеющего в качестве *характеристик* *цвет* и *качество материала*. Пусть имеются *отношение* `R`, *содержащее запросы* на *покупку предмета*, и *отношение* `S`, *содержащее* *выставленные* на *продажу предметы*.\n\n#### Отношение `R` (запросы на покупку предмета)\nПокупатель | Желаемая цена\n:--: | :--: \nmagic | 240\nwoodruf | 180\n\n\n#### Отношение `S` (выставленные на продажу предметы)\nПродавец | Предлагаемая цена | Качество материала | Цвет\n:--: | :--: | :--: | :--:\nevergreen | 230 | 0.7 | зелёный\nfunnyrabbit | 180 | 0.5 | бирюзовый\nredhot | 300 | 1.0 | красный\n\nНайдём *все соответствия* между *запросами на покупку предметов* и *выставленными на продажу предметами* при *условии*, что *желаемая цена покупателя выше (или равна) установленной цены продавца*.\n\n#### Отношение `(R TIMES S) WHERE Желаемая цена >= Предлагаемая цена`\nПокупатель | Желаемая цена | Продавец | Предлагаемая цена | Качество материала | Цвет\n:--: | :--: | :--: | :--: | :--: | :--:\nmagic | 240 | evergreen | 230 | 0.7 | зелёный\nmagic | 240 | funnyrabbit | 170 | 0.5 | бирюзовый\nwoodruf | 180 | evergreen | 230 | 0.7 | зелёный\n\nИтак, пользователь `magic` по *своему запросу* может *купить предмет* у *пользователя* `evergreen` или у `funnyrabbit`, а *пользователь* `woodruf` - *только* у `evergreen`. *Пользователь* `redhot` *остался без потенциальных покупателей*, поскольку *установил слишком высокую цену*.\n\n\n### Экви-соединение\n\n**Экви-соединением отношения `R` по атрибуту `a` с отношением `S` по атрибуту `b`** называют *тэта-соединение*, при котором *символ* `Θ` является *символом равенства* `=`.\n\n\n*Обозначение экви-соединения отношения `R` по атрибуту `a` с отношением `S` по атрибуту `b`*: `(R TIMES S) WHERE a = b`, или более *кратко* `R(a = b)S`.\n\nРассмотрим *экви-соединение* на *примере отношений* `R` и `S`, имеющих *связь* `1:1` и *представленных таблицами ниже*.\n\n#### Отношение `R` (современные короли и королевы)\nID | Имя | KINGDOM_ID\n:--: | :--: | :--:\nval | Виллем-Александр | NL\nel2 | Елизавета II | GB\nk16 | Карл XVI Густав | SE\nma2 | Маргрета II | DK\nfil | Филипп | BE\nfi6 | Филипп VI | ES\nha5 | Харальд V | NO\n\n#### Отношение `S` (страны)\nID | Название | KING_ID\n:--: | :--: | :--:\nBE | Бельгия | fil\nGB | Великобритания | el2\nDK | Дания | ma2\nES | Испания | fi6\nNL | Нидерланды | val\nNO | Норвегия | ha5\nSE | Швеция | k16\n\nОперацией *экви-соединения* получим *новое отношение*, *содержащее* и *страны*, и их *королей одновременно*.\n\nБлагодаря тому, что *между* `R` и `S` установлена *связь* `1:1` (*взаимооднозначное соответствие*), можем использовать *одно из двух условий* `c` (*результат* будет *тот же*):\n1) `R.KINGDOM_ID = S.ID`.\n2) `S.KING_ID = R.ID`.\n\n\n\n#### Отношение `R(ID = KING_ID)S = R(KINGDOM_ID = ID)S`\nR.ID | Имя | KINGDOM_ID | S.ID | Название | KING_ID\n:--: | :--: | :--: | :--: | :--: | :--:\nval | Виллем-Александр | NL | NL | Нидерланды | val\nel2 | Елизавета II | GB | GB | Великобритания | el2\nk16 | Карл XVI Густав | SE | SE | Швеция | k16\nma2 | Маргрета II | DK | DK | Дания | ma2\nfil | Филипп | BE | BE | Бельгия | fil\nfi6 | Филипп VI | ES | ES | Испания | fi6\nha5 | Харальд V | NO | NO | Норвегия | ha5\n\nПонятно, что *получившееся отношение* имеет *избыточные атрибуты*. После *применения операций проекции* и *переименования атрибутов* получим *новое отношение* `P`:\n\n#### Отношение `P`\nID | Имя правителя | Название страны\n:--: | :--: | :--:\nNL | Виллем-Александр | Нидерланды\nGB | Елизавета II | Великобритания\nSE | Карл XVI Густав | Швеция\nDK | Маргрета II | Дания\nBE | Филипп | Бельгия\nES | Филипп VI | Испания\nNO | Харальд V | Норвегия\n\n\n*Атрибуты* в *экви-соединении не обязательно* должны быть *ключами*, но в этом виде соединения *ключи* в качестве атрибутов *используются чаще всего*.\n\n### Естественное соединение\n\n*Естественное соединение* является *особым видом соединения*, при котором *наличие одноимённых атрибутов между отношениями* не только *разрешено*, но и является *принципиально важным*.\n\nПусть *имеются отношение* `R` с *заголовком* `{ a1, a2, ..., an, b1, b2, ... bk }` и *телом*, *содержащим кортежи* вида `(x1, x2, ..., xn, y1, y2, ..., yk)`, и *отношение* `S` с *заголовком* `{ b1, b2, ..., bk, c1, c2, ..., cm)` и *телом*, *содержащим кортежи* вида `(y1, y2, ..., yk, z1, z2, ..., zm)`. *Атрибуты* `b1, b2, ..., bk` отношений `R` и `S` *совпадают* (то есть *совпадают* их *имена и типы*).\n\nТогда **естественным соединением отношений `R` и `S`** называют *соединение* `T`, имеющее *заголовок* `{ a1, a2, ..., an, b1, b2, ... bk, c1, c2, ..., cm }`, и *тело*, *содержащее кортежи* вида `(x1, x2, ..., xn, y1, y2, ..., yk, z1, z2, ..., zm)`. \n\nТаким образом, при *естественном соединении* *кортежи* вида `(x1, x2, ..., xn, y1, y2, ..., yk)` и `(y1, y2, ..., yk, z1, z2, ..., zm)` *объединяются* в *один кортеж* при *совпадении* между ними *значений* `y1, y2, ..., yk`.\n\n*Обозначение естественного соединения отношений* `R` и `S`: `R JOIN S`.\n\n#### Отношение `G` (Товары)\nНазвание | Количество | Номер товара\n:--: | :--: | :--:\nСтол | 16 | 501\nСтул | 64 | 502\nШкаф | 8 | 503\n\n#### Отношение `P` (Цена)\nЦена | Номер товара\n:--: | :--:\n1000 | 502\n2000 | 501\n5000 | 503\n\n*Общим атрибутом отношений* `G` и `P` является только `Номер товара`.\n\n#### Отношение `G JOIN P`\n\nНазвание | Количество | Цена | Номер товара\n:--: | :--: | :--: | :--:\nСтол | 16 | 2000 | 501\nСтул | 64 | 1000 | 502\nШкаф | 8 | 5000 | 503\n\n## Свойства операций над отношениями\n\n*Рекомендуется* сперва прочесть: «[Свойства бинарных операций (теория множеств)](./DiscreteMath.md#свойства-бинарных-операций)».\n\n*Обозначим символом* `◇` *произвольную реляционную операцию*, тогда при изучении свойств *обозначение* `◇ ∈ { INTERSECT, UNION }` будет *означать*, что *свойство выполняется для операций пересечения и объединения* .\n\n\n1) **Идемпотентность унарных операций**: `◇ R = ◇ (◇ R), ◇ ∈ { PROJECT, WHERE }`. То есть `R[a] = (R[a])[a]` и `R WHERE c = (R WHERE c) WHERE c`. \n2) **Идемпотентность бинарных операций**: `R ◇ R = R , ◇ ∈ { UNION, INTERSECT }`.  \n3) **Коммутативность**: `R ◇ S = S ◇ R , ◇ ∈ { TIMES, JOIN, UNION, INTERSECT }`. То есть `R TIMES S = S TIMES R`.  \n4) **Ассоциативность**: `(R ◇ S) ◇ T = R ◇ (S ◇ T) , ◇ ∈ { TIMES, JOIN, UNION, INTERSECT }`.  \n5) **Дистрибутивность**  \n - *выборки относительно пересечения, объединения, разности и декартова произведения*: `(R ◇ S)[a] = R[a] ◇ S[a], ◇ ∈ { INTERSECT, UNION, MINUS, TIMES }`.\n - *проекции относительно объединения декартова произведения*: `(R ◇ S) WHERE c = (R WHERE c) ◇ (S WHERE c), ◇ ∈ { UNION, TIMES }`.  \n6) *Выборку* `R WHERE c` и *проекцию* `R[a]` можно *менять местами* при *условии*, что `c` *зависит только от выбранных атрибутов* `a`: `(R WHERE c)[a] = R[a] WHERE c`.\n\n\nВажно отметить, что *декартово произведение отношени коммутативно* и *ассоциативно в отличии* от *декартова произведения множеств*. Это *связано* с тем, что *кортежи отношения не должны быть упорядоченными*.\n\nОперация *переименования атрибутов* в общем случае *не идемпотентна*.\n\n## Функциональная зависимость\n\n**Функциональной зависимостью** (англ. `functional dependency`) называют *бинарное отношение* между *двумя подмножествами атрибутов* некоторого *отношения* `R`.\n\nФактически, *функциональная зависимость* - это *бинарное отношение между двумя ключами* (англ. `constraint between two keys`) в некотором *отношении* `R`. Обычно *функциональная зависимость отражает связь* \"один-ко многим\" (`1:M`).\n\nПусть у нас имеются *два подмножества атрибутов* `A` и `B` *отношения* `R`. Говорят, что **множество `B` функционально зависит**  (англ. `A functionally determine B`) от **множества `A`** тогда и только тогда, *когда каждое значение* множества `A` *связано* в *точности* с *одним значением* множества `B`. \n\nГоворя *другими словами*, если *два кортежа отношения* `R` *совпадают* по *значением* в *подмножестве атрибутов* `A`, то эти *кортежи* также *должны совпадать* по *значениям* и в *подмножестве атрибутов* `B`.\n\n*Функциональную зависимость* `B` от `A` *обозначают*: **`A → B`**, при этом *подмножество атрибутов* `A` называют **детерминантом** (англ. `determinant set`), а *подмножество атрибутов* `B` называют **зависимой частью** (англ. `dependent set`).\n\n*Функциональную зависимость* называют **тривиальной** (англ. `trivial`), *если* `B ⊆ A`, то есть *если зависимая часть* является *подмножеством детерминанта*.\n\n#### Отношение `R` (Работник)\nID | Дата поступления | Название проекта\n:--: | :--: | :--:\nID1001 | 2019 | Jest\nID1001 | 2020 | Graphql Apollo\nID7017 | 2020 | Graphql Apollo\nID7017 | 2021 | Koa\n\n\n\n\n## Нормализация и нормальные формы\n- [О нормальной форме и нормализации](#о-нормальной-форме-и-нормализации)\n- [О денормализации](#о-денормализации)\n- [Первая нормальная форма](#первая-нормальная-форма)\n\n\n\n\n### О нормальной форме и нормализации\n\n**Нормальной формой** (англ. `normal form`) называют некоторое *требование* или *совокупность требований*, которым *должно удовлетворять отношение* для *устранения избыточности*.\n\nЕсли эти *требования выполняются* для *каждого отношения* некоторой *базы данных*, то её называют **нормализованной базой данных** (англ. `normalized database`). Сам *процесс приведения базы данных* к *нормализованному виду* называется **нормализацией** этой **базы данных** (англ. `database normalization`).\n\n*Нормализация позволяет избежать избыточности данных*, то есть *исключить дублирование данных*. *Дублирование данных* в свою очередь может стать причиной ошибок при изменении данных.\n\n*Понятие нормализации ввёл Эдгад Кодд* как *часть* своей *реляционной модели*.\n\n### О денормализации\n\n**Денормализацией базы данных** (англ. `database denormalization`) называют *процесс*, *обратный нормализации*. В этом случае в *базу данных умышленно добавляют избыточные данные* (*дубликаты данных*) для *ускорения получения данных*.\n\nКогда *отношений слишком много* и они *тесно связаны* друг с другом, *группировка* (*операция соединения*) *данных* может *занимать продолжительное время* (скажем, сделать *соединение* `INNER JOIN` *десяти таблиц*), что *плохо сказывается* на *производительности системы*, а значит и на *пользовательском опыте*. \n\nПо этой причине данные иногда умышленно хранят в денормализованном виде, когда одна одношение представляет собой композицию нескольких отношений.\n\nДенормализованный вид усложняет операции создания, обновления и удаления данных (ведь нужно изменять не только сами данные, но и все их дубликаты), а также увеличивает размер базы данных. Зато существенно снижается время отклика системы при чтении данных.\n\n*Ниже* представлен *пример денормализации* в *табличном виде*.\n\n#### Публикация (дублирует только те поля пользователя, которые нужны для отображения на странице)\nID | Заголовок | Текст | Автор\n:--: | :--: | :--: | :--:\n`N1` | Нормализация | Нормализацией базы данных называют.... | ID: `U1` <br /> Никнейм: `@max.starling` <br /> Почта: `max.starling@email`\n`N2` | Денормализация | Денормализацией базы данных называют.... | ID: `U1` <br />   Никнейм: `@max.starling` <br />   Почта: `max.starling@email`\n`N3` | Реляционная модель данных | Реляционной моделью данных называют.... | ID: `U2` <br />   Никнейм: `@ray.gray` <br />   Почта: `ray.gray@email`\n\n#### Пользователь (хранит идентицикаторы публикаций, чтобы публикации было проще найти на странице пользователя)\nID | Никнейм | Почта | Возраст | Публикации\n:--: | :--: | :--: | :--: | :--:\n`U1` | @max.starling | max.starling@mail | 23 | [`N1`, `N2`]\n`U2` | @ray.gray | ray.gray@mail | 27 | [`N3`]\n\n*Ниже* представлен *пример денормализации* в *документном виде*.\n\n#### Публикации в формате JSON\n```js\n[{\n  \"_id\": \"N1\",\n  \"title\": \"Нормализация\",\n  \"text\": \"Нормализацией базы данных называют....\",\n  \"user\": {\n    \"_id\": \"U1\",\n    \"username\": \"@max.starling\",\n    \"email\": \"max.starling@email\"\n  }\n},\n{\n  \"_id\": \"N2\",\n  \"title\": \"Денормализация\",\n  \"text\": \"Денормализацией базы данных называют....\",\n  \"user\": {\n    \"_id\": \"U1\",\n    \"username\": \"@max.starling\",\n    \"email\": \"max.starling@email\"\n  }\n},\n{\n  \"_id\": \"N3\",\n  \"title\": \"Реляционная модель данных\",\n  \"text\": \"Реляционной моделью данных называют....\",\n  \"user\": {\n    \"_id\": \"U2\",\n    \"username\": \"@ray.gray\",\n    \"email\": \"ray.gray@email\"\n  }\n}]\n````\n\n#### Пользователи в формате JSON\n```js\n[{\n  \"_id\": \"U1\",\n  \"username\": \"@max.starling\",\n  \"email\": \"max.starling@email\",\n  \"age\": 23,\n  \"posts: [\"N1\", \"N2\"]\n}, {\n  \"_id\": \"U2\",\n  \"username\": \"@ray.gray\",\n  \"email\": \"ray.gray@email\",\n  \"age\": 27,\n  \"posts: [\"N3\"]\n}]\n````\n\n\n### Первая нормальная форма\n*Отношение находится* в **первой нормальной форме** (англ. `first normal form`), **1НФ** (англ. `1NF`) тогда и только тогда, когда *любой кортеж отношения* может *содержать только одно значение* для *каждого* из *атрибутов*.\n\nТаким образом, *значение атрибута не может быть представлять собой набор значений* (массив, множество, таблицу и так далее), если *отношение находится* в *1НФ*.\n\nВ *реляционной модели данных* *отношение всегда находится* в *1НФ* по своему *определению*. \n\nТем не менее, некоторые *таблицы*, которыми можно *представить отношение*, могут *не удовлетворять данному требованию*, а значит и *реляционной модели*.\n\nНапример, `SQL` *не поддерживает использование столбцов* с *табличными значениями* (англ. `table-valued columns`).\n\nПример *ненормализованной таблицы*:\nСтрана | Город\n:--: | :--:\nРоссия | Москва, Санкт-Петербург\nУкраина | Киев, Львов\nБеларусь | Минск\n\nПример *нормализованной таблицы*:\nСтрана | Город\n:--: | :--:\nРоссия | Москва\nРоссия | Санкт-Петербург\nУкраина | Киев\nУкраина | Львов\nБеларусь | Минск\n\n*Базы данных*, которые *нарушают первую нормальную форму*, обычно называют `NoSQL` *базами данных*. Примером такой базы данных является документная база данных `MongoDB`, которая *позволяет хранить любые JSON-документы* (значения могут быть массивами или объектами).\n\n<!--\n\n# Резидентные базы данных\n\n**Резидентная база данных**, **база данных в памяти** (in-memory database, IMDB) — база данных, размещаемая в оперативной памяти.\n\nРезидентные базы данных могут использовать и дисковую память, но в первую очередь используют оперативную память. Это главное отличае с традиционными СУБД (например, MySQL) и современными базами данных (NoSQL), которые в первую очередь используют диск.\n\n### Преимущества и недостатки резидентной базы данных\n\nПреимуществом резидентной базы данных является быстродействие. Работа с диском является достаточно трудоёмкой операцией, поэтому традиционные базы данных считаются самым узким местом в приложении, в то время как работа с оперативной памятью является очень быстрой.\n\nНедостатком резидентной базы данных является то, что оперативная память не надёжна, поскольку при отключении сервера или при его перегрузке она очищается. Чтобы этого избежать, необходимо перед отключением кэшировать данные на диск.\n\n## О Redis\n-->\n\n<!--\n**DAO** (Data Access Object) — объект, представляющий абстрактный интерфейс к какой-то базе данных или другому механизму хранения (persistence mechanism). Он сопоставляет вызовы приложения (app calls) с уровнем хранения (persistance layer), предоставляя некоторые методы работы с данными, не раскрывая детали базы данных. Пример инструмента с DAO: ORM.\n**DTO** (Data Transfer Object) — объект, передающий данные между подсистемами приложения. В отличие от DAO, DTO не может содержать поведение (кроме хранения, поиска, сериализации и десериализации своих собственных данных). DTO используют, чтобы объединить в них данные нескольких запросов в один запрос (поскольку подсистемы могут общаться запросами, а каждый запрос - дорогостоящая операция).\n**CRUD** (Create, Read, Update, Delete) — базовый набор операций постоянного хранилища (persistent storage): создание, чтение, обновление, удаление.\n-->\n\n# Оптимизация баз данных\n\n# Паттерны баз данных\n\n# Движки баз данных\n\n\n# Проблема n + 1\n\nN+1 проблема возникает, когда для получения связанных данных делается *1 запрос на основную сущность* и `n` *отдельных запросов на связанные сущности*.\nРассмотрим эту проблему на примере GraphQL и Prisma.\n\nНапример, у нас есть `users` и `posts` в отношении `1:M` (один-ко-многим), мы хотим вытянуть данные о пользователях и их постах.\nПри нативной реализации такого запроса на SQL мы сначала возьмём возьмём пользователей\n```sql\nSELECT * FROM users\n```\nа затем для каждого получаем посты по айди пользователя (`n` дополнительных запросов):\n```sql\nSELECT * FROM posts WHERE user_id = 1\nSELECT * FROM posts WHERE user_id = 2\n...\nSELECT * FROM posts WHERE user_i = n\n```\nОтсюда и `n + 1` запросов, что неэффективно.\n\nПроблема решается объединением запросов при помощи `JOIN`. Например, на чистом SQL решение было бы следующим:\n```sql\nSELECT * FROM users u LEFT JOIN posts p WHERE p.user_id = u.id\n```\nИ того у нас лишь один запрос к базе вместо `n + 1`.\n\n## Проблема n + 1 в Prisma\nДопустим, у нас есть следующая схема Prisma.\n```prisma\nmodel User {\n  id    Int    @id @default(autoincrement())\n  name  String\n  posts Post[]\n}\n\nmodel Post {\n  id     Int    @id @default(autoincrement())\n  title  String\n  user   User   @relation(fields: [userId], references: [id])\n  userId Int\n}\n```\nТогда проблема n + 1 выглядела бы так на языке JavaScript\n```ts\nconst users = await prisma.user.findMany(); // 1 запрос, чтобы получить всех пользователей\n\nconst usersWithPosts = await Promise.all(\n  users.map(async (user) => {\n    const posts = await prisma.post.findMany({ where: { userId: user.id } }); // n запросов, по одному для каждого пользователя\n    return { ...user, posts };\n  })\n);\n```\nРешение проблемы - **жадная загрузка** (англ. `eager loading`), подробнее о ней будет ещё далее.\n* через `include`\n```ts\nconst usersWithPosts = await prisma.user.findMany({\n  include: {\n    posts: true, // загружаем связанные посты сразу\n  },\n});\n```\n* через `select`\n```ts\nconst usersWithPosts = await prisma.user.findMany({\n  select: {\n    id: true,\n    name: true,\n    posts: {\n      select: {\n        id: true,\n        title: true,\n      },\n    },\n  },\n});\n```\n\n## Проблема n + 1 в GraphQL\nПример проблемы n + 1 запроса в GraphQL\n```graphql\nquery {\n  users {\n    id\n    name\n    posts {\n      id\n      title\n    }\n  }\n}\n```\n```js\nconst resolvers = {\n  Query: {\n    users: () => prisma.user.findMany(), // 1 запрос\n  },\n  User: {\n    posts: (parent) => prisma.post.findMany({ where: { userId: parent.id } }), // дополнительные n запросов\n  },\n};\n```\nРешение\n* через `include` в корневом запросе (если ORM предоставляет такую возможность)\n```js\nconst resolvers = {\n  Query: {\n    users: () => prisma.user.findMany({ include: { posts: true } }), // ✔️ Один запрос\n  },\n};\n```\nтогда GraphQL схема будет\n```graphql\ntype User {\n  id: ID!\n  name: String!\n  posts: [Post!]!\n}\n```\n* через `DataLoader`\n```js\nconst postLoader = new DataLoader(async (userIds) => {\n  const posts = await prisma.post.findMany({\n    where: { userId: { in: userIds } },\n  });\n\n  // Группируем посты по userId\n  return userIds.map(userId => posts.filter(post => post.userId === userId));\n});\n```\n```js\nconst resolvers = {\n  User: {\n    posts: (parent) => postLoader.load(parent.id), // одно обращение к DataLoader вместо n запросов\n  },\n};\n```\n\n\n## Проблема n + 1 в MongoDB (NoSQL)\nОставляем задачу с пользователями и постами, имеем 1 запрос для пользователей `n` запросов постов для каждого из `n` пользователей.\n```js\nconst users = await db.collection('users').find().toArray(); // 1 запрос\n\nconst usersWithPosts = await Promise.all(\n  users.map(async (user) => {\n    const posts = await db.collection('posts').find({ user_id: user._id }).toArray(); // n запросов\n    return { ...user, posts };\n  })\n);\n```\nПочему плохо:\n* большая нагрузка на сервер (много запросов)\n* медленное выполнение\n* бОльшие траты из-за бОльшего количества облачных вычислений\n\nРешение:\n* Денормализация. Хранить вложенные объекты (объекты постов вложены в объекты пользователей), не вынося их в разные коллекции.\n```js\n{\n  \"_id\": \"123\",\n  \"name\": \"Alice\",\n  \"posts\": [\n    { \"title\": \"Post 1\" },\n    { \"title\": \"Post 2\" }\n  ]\n}\n```\n* Агрегации и `$lookup` (Аналог `JOIN` в MongoDB)\n```js\nconst usersWithPosts = await db.collection('users').aggregate([\n  {\n    $lookup: {\n      from: \"posts\",\n      localField: \"_id\",\n      foreignField: \"user_id\",\n      as: \"posts\"\n    }\n  }\n]).toArray();\n```\n* Запрос `$in`. Передаём список айдишек пользователей и имеем `1 + 1` запрос вместо `n + 1`:\n```\nconst users = await db.collection('users').find().toArray();\nconst userIds = users.map(u => u._id);\nconst posts = await db.collection('posts').find({ user_id: { $in: userIds } }).toArray();\n```\n\n## Проблема n + 1 в elasticsearch\nОпять же, пользователи и посты.\n\nПолучаем пользователей.\n```http\nGET /users/_search\n{\n  \"query\": {\n    \"match_all\": {}\n  }\n}\n```\nПолучаем посты для каждого из пользователей, `n` пользователей = `n` дополнительных запросов\n```http\nGET /posts/_search\n{\n  \"query\": {\n    \"term\": { \"user_id\": \"1\" }\n  }\n}\n```\n...\n```http\nGET /posts/_search\n{\n  \"query\": {\n    \"term\": { \"user_id\": \"n\" }\n  }\n}\n```\nРешения:\n* **Денормализация**. Поскльку Elasticsearch не реляционная база, то лучший способ оптимизировать запросы — избегать JOIN-подобных операций, вкладывать нужные данные сразу в документ (хранить вложенные (англ. `nested`) объекты).\n* Вложенные документы (англ. `Nested fields`).Если данные часто меняются и денормализация невозможна, то можно использовать тип `nested`. Тогда можно искать и фильтровать вложенные объекты без отдельного запроса.\n```js\n{\n  \"mappings\": {\n    \"properties\": {\n      \"posts\": {\n        \"type\": \"nested\"\n      }\n    }\n  }\n}\n```\n* Multi-search, batch-запросы. Если предыдущие опции не подходят, батчим запросы и отправляем их вместе\n```http\nPOST /_msearch\n{ }\n{ \"query\": { \"term\": { \"user_id\": \"123\" } } }\n{ }\n{ \"query\": { \"term\": { \"user_id\": \"124\" } } }\n...\n```\n* Запрос `terms` позволяет передать списком id пользователей. Таким образом вместо n + 1 запросов имеем 1 + 1 запрос (один на пользователей, один на посты):\n```\nGET /posts/_search\n{\n  \"query\": {\n    \"terms\": {\n      \"user_id\": [ \"123\", \"124\", \"125\", \"126\" ] // список всех id пользователей\n    }\n  }\n}\n```\n## Mongoose, проблема `n + 1` и `populate`\n\nПроблема `n + 1` запросов в Mongoose часто возникает при использовании `populate()`, особенно в ситуациях, когда у нас есть список сущностей, и для каждой из них мы подгружаем связанные данные. Это может приводить к выполнению большого количества отдельных запросов к базе, что существенно замедляет приложение.\n```js\nconst UserSchema = new mongoose.Schema({\n  name: String,\n});\n\nconst PostSchema = new mongoose.Schema({\n  title: String,\n  author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },\n});\n```\n### Проблемы `n + 1` в Mongoose\n* Проблема `n + 1`:\n```js\nconst posts = await Post.find();\nfor (const post of posts) {\n  post.author = await User.findById(post.author);\n}\n```\n* Проблема `n + 1` с `populate` в цикле:\n```js\nconst posts = await Post.find();\nfor (const post of posts) {\n  await post.populate('author');\n}\n```\n### Решение проблемы \n* Многоуровневый `populate`:\n```js\nconst posts = await Post.find().populate({\n  path: 'author',\n  populate: { path: 'profile' }\n});\n```\n* `populate` на уровне поста (без цикла):\n```js\nconst posts = await Post.find().populate('author').lean();\n```\n* Использование `$in`:\n```js\nconst posts = await Post.find();\nconst authorIds = posts.map(post => post.author);\nconst authors = await User.find({ _id: { $in: authorIds } }).lean();\nconst authorsMap = new Map();\nauthors.forEach(author => {\n  authorsMap.set(author._id.toString(), author);\n});\nconst postsWithAuthors = posts.map(post => ({\n  ...post.toObject(),\n  author: authorsMap.get(post.author.toString()) || null\n}));\n```\n\n## Сравнение жадной загрузки и `populate`\n\n**Жадная загрузка** (англ. `eager loading`) — это общий подход для загрузки связанных данных сразу, а `populate` — конкретный механизм в Mongoose/MongoDB для подстановки связанных данных, который может вести себя как “ленивая” или “жадная” загрузка в зависимости от реализации.\n* Eager loading — архитектурный подход загрузки данных сразу (в SQL, Prisma и др.).\n```js\nconst users = await prisma.user.findMany({\n  include: {\n    posts: true, // жадная загрузка постов\n  },\n});\n```\n* Populate — специфическая реализация этого подхода в MongoDB (но часто через дополнительные запросы, если не “optimized populate”).\n```js\nconst users = await User.find().populate('posts');\n```\n\n# Оптимизация SQL-запросов\n\n## Анализ производительности запросов\n\nКоманда `EXPLAIN` — основной инструмент анализа производительности запросов.\n```sql\nEXPLAIN SELECT * FROM users WHERE email = 'test@example.com';\n```\nОна позволяет смотреть\n* типы доступа к данным (Index Scan, Full table scan и тд)\n* используемые индексы и их эффективность\n* количество просматриваемых строк\n\n## Используйте индексы правильно\n**Индексы** – *важнейший инструмент ускорения поиска запроса*\nСоздавайте индекс для полей, которые используются в\n* `JOIN`\n* `WHERE`\n* `ORDER BY`\n* `GROUP BY`\n\nУчитывайте **селективность** (индексируй поля с высокой уникальностью).\n\nПример создания индекса\n```sql\nCREATE INDEX idx_users_email ON users(email);\n```\n\n## Выбирайте только нужные поля таблицы и не используй `SELECT *`\n```sql\n# плохо\nSELECT *\n# хорошо\nSELECT id, username FROM users; # хорошо\n```\n\n## Избегайте лишних JOIN и подзапросов\nИспользуйте `JOIN` вместо подзапросов, если это возможно\n```sql\n# плохо - подзапрос\nSELECT id, (SELECT id FROM orders WHERE orders.user_id = users.id) FROM users;\n# хорошо - JOIN\nSELECT users.id, orders.id FROM users JOIN orders ON orders.user_id = users.id;\n```\n\n## Используйте пагинацию, когда это возможно\nЗадайте `LIMIT` и `OFFSET`\n```sql\nSELECT id, username FROM users LIMIT 50 OFFSET 0;\n```\n\n## Грамотно используйте условия WHERE и агрегацию\n\n## Регулярно чистите базу, выполняйте оптимизацию таблиц и перестройку индексов\n```sql\nOPTIMIZE TABLE your_table\n```\n## Следите за производительностью запросов\nНапример, в MySQL можно сделать так\n```sql\nSET GLOBAL slow_query_log = 'ON';\n```\n"
  },
  {
    "path": "DataStructures.md",
    "content": "- [Структуры данных](#структуры-данных)\n\n# Структуры данных\n\n**Структура данных** (англ. data structure) - это *набор значений* некоторых *логически связанных данных*, *отношения между этими данными* и *доступные операции*, которые *можно к ним применить*.\n\n"
  },
  {
    "path": "DataTypes.md",
    "content": "\n# Оглавление\n- [О типе данных](#о-типе-данных)\n- [Примитивные типы данных](#примитивные-типы-данных)\n- [Логический тип](#логический-тип)\n- [Строковый тип](#строковый-тип)\n- [Символьный тип](#символьный-тип)\n- [Целочисленный тип](#целочисленный-тип)\n- [Число с плавающей точкой](#число-с-плавающей-точкой)\n- [Ссылка](#ссылка)\n- [Указатель](#указатель)\n\n\n<!-- дополнительно: -->\n\n# О типе данных\n*Рекомендуется* прочитать: [Множество (дискретная математика)]().\n\n**Типом данных** (англ. data type) или просто **типом** (англ. type) называют *множество* (*допустимых*) *значений* и *совокупность операций* над этими *значениями*.\n\n*Компьютер не видит разницы между числом*, *строкой*, *датой* - для него *всё* это лишь *последовательности нулей и единиц*. \n\n*Тип данных* подсказывает *компьютеру*, как *следует обращаться* с этими *данными*, *предоставляя* (*ограничивая*) *операции* для *работы* с *данными* этого *типа*. Например, *поиск подстроки* для *строковых значений*, *умножение* для *числовых значений*, *логические операции* над *логическими значениями* и так далее.\n\n# Примитивные типы данных\n\n*Большинство языков программирования поддерживает* **3 основных типа данных**:  \n1) **Логический тип**. Содержит *логическое значение*: *истина* или *ложь*. Например, `true` или `false`, `1` или `0`, `да` или `нет`.  \n2) **Строковый тип**. Содержит *текст*. Например, `'По ту сторону изгороди'`, `\"письма незнакомке\"`.  \n3) **Числовой тип**. Содержит *число*. Например, `7`, `-3`, `3.14`.\n\n*Эти типы данных* также *называют* **примитивными** или **базовыми типами**.\n\n# Логический тип\n- [О логическом типе](#о-логическом-типе)\n- [Логические операции](#логические-операции)\n- [Представление логического значения в памяти компьютера](#представление-логического-значения-в-памяти-компьютера)\n\n## О логическом типе\n\nВ *основу логический типа данных* легла *математическая логика*.\n\n**Логическим типом** (англ. Boolean) называют *тип данных*, который *принимает одно* из *двух логических значений*, называемых **истиной** (англ. true) и **ложью** (англ. false). \n\n*Логические значения* должны быть по *смыслу противоположны друг другу*. Например, этими *значениями* могут быть: *да* и *нет*, *включено* и *выключено*, *присутствует* и *отсутствует* и так далее. \n\n*Удобнее* всего *представлять истину* в виде `1` (*логическая единица*), а *ложь* в виде `0` (*логический ноль*).\n\nВ *компьютере значение логического типа* можно *задать одним битом*.\n\n## Логические операции\n- [Унарные логические операции](#унарные-логические-операции)\n- [Бинарные логические операции](#бинарные-логические-операции)\n- [Тернарная условная операция](#тернарная-условная-операция)\n\n*Операции над логическими значениями* называются **логическими операциями** (англ. Boolean algebraic operations).\n\n*Ниже* будут *представлены лишь* те *логические операции*, которые *используются* в *большинстве языков программирования*. С *полным списком логических операций* можно *ознакомиться* в *соответствующем разделе математической логики*.\n\n### Унарные логические операции\n* **Отрицание** (англ. negation). *Обозначение отрицания* `x`: `!x`, `NOT x`. *Свойства отрицания*: `!0 = 1`, `!1 = 0`, `!!x = x` (*закон двойного отрицания*). *Подробнее* об *отрицании* и его *свойствах*.\n\n### Бинарные логические операции\n* **Логическое “И”** (англ. logical “AND”), **конъюнкция** (англ. conjunction). *Обозначение конъюнкции* `x` и `y`: `x && y`, `x AND y`. *Свойства конъюнкции*: `x && x = x`, `x && y = y && x`, `0 && 0 = 0`, `0 && 1 = 0`, `1 && 1 = 1`.  \n* **Логическое “ИЛИ”** (англ. logical “OR”), **дизъюнкция** (англ. disjunction). *Обозначение дизъюнкции* `x` и `y`: `x || y`, `x OR y`. *Свойства дизъюнкции*: `x || x = x`, `x || y = y || x`, `0 || 0 = 0`, `0 || 1 = 1`, `1 || 1 = 1`.  \n* **Эквиваленция** (англ. equivalence). *Обозначение эквиваленции* `x` и `y`: `x == y`, `x EQV y`. *Свойства эквиваленции*: `(x == x) = 1`, `(x == y) = (y == x)`, `(0 == 0) = 1`, `(1 == 1) = 1`, `(0 == 1) = 0`.  \n* **Логическое исключающее “ИЛИ”** (англ. logical exclusive “OR”, logical “XOR”), **отрицание эквиваленции** (англ. non-equivalence). *Обозначение отрицания эквиваленции* `x` и `y`: `x != y`, `x XOR y`, `x NEQV y`. *Свойства отрицания эквиваленции*: `(x != x) = 0`, `(x != y) = (y != x)`, `(0 != 0) = 0`, `(1 != 1) = 0`, `(0 != 1) = 1`.  \n\n*Подробнее* о *конъюнкции*, *дизъюнкции*, *эквиваленции*, *исключающем “ИЛИ”* и их *свойствах* можно прочитать в [*математической логике*]().\n\n<!-- сравнения -->\n\n### Тернарная условная операция\n\n**Тернарной условной операцией** или просто **тернарной операцией** (англ. ternary operation) называют *операцию*, которая *принимает три операнда* (*логическое выражение* `x` и *два значения любого типа* `y`, `z`) и *производит* над ними *следующее*: если *логическое выражение истинно* (`x == 1`), то *возвращается значение* `y`, в *противном случае возвращается значение* `z`. Более *кратко*, “*если* `x`, *то* `y`, *иначе* `z`”.\n\n*Обозначение тернарной операции* с *операндами* `x`, `y`, `z`:  \n* `x ? y : z`,\n* `if x then y else z`.\n\n*Свойства тернарной операции* с *операндами* `x`, `y`, `z`:  \n* Если `x == 1`, то `(x ? y : z) == y`.\n* Если `x == 0`, то `(x ? y : z) == z`.\n\n*Тернарная операция соответствует условной дизъюнкции* в *математической логике*.\n\n## Представление логического значения в памяти компьютера\n\nДля *представления логического значения* в *памяти компьютера достаточно одного бита*, поскольку *один бит может принять любые два возможных состояния*. В таком случае, `1` - это *истина*, `0` - это *ложь*.\n\n*Реализовать* сам *бит* в *компьютере* достаточно *просто*: например, *подача напряжения* по *проводу* - это `1` (*истина*), *отсуствие напряжения* на *проводе* - `0` (*ложь*).\n\n\n\n# Строковый тип\n- [О строковом типе](#о-строковом-типе)\n- [Обозначение строки](#обозначение-строки)\n- [Операции над строками](#операции-над-строками)\n- [Сравнение строк](#сравнение-строк)\n- [Представление строк в памяти компьютера](#представление-строк-в-памяти-компьютера)\n- [Размер строки](#размер-строки)\n\n\n## О строковом типе\n\n**Строковым типом** (англ. String) называют *тип данных*, *каждое значение* которого *представляет* собой *последовательность символов* некоторого *алфавита*. \n\n*Значение строкового типа* называют **строкой** (англ. string). \n\n<!-- *Строка* *представляет* собой *упорядоченный набор элементов с повторениями*. -->\n\n*Количество символов строки* называют **длиной строки** (англ. string length).\n\n## Обозначение строки\n\nВ *программном коде значения строкового типа* (*строки*) обычно *заключаются* в *кавычки*:\n1) **Одинарные**: `'Мартин Иден'`, `'evergreen'`.\n2) **Двойные**: `\"Унесённые ветром\"`, `\"inhale\"`.\n3) **Косые**: \\``Триумфальная арка`\\`, \\``Round Robin`\\`.\n\n\n## Операции над строками\n\n*Над строками* можно *выполнять* следующие *операции*:\n* *Вычисление длины* строки. Например, `(\"Notes\").length --> 5`.\n* *Получение символа* из строки по *индексу*. Например, `(\"Notes\").find(\"t\") --> 2`.\n* *Конкатенация* (*сложение*) *строк*. Например, `\"No\" + \"tes\" --> \"Notes\"`.\n* *[Сравнение](#сравнение-строк)* двух *строк*. Например, `\"No\" == \"tes\" --> false`.\n* *Поиск подстроки* в строке. Например, `(\"Notes\").includes(\"tes\") --> true`.\n* *Замена подстроки* в строке. Например, `(\"Notes\").replace(\"es\", \"ion\") --> \"Notion\"`.\n\nВ *большинстве языков программирования* *данные операции над строками встроены по умолчанию* или же их *можно подключить вместе* с *библиотекой*.\n\n## Сравнение строк\n\nВ *программировании* символы `<`, `<=`, `==`, `>=`, `>` *представляют* собой *операторы* (не *отношения*, как в *теории множеств*), которые *являются частью операций сравнения*. \n\n*Операция сравнения* является *бинарной операцией*, которая *принимает два операнда любого типа* и *возвращает результат логического типа*, называемый *результатом сравнения*.\n\n*Сравнение строк производится* в *лексикографическом порядке* (*посимвольно*), то есть *символы двух строк сравниваются попарно* по их *числовым кодам*. *Коды символов* *задаются кодировкой*. \n\n*Больше* считается *та строка*, чей *код символа оказался больше* на *некотором шаге алгоритма сравнения*.\n\nНапример, `\"music\" > \"museum\"`, поскольку *код символа* `'i'` *больше кода символа* `'e'`. Ниже представлена *таблица* для *наглядности сравнения сравнения*. *Коды символов* взяты из *кодировки ASCII*.\n\nИндекс символа | Символ первой строки | Символ второй строки | Сравнение кодов символов\n:--: | :-: | :--: | :-:\n0 | `'m'` | `'m'` | `109` = `109`\n1 | `'u'` | `'u'` | `117` = `117`\n2 | `'s'` | `'s'` | `115` = `115`\n3 | `'e'` | `'i'` | `101` < `105`\n4 | `'u'` | `'c'` | \n5 | `'m'` |   | \n\nАналогично, `\"музыка\" > \"музей\"` (`'ы' > 'е'`), `”волк < \"тигр\"` (`'в' < 'т'`), `\"близнец\" = \"близнец\"`.\n\n### Алгоритм сравнения строк\n\nПусть *сравниваются две строки* `a` и `b`.\n\nЕсли *хотя бы одна* из *строк* `a` и `b` *пуста*, то *результат сравнения* можно *определить сразу*:\n1) Если *обе строки пусты*, то `a = b`.\n2) Если *одна строка пуста*, то *больше непустая строка*.\n\n*Далее считаем*, что `a` и `b` *не пусты*.\n\nСначала берётся *код первого символа строки* `a`, *код первого символа строки* `b` и *осуществляется их сравнение*:\n  * Если *код символа строки* `a` *больше кода символа строки* `b`, то `a > b`. \n  * Если *код символа строки* `a` *меньше кода символа строки* `b`, то `a < b`.\n  * Если *коды символов совпали*, то *выполнение алгоритма продолжается*.\n  * Если *достигнут конец* хотя бы *одной строки*, то *сравнение заканчивается*:\n    * Если *все предыдущие символы совпали* и *длины строк* `a` и `b` *совпадают*, то `a = b`.\n    * Если *все предыдущие символы совпали* и *длины строк* `a` и `b` *не совпадают*, то *больше та строка*, в *которой больше символов*.\n  * *Берётся код следующего символа* из *каждой строки* и *выполнение алгоритма начинается сначала*.\n\n## Представление строк в памяти компьютера\n\n<!-- перенести в programming -->\n\nСуществует *два способа представить строку* в *памяти компьютера*:  \n* *[Массив символов](#массив-символов)*.\n* *[Нуль-терминированная строка](#нуль-терминированная-строка)*.\n\n### Массив символов\n\n**Массивом символов** называют *способ представления* **строки длины `n`** в *памяти компьютера* как **массива** из **`n + 1` элементов**, в котором *символы строки* занимают *индексы массива от* `1` *до* `n`, а *элемент* с *нулевым индексом хранит длину строки* (*количество символов*) или *размер строки* (*количество байт*).\n\n<!--\nНапример, можно представить слово `Notes` как \\u004e\\u006f\\u0074\\u0065\\u0073\n Символ (код символа)\n-->\n\n*Представление строки* `\"Notes\"` *массивом символов*:\nИндексы элементов массива | 0 (длина строки) | 1 | 2 | 3 | 4 | 5\n:--: | :--: | :--: | :--: | :--: | :--: | :--:\nЭлементы массива | 5 | `'N'` | `'o'` | `'t'` | `'e'` | `'s'`\n\n<!--\nЭлементы массива, строка `\"море\"` | 4 | `'м'` | `'о'` | `'р'` | `'е'` | -\n-->\n\n### Нуль-терминированная строка\n\n**Нуль-терминированной строкой** называют *способ представления строки* в *памяти компьютера*, при котором *строка представляется непрерывной последовательностью байт*, которая *оканчивается специальным* *нуль-символом* (*признаком конца строки*, *завершающим байтом*). \n\n**Нуль-символ** *представляет* собой *любой символ алфавита*, который был *выбран* в *качестве признака конца строки*. Например, в *алфавите ANSI* *нуль-символом выступает символ* `NUL`, имеющий *код* `0`.\n\nЕсли *выделяется один байт на один символ алфавита*, то *нуль-терминированная строка* из **`n` символов** *занимает* **`n + 1` байт**.\n\n*Нуль-терминированные строки* являются *стандартом* в *языке C*, поэтому их *иногда называют* **C-строками**.\n\n## Размер строки\n\n**Размер строки** зависит от *длины* этой *строки* (*количества символов* в ней), от *размера одного символа* (*размер задаётся кодировкой*) и от *способа представления строки* в *памяти компьютера* (например, если *строка* *нуль-терминированная*, то её общий *размер увеличивается на один байт*).\n\nНапример, при *использовании однобайтовой кодировки ASCII* *нуль-терминировання строка* `\"Notes\"` *займёт* `5 + 1 = 6 байт`. Кстати говоря, *столько же* вышло бы при *использовании* *кодировки UTF-8*, поскольку она *обратно совместима* с *кодировкой ASCII*\n\n\nНапример, при *использовании кодировки UTF-8* *нуль-терминированная строка* `\"Роза\"` *займёт* `4 • 2 + 1 = 9 байт`, поскольку *каждый символ кириллицы* в *кодировке UTF-8 представляется двумя байтами*.\n\n*Представление нуль-терминированной строки* `\"Notes\"` *последовательностью байт*, *записанных* в *шестнадцатиричной системе счисления*:\n```\n0x4e 0x6f 0x74 0x65 0x73 0x00\nN    o    t    e    s    NUL\n```\n\n# Символьный тип\n\n**Символьным типом** (англ. Char) называют *тип данных*, *каждое значение* которого *представляет* собой *один* **символ** (англ. character) некоторого *алфавита* (некоторой *кодировки*).\n\nНапример, *значениями символьного типа* являются: `'%'`, `'g'`, `'/'`.\n\n*Значение символьного типа занимает столько памяти*, *сколько* должен занимать *символ соответствующей кодировки* . Например, для *ANSII* это всегда `1 байт`, для *кодировок семейства UTF размер варьируется от* `1` *до* `4 байт`.\n\nНапример, в *языке C нет строкового типа*, поэтому *строки рассматриваются* в нём как *массивы символов* (`char[]` или `char*`).\n\n# Целочисленный тип\n- [О целочисленном типе](#о-целочисленном-типе)\n- [Беззнаковые целые числа и целые числа со знаком](беззнаковые-целые-числа-и-целые-числа-со-знаком)\n- [Представление целого числа в памяти компьютера](#представление-целого-числа-в-памяти-компьютера)\n- [Размер целого, короткое и длинное целые](#размер-целого-короткое-длинное-целое)\n- [Арифметические операции над целыми числами](#арифметические-операции-над-целыми-числами)\n- [Битовые операции над целыми числами](#битовые-операции-над-целыми-числами)\n\n## О целочисленном типе\n\n**Целочисленным типом** (англ. Integer, Integral data type, Int) называют *тип данных*, *каждое значение* которого *представляет* некоторое *число* из *множества целых чисел* `Z`.\n\n*Значение целочисленного типа* называют **целочисленным значением** или **целым числом**.\n\nНапример, *целочисленным значениями* являются: `17`, `-3`, `0`.\n\n## Беззнаковые целые числа и целые числа со знаком\n\nВ *программировании целые числа подразделяют* на *беззнаковые целые числа* и *целые числа со знаком*\n\n**Беззнаковое целое число** (англ. unsigned integer) *представляет* только *неотрицательные целые числа*:  \n`0, 1, 2, 3, ...`.\n\n**Целым числом со знаком** (англ. signed integer) *представляюет любое целое число* (*положительное*, *отрицательное* или *ноль*). Например, `-37`, `8`, `0`.\n\n\n## Представление целого числа в памяти компьютера\n\n*Математическое множество целых чисел* `Z`, вообще говоря, *бесконечно*. При этом *ресурсы компьютера ограничены*. *Компьютер не способен хранить* что-либо *бесконечное*, поэтому *представление множества целых чисел* в *компьютере ограничивается сверху* и *снизу максимальным* и *минимальным значениями*. \n\n*Целое число представляется* в *памяти компьютера* *последовательностью байт* (*бит*).\n\n**Длиной целочисленного значения** (англ. integer width, precision) называют *количество бит* в его *двоичном представлении*.\n\n## Кодирование целых чисел\n- [Кодирование беззнаковых целых чисел](#кодирование-беззнаковых-целых-чисел)\n- [Кодирование целых чисел со знаком](#кодирование-целых-чисел-со-знаком)\n\nДля *кодирования целого числа кодировка не нужна*: *достаточно* применить *алгоритм перевода чисел из десятичной системы счисления* в *двоичную*.\n\n### Кодирование беззнаковых целых чисел\n\nВсего *существует* `m = 2^n` *n-битных двоичных последовательностей*, что *можно* легко *доказать комбинаторно*, используя *размещения с повторениями*. Этими `m` *двоичными последовательностями* можно *представить беззнаковые целые числа* от `0` до `m - 1`. \n\n*Число* `0` называют **минимальным беззнаковым целым числом** (*нижняя граница множества*), а *число* `m - 1 = 2^n - 1` называют **максимальным беззнаковым целым числом** (*верхняя граница множества*).\n\nНапример, *одним байтом* (`8 бит`) можно *представить беззнаковые целые числа от* `0` *до* `2^8 - 1 = 255`. \n\nВ *таблице ниже беззнаковые целые числа закодированы одним байтом*.\n\nБеззнаковое целое число | Двоичный код числа\n:--: | :--:\n0 | 00000000\n1 | 00000001\n2 | 00000010\n3 | 00000011\n127 | 01111111\n128 | 10000000\n254 | 11111110\n255 | 11111111\n\n### Кодирование целых чисел со знаком\n\nНа *кодирование знака числа выделяется один бит* - *старший бит* (*первый бит последовательности*):  \n* `0` - *знак* `+`, *соответствует положительному числу*\n* `1` - *знак* `-`, *соответствует отрицательному числу*.\n\n\nВсего *существует* `m = 2^n` *n-битных двоичных последовательностей*. Этими `m` *двоичными последовательностями* можно *представить целые числа со знаком* от `- (m / 2)` до `(m / 2) - 1`. \n\n*Число* `- (m / 2) = - 2^(n - 1)` называют **минимальным целым числом со знаком** (*нижняя граница множества*), а *число* `m - 1 = 2^(n - 1) - 1` называют **максимальным целым числом со знаком** (*верхняя граница множества*).\n\nНапример, при *представлении целого числа со знаком одним байтом*: *выделяется* `1 бит` на *знак* и *остаётся* `7 бит` на само *число*. Таким образом, *одним байтом* могут быть *представлены целые числа со знаком от* `0` *до* `127`.\n\nВ *таблице ниже целые числа со знаком закодированы одним байтом*.\n\nЦелое число со знаком | Двоичный код числа\n:--: | :--:\n0 | 00000000\n1 | 00000001\n-1 | 10000001\n2 | 00000010\n-2 | 10000010\n127 | 01111111\n-127 | 11111111\n\n\n<!--\n\n### Перевод чисел \n\nДесятичная с/с | Двоичная с/с\n:--: | :--:\n0 | 00000000\n1 | 00000001\n2 | 00000010\n3 | 00000011\n4 | 00000100\n7 | 00000111\n8 | 00001000\n15 | 00001111\n16 | 00010000\n31 | 00011111\n32 | 00100000\n63 | 00111111\n64 | 01000000\n127 | 01111111\n128 | 10000000\n255 | 11111111\n\n-->\n\n## Размер целого, короткое и длинное целые\n\n<!--\nВ зависимости от *количества байт*, используемых на *представление целого числа*, выделяют *несколько типов целочисленных значений*. \n\nДля удобства использования вводятся также короткое целое и длинное целое.\n-->\n\n*Размер целочисленного значения задаётся* либо *языком программирования*, либо *зависит* от *размера машинного слова*, который *определяется* *архитектурой процессора* (CPU).\n\n*Ранее* было *показано*, что от *размера целочисленного значения* (`int`) *напрямую зависят диапазоны допустимых знаковых и беззнаковых целых чисел*.\n\n#### Диапазон целых чисел в зависимости от размера целочисленного значения\nРазмер целого | Беззнаковое целое | Знаковое целое\n:--: | :--: | :--:\n`1 байт` | *от* `0` *до* `2^8 - 1 = 255` | *от* `-2^7 = -128` *до* `2^7 - 1 = 127`\n`2 байта` | *от* `0` *до* `2^16 -1 = 65535` | *от* `-2^15 = -32768` *до* `2^15 - 1 = 32767`\n`4 байта` | *от* `0` *до* `2^32 - 1 = 4294967295` | *от* `-2^31 = -2147483648` *до* `2^31 - 1 = 2147483647`\n`8 байт` | *от* `0` *до* `2^64 - 1 = 18446744073709551615` | *от* `-2^63 = -9223372036854775808` *до* `2^63 - 1 = 9223372036854775807`\n\n\n**Коротким целым** (англ. short integer) называют *тип данных*, *предназначенный* для *представления целых чисел*, *размер* которых *меньше либо равен стандартному размеру целочисленного значения* (`int`). *Этот тип* данных может быть *использован* для *экономии памяти*.\n\n**Длинным целым** (англ. long integer) называют *тип данных*, *предназначенный* для *представления целых чисел*, *размер* которых *равен или превышает стандартный размер целочисленного значения* (`int`). *Этот тип* данных *используется* при *работе* с *большими целыми числами*.\n\nВ *различных языках программирования размеры короткого* и *длинного целого отличаются*, что можно увидеть на *таблице ниже*.\n\n#### Размеры короткого и длинного целых чисел в различных языках программирования\nЯзык программирования | Размер короткого целого (`short`) | Размер целого (`int`) | Размер длинного целого (`long`)\n:--: | :--: | :--: | :--:\n**C** | `2 байта` | `4 байта` | `4 байта`\n**C++** | `2 байта` | `4 байта` | `4 байта`\n**C#** | `2 байта` | `4 байта` | `8 байт`\n**Java** | `2 байта` | `4 байта` | `8 байт`\n\n<!--\nПример с JavaScript: Max_value, max safe int, big int\n-->\n\n<!--\n### Сводная таблица\nТип | Размер  | Диапазон допустимых значений\n:--: | :--: | :--:\n**Ниббл** (англ. **nibble**), **полуоктет** (англ. **semioctet**) | `половина байта` (`4 бит`) | *Беззнаковый*: *от* `0` *до* `2^4 - 1 = 15`, *знаковый*: *от* `-2^3 = -8` до `2^3 - 1 = 7`\n**Байт** (англ. **byte**),  **октет** (англ. **octet**) | `1 байт` (`8 бит`) | *Беззнаковый*: *от* `0` *до* `2^8 - 1 = 255`, *знаковый*: *от* `-2^7 = -128` до `2^7 - 1 = 127`\n**Короткое целое** (англ. **short**), **полуслово** (англ. **halfword**) | `2 байта` (`16 бит`) | *Беззнаковый*: *от* `0` *до* `2^16 - 1 = 65535`, *знаковый*: *от* `-2^15 = -32768` до `2^15 - 1 = 32767`\n**Целое** (англ. int), **слово** (англ. **word**) | `4 байта` (`32 бит`) | *Беззнаковый*: *от* `0` *до* `2^32 - 1`, *знаковый*: *от* `-2^31` до `2^31 - 1`\n**Длинное целое** (англ. **long**), **длинное слово** (англ. **longword**),  **двойное слово** (англ. **doubleword**), (англ. **long long**) | `8 байт` (`64 бит`) | *Беззнаковый*: *от* `0` *до* `2^64 - 1`, *знаковый*: *от* `-2^63` до `2^63 - 1`\n-->\n\n\n## Арифметические операции над целыми числами\n- [Унарные арифметические операции](#унарные-арифметические-операции)\n- [Бинарные арифметические операции](#бинарные-арифметические-операции)\n\n### Унарные арифметические операции\n* **Инкремент** (англ. **inc**rement) - *увеличение значения на единицу*. *Обозначение инкремента числа* `x`: `x++` или `++x`. Например, `++6 = 7`.\n* **Декремент** (англ. **dec**rement) - *увеличение значения на единицу*. *Обозначение инкремента числа* `x`: `x--` или `--x`. Например, `--4 = 3`.\n* **Модуль** (англ. **abs**olute value) - *значение числа без* учёта *знака*. *Обозначение модуля числа* `x`: `|x|` (мат.), `abs(x)` (прог.). Например, `|-5| = 5`.\n* **Отрицание** (англ. **neg**ation) - *смена знака числа* на *противоположный*. *Обозначение отрицания числа* `x`: `-x`, `neg(x)` (прог.). Например, *отрицание числа* `8`: `-8`, *отрицание числа* `-2`: `2`.\n* **Взятие квадратного корня** (англ. **sq**uare **r**oo**t**). *Обозначение квадратного корня числа* `x`: `√x` (мат.), `sqrt(x)` (прог.). Например, `√49 = 7`, то есть `7 • 7 = 49`.\n* **Натуральный логарифм** (англ. **log**arithm). *Обозначение натурального логарифма числа* `x`: `ln(x)`, `log(x)` (прог.). Например, `ln(7) = 1.94591`, то есть `e^(1.94591) = 7`.\n\n### Бинарные арифметические операции\n* **Сложение** (англ. **add**ition). *Обозначение сложения чисел* `x` и `y`: `x + y`. Например, `7 + 6 = 13`.\n* **Разность** (англ. **sub**traction). ). *Обозначение вычитания числа* `y` из *числа* `x`: `x - y`. Например, `7 - 6 = 1`.\n* **Умножение** (англ. **mul**tiplication). *Обозначение умножения чисел* `x` и `y`: `x • y` (мат.), `x * y` (прог.). Например, `6 • 7 = 42`.\n* **Деление** (англ. **div**ision). *Обозначение деления числа* `x` на *число* `y`: `x ÷ y` (мат.), `x / y` (прог.). Например, `42 ÷ 6 = 7`.\n* **Возведение в степень** (англ. **pow**er). *Обозначение возведения числа* `x` в *степень* `y`: `x ^ y` (мат.), `x ** y` (прог.), `pow(x, y)` (прог.). Например, `2 ^ 4 = 16`.\n\n## Битовые операции над целыми числами\n- [О битовых операциях](#о-битовых-операциях)\n- [Различие битовых и логических операций](#различие-битовых-и-логических-операций)\n- [Унарные битовые операции](#унарные-битовые-операции)\n- [Бинарные битовые операции](#бинарные-битовые-операции)\n- [Битовые сдвиги](#битовые-сдвиги)\n\n### О битовых операциях\n\n**Битовые**, **побитовые операции** (англ. bitwise operation), **поразрядные операции** *предназначены* для работы с *последовательностями бит*.\n\nПоскольку *целое число* можно *представить* в *двоичной системе счисления* (как *двоичное число*), к нему *применимы битовые операции*.\n\n*Первый бит двоичной последовательности* называют **старшим битом**, *последний бит* - **младшим битом**. \n\n*Старший* и *младший биты* называют **крайними битами**, *оставшиеся биты последовательности* называют **средними битами**.\n\n### Различие битовых и логических операций\n\n**Логические операции** *принимают операнды логического типа* (или *операнды приводятся* к *данному типу*) и *возвращают результат логического типа*.\n\nВ *большинстве языков программирования логические значения представлены значениями* `true` и `false` (иногда `1` и `0`).\n\n```js\n0 && 1 = false /* false && true */\n3 && 2 = true /* true && true */\n```\n\n**Битовые операции** *принимают* в качестве *операндов последовательности бит* (или *значения другого типа приводятся* к *последовательностям бит*) и *возвращают последовательность бит*.\n\n```js\n0 & 1 = 0\n3 & 2 = 0010 /* 0011 & 0010 */\n01100011 & 11110000 = 01100000\n```\n\n### Унарные битовые операции\n* **Битовое отрицание**, **битовое “НЕ\"** (англ. bitwise “NOT”), **дополнение** (англ. complement) *принимает двоичную последовательность* и производит *логическое отрицание каждого* её *бита*, то есть *каждый ноль становится единицей* и *каждая единица становится нулём*. *Обозначение побитового отрицания последовательности* `x`: `NOT x`. \n\nВыражение | Значение # 1 | Значение # 2 | Значение # 3\n:--: | :--: | :--: | :--:\n`x` | 00000001 | 10111010 | 11111111\n`NOT x` | 11111110 | 01000101 | 00000000\n\n* **Получение знака** *целого числа со знаком*. *Знак определяется* *старшим битом*: `0` - `+`, `1` - `-`.\n\nЧисло | Знак\n:--: | :--:\n00000001 | +\n10111010 | -\n11111111 | -\n\n* **Получение чётности** *целого числа*. *Чётность определяется младшим битом*: `0` - *чётное*, `1` - *нечётное*.\n\nЧисло | Чётность\n:--: | :--:\n00000001 | нечётное\n10111010 | чётное\n11111111 | нечётное\n\n### Бинарные битовые операции\n* **Битовое “И”** (англ. bitwise “AND”) *принимает две двоичные последовательности* и производит *логическое “И”* над *каждой парой бит*, которые *стоят* на *одинаковых позициях* в *последовательностях*. *Обозначение битового “И”* для *последовательностей* `x` и `y`: `x AND y`.\n\nВыражение | Значение # 1 | Значение # 2 | Значение # 3\n:--: | :--: | :--: | :--:\n`x` | 10000001 | 10101010 | 11110001\n`y` | 00000111 | 01010101 | 00010000\n`x AND y` | 00000001 | 00000000 | 00010000\n\n* **Битовое “ИЛИ”** (англ. bitwise “OR”) *принимает две двоичные последовательности* и производит *логическое “ИЛИ”* над *каждой парой бит*, которые *стоят* на *одинаковых позициях* в *последовательностях*. *Обозначение битового “ИЛИ”* для *последовательностей* `x` и `y`: `x OR y`.\n\nВыражение | Значение # 1 | Значение # 2 | Значение # 3\n:--: | :--: | :--: | :--:\n`x` | 10000001 | 10101010 | 11110001\n`y` | 00000111 | 01010101 | 00010000\n`x OR y` | 10000111 | 11111111 | 11110001\n\n* **Битовое “ИЛИ-НЕ”**, **битовое исключающее “ИЛИ”** (англ. bitwise exclusive “OR”, bitwise XOR) *принимает две двоичные последовательности* и производит *логическое исключающее “ИЛИ”* над *каждой парой бит*, которые *стоят* на *одинаковых позициях* в *последовательностях*. *Обозначение битового исключающего “ИЛИ”* для *последовательностей* `x` и `y`: `x XOR y`.\n\nВыражение | Значение # 1 | Значение # 2 | Значение # 3\n:--: | :--: | :--: | :--:\n`x` | 10000001 | 10101010 | 11110001\n`y` | 00000111 | 01010101 | 00010000\n`x XOR y` | 01111001 | 00000000 | 00011110\n\n#### Сводная таблица битовых операций\nВыражение | Значение # 1 | Значение # 2 | Значение # 3\n:--: | :--: | :--: | :--:\n`x` | 10000001 | 10101010 | 11110001\n`y` | 00000111 | 01010101 | 00010000\n`NOT x` | 01111110 | 01010101 | 00001110\n`NOT y` | 11111000 | 10101010 | 11101111\n`x AND y` | 00000001 | 00000000 | 00010000\n`x OR y` | 10000111 | 11111111 | 11110001\n`x XOR y` | 01111001 | 00000000 | 00011110\n\n\n### Битовые сдвиги\n\n**Битовым сдвигом** (англ. bit shift) называют *битовую операцию*, которая *принимает двоичную последовательность* и *смещает биты* (*позиции бит*) в ней.\n\n<!--\nРазличают *несколько видов сдвигов*.\n-->\n\n#### Сдвиги по *направлению смещения бит*\n* **Битовый сдвиг влево** (англ. left bitwise shift), *сдвиг* от *младшего бита* к *старшему*, подразумевает *смещение* (*перестановку*) *кадого* из *значений средних бит* на *1 бит влево*.\n* **Битовый сдвиг вправо** (англ. right bitwise shift), *сдвиг* от *старшего бита* к *младшему*, подразумевает *смещение* *кадого* из *значений средних бит* на *1 бит вправо*.\n\n#### Сдвиги по *поведению крайних бит*\n* При **логическом сдвиге** (англ. logical shift) *один* из *крайних битов выпадает* из *последовательности*, *другой принимает нулевое значение*. \n```\n>> 10001111 = 01000111\n>> 01000111 = 00100011\n\n<< 10001111 = 00011110\n<< 00011110 = 00111100\n```\n* При **арифметическом сдвиге** (англ. arithmetic shift) *сохраняется знак числа*, который *хранится* в *старшем бите* (*сохраняется значение старшего бита*). В *остальном арифметический сдвиг аналогичен логическому*.\n```\n>> 10001111 = 11000111\n>> 11000111 = 11100011\n\n>> 01000111 = 00100011\n\n<< 10001111 = 00011110\n<< 00011110 = 00111100\n```\n* При **циклическом сдвиге** (англ. circular shift, bitwise rotation) *значение одного крайнего бита переносится* в *другой крайний бит*, то есть *производится [круговая перестановка]()*.\n```\n>> 10001111 = 11000111\n>> 11000111 = 11100011\n>> 11100011 = 11110001\n\n<< 10001111 = 00011111\n<< 00011111 = 00111110\n<< 00111110 = 01111100\n```\n\n*Логический сдвиг влево эквивалентен умножению беззнакового целого* на *два*, а *логический сдвиг вправо* - *делению беззнакового целого* на *два*.\n\n*Арифметический сдвиг влево эквивалентен умножению целых чисел* со *знаком* на *два*, а *арифметический сдвиг вправо* - *делению целых чисел* со *знаком* на *два*. \n\n\n\n## Число с плавающей точкой\n- [Экспоненциальная форма записи числа](#экспоненциальная-форма-записи-числа)\n- [О числе с плавающей точкой](#о-числе-с-плавающей-точкой)\n- [Операции над числами с плавающей точкой](#операции-над-числами-с-плавающей-точкой)\n- [Точность](#точность)\n\n### Экспоненциальная форма записи числа\n\n**Экспоненциальной формой записи числа `n`** называют *представление числа* в виде *произведения некоторого* другого *числа* `m`, называемого **мантиссой** (англ. mantissa, significand), и *показательной функции* `b^e`, в которой *число* **`b`** называют **основанием** (англ. base), а *число* **`e`** - **экспонентой** (англ. exponent) или **порядком**: **`n = m • b^e `**.\n\n*Обычно* в качестве *основания* берут *число* `10`. В этом случае **`n = m • 10^e`**. Такую *форму записи числа* называют **стандартной формой** (англ. standard form) или **научной формой** (англ. scientific form)\n\n\nВ *научной форме записи* *мантисса содержит все ненулевые цифры числа* `n`, а *все нулевые цифры представляются порядком* `e`.\n\nВ *математике доказано*, что *любое действительное число* может быть *представлено десятичной дробью*, а значит *любое число* можно *представить* в *научной форме записи*, причём *бесконечным множеством способов*. Например, `17` *можно представить* как `17 • 10^0`, `1.7 • 10^1`, `0.17 • 10^2`, `0.017 • 10^3` и так далее.\n\n*Чаще всего порядок* `e` *подбирают* так, чтобы *значение модуля числа* `m` было *между* `1` и `10`: `1 <= |m| < 10`. Такую *форму записи числа* называют **нормализованной формой** (англ. normalized form).\n\nЧисло | Нормализованная форма\n:--: | :--:\n`3` | `3 • 10^0`\n`17` | `1.7 • 10^1`\n`-10` | `-1 • 10^1`\n`1000` | `1 • 10^3`\n`-0.051` | `-5.1 • 10^(-2)`\n`123` | `1.23 • 10^2`\n`0.0008` | `8 • 10^(-4)`\n\n<!--\n*Научную форму записи удобно использовать*, когда имеем дело с *очень большими* или *очень маленькими числами*. В *остальных случаях* обычно *записывают число как есть*.\n-->\n\nВ *компьютере* для записи *очень больших* и *очень маленьких чисел* так же используется *научная (нормализованная) форма записи чисел* в виде: **`n = mEe`**, где **`E`** означает `10^`.\n\n*Порядок* `e` содержит *знак* `+`, если *число* `n` *больше единицы*, и *знак* `-`, если *число* `n` *меньше*. *Порядок* `e` также *содержит ведущий ноль*, если *число порядка меньше* `10`.\n\nЧисло | Нормализованная форма | Представление в компьютере\n:--: | :--: | :--:\n`17000000000` | `1.7 • 10^10` | `1.7E+10`\n`-73650000` | `-7.365 • 10^7` | `-7.365E+07`\n`0.00000154` | `1.54 • 10^(-6)` | `1.54E-06`\n`-0.000000037` | `-3.7 • 10^(-8)` | `-3.7E-08`\n\n### О числе с плавающей точкой\n\n**Числом с плавающей точкой** (англ. floating-point number) или **числом с плавающей запятой** называют *представление числа* в *экспоненциальной форме записи*.\n\n*Числом с плавающей точкой* можно *представить* как *любое целое число* (например, `371 = 3.71 • 10^2`), так и *любую десятичную дробь* (например, `0.0333 = 3.33 • 10^(-2)`).\n\n*Название числа с плавающей точкой объясняется* тем, что *точку можно поставить где угодно между цифр во внутреннем представлении*. Например, *число* `1984` *можно представить* как `0,1984 • 10^4`, `1,984 • 10^3`, `1984 * 10^0`, `19840 * 10^(-1)` и так далее. \n\n*Пример выше показывает*, что *существует бесконечное множество представлений числа* как *числа с плавающей точкой*. \n\nВ *программировании* для *представления слишком больших* или *слишком малых* *чисел с плавающей точкой* используют *научную форму записи*, *остальные числа с плавающей точкой записываются* как обыкновенная *десятичная дробь* (*порядок не указывается*).\nЧисло | Научная форма записи | Компьютерное представление числом с плавающей точкой\n:--: | :--: | :--:\n`7` | `7 • 10^0` | `7.0`\n`-322` | `-3.22 • 10^2` | `-322.0`\n`19.84` | `1.984 • 10` | `19.84`\n`0.000000001` | `1 • 10^(-9)` | `1E-09`\n`-0.0000000000177` | `-1.77 • 10^(-11)` | `-1.77E-11`\n`300000000` | `3 • 10^8` | `3E+08`\n\nЧасто *языки программирования* предоставляют *несколько типов данных*, *каждый* из которых используется для *целых чисел с плавающей точкой*. *Обычно* такими являются *типы данных* `Float` и `Double`, \n\n### Операции над числами с плавающей точкой\n- [О сложении и разности чисел с плавающей точкой](#о-сложении-и-разности-чисел-с-плавающей-точкой)\n- [Сложение чисел с плавающей точкой](#сложение-чисел-с-плавающей-точкой)\n- [Разность чисел с плавающей точкой](#разность-чисел-с-плавающей-точкой)\n- [Умножение чисел с плавающей точкой](#умножение-чисел-с-плавающей-точкой)\n- [Деление чисел с плавающей точкой](#деление-чисел-с-плавающей-точкой)\n\n\nДля *чисел с плавающей точкой* доступны *базовые бинарные арифметические операции*:\n\n\n*Далее* будем рассматривать *операции над числами* **`x = m1 • 10^e1`** и **`y = m2 • 10^e2`**.\n\n#### О сложении и разности чисел с плавающей точкой\n\n*Сложение* и *разность чисел с плавающей точкой требуют совпадения порядков* чисел: **`e = e1 = e2`**. Если *порядки не совпадают*, то *необходимо перенести точку* в одной из *мажорант* `m1` и `m2` так, чтобы *порядки* `e1` и `e2` *совпали*.\n\nНапример, если `x = 3 • 10^2` и `y = 2 • 10^3`, то перед *сложением* или *разностью* `x` и `y` *необходимо*\n* *либо* `x` *привести* к *форме* `x = 0.3 • 10^3`,\n* *либо* `y` *привести* к *форме* `y = 20 • 10^2`.\n\n#### Сложение чисел с плавающей точкой\n\n**Сложение** (англ. addition) **чисел с плавающей точкой `x` и `y`** осуществляется по *формуле*:  \n**`x + y = (m1 + m2) • 10^e`**.\n\n*Ниже* представлено *сложение чисел* `2.5 • 10^5` и `3.25 • 10^6`.\n```js\n2.5 • 10^5 + 3.25 • 10^6 = \n2.5 • 10^5 + 32.5 • 10^5 =\n(2.5 + 32.5) • 10^5 =\n36.0 • 10^5 =\n3.6 • 10^6 /* инженерная форма */\n```\n\n#### Разность чисел с плавающей точкой\n\n\n**Разность** (англ. subtraction) **чисел с плавающей точкой `x` и `y`** осуществляется по *формуле*:  \n**`x - y = (m1 - m2) • 10^e`**.\n\n*Ниже* представлена *разность чисел* `3.5 • 10^3` и `4.5 • 10^2`.\n```js\n3.5 • 10^3 - 4.5 • 10^2 = \n35 • 10^2 - 4.5 • 10^2 =\n(35 - 4.5) • 10^2 =\n30.5 • 10^2 =\n3.05 • 10^3 /* инженерная форма */\n```\n\n#### Умножение чисел с плавающей точкой\n\n\n**Умножение** (англ. multiplication) **чисел с плавающей точкой `x` и `y`** осуществляется по *формуле*:  \n**`x • y = (m1 • m2) • 10^(e1 + e2)`**.\n\n*Ниже* представлено *умножение чисел* `7.25 • 10^3` и `3.0 • 10^2`.\n```js\n(7.25 • 10^3) • (3.0 • 10^2) = \n(7.25 • 3.0) • 10^(3 + 2) =\n21.75 • 10^5 =\n2.175 • 10^6 /* инженерная форма */\n```\n\n#### Деление чисел с плавающей точкой\n\n\n**Деление** (англ. division) **чисел с плавающей точкой `x` и `y`** осуществляется по *формуле*:  \n**`x ÷ y = (m1 ÷ m2) • 10^(e1 - e2)`**.\n\n*Ниже* представлено *деление чисел* `26.4 • 10^8` и `2.4 • 10^6`.\n```js\n(26.4 • 10^8) ÷ (2.4 • 10^6) = \n(26.4 ÷ 2.4) • 10^(8 - 6) =\n11 • 10^2 =\n1.1 • 10^3 /* инженерная форма */\n```\n\n### Точность\n\nИногда появляется необходимость *округлить действительное число*.\n\nНапример, *число* `0.16738` может быть *математически округлено до* `0.0`, `0.2`, `0.17`, `0.167` или `0.1674`.\n\n*Округляемое число* называют **истинным значением** (англ. true value) или **действительным**  (*фактическим*) **значением** (англ. actual value).\n\n*Результат округления* называют **измеренным значением** (англ. measured value).\n\nИз *примера выше* следует, что *вместо истинного значения* `0.16738` может быть *использовано одно* из *измеренных значений* `0.0`, `0.2`, `0.17`, `0.167`, `0.1674` в *зависимости* от *требуемой точности*.\n\n**Точностью вычислений** (англ. accuracy) `a` называют *меру близости измеренного значения* `x1` к *истинному значению* `x`. Обычно эта *мера выражается разницей между значениями* `x` и `x1`, *представленной* их *модулем разности*:  \n`a = |x - x1|`.\n\n**Точностью** (англ. precision) `p` называют *меру разброса измеренных значений*. Например, для *измеренных значений* `x1` и `x2` она *выражается модулем разности* этих *значений*: `p = |x1 - x2|`·\n\n*Несмотря* на *похожесть формул*, *точность* `p` и *точность вычислений* `a` имеют *существенное различие*, выраженное в их *предназначении*: `a` *вычисляется* для *проверки точности промежуточного* или *конечного результата*, а `p` *задаётся* для *ограничения области допустимых значений*.\n\n\n<!--\n## Ссылка и указатель\n- [Ссылка](#ссылка)\n-->\n\n## Ссылка\n- [О ссылке](#о-ссылке)\n- [Создание и разыменование ссылки](#создание-и-разыменование-ссылки)\n- [Изменение данных по ссылке](#изменение-данных-по-ссылке)\n- [Ссылка как параметр функции](#ссылка-как-параметр-функции)\n- [Использование ссылок в различных языках программирования](#использование-ссылок-в-различных-языках-программирования)\n\n### О ссылке\n\n**Ссылкой** (англ. reference) называют *объект*, который *указывает* на *определённые данные*, но *не хранит* их.\n\nГоворят, что *ссылка* **ссылается** (англ. refers) на некоторые *данные* в *памяти компьютера*.\n\n*Значением ссылки* являются сами *данные*, на которые она *ссылается*.\n\n### Создание и разыменование ссылки\n\n*Получение данных* (*доступ к данным*) по *ссылке* называют **разыменованием ссылки** (англ. dereferencing).\n\nДля *разыменования ссылки достаточно* просто *использовать значение ссылки*.\n\n<!--\n*Разыменование ссылки производится неявно* (*автоматически*): для *разыменования* достаточно вы\n-->\n\n*Ниже* представлен *пример создания* и *разыменования ссылки* на *языке C++*. При *создании ссылки* *перед названием переменной* добавляется **амперсанд** (`&`).\n```cpp\n/* объявление переменной value целочисленного типа */\nint value = 17;\n/* создание ссылки ref на переменную value */\nint &ref = value;\n/* разыменование ссылки ref, то есть получение значения переменной value, и вывод значения на консоль */\ncout << ref; // 17\n```\n\nВ *языке C++* также имеется *возможность получить физический адрес* любой *переменной* с помощью *оператора* `&`.\n```cpp\ncout << &value; // 0017BF22\ncout << &ref; // 0017BF22\n```\n\n\n*Ссылка не может быть пустой*, то есть она *не может не иметь значения*, *не может не ссылаться* на *данные*.\n```cpp\nint &ref; // ошибка\n```\n\nИз *примеров выше следует*, что *ссылку* можно *использовать как псевдоним*: *создание ссылки позволяет обратиться* к некоторому *старому значению* по *новому* (еще одному) *имени*.\n\n\n### Изменение данных по ссылке\n\n*По ссылке можно* не только получать, но и *изменять данные*.\n\n*Ниже* представлен *пример изменения данных* по *ссылке* на *языке C++*. \n\n```cpp\n/* объявление переменной value целочисленного типа */\nint value = 17;\n/* создание ссылки ref на переменную value */\nint &ref = value;\n/* изменение значения переменной value по ссылке */\nref = 18;\n/* вывод значения переменной value на консоль */\ncout << value; // 18\n```\n\n### Ссылка как параметр функции\n\n*Вспомним*, что *объекты передаются* по *ссылке*, а *значения примитивных типов данных* просто *копируются*.\n\n*Вспомним также*, что *параметры функции* представляют собой *локальные переменные*, *доступные лишь внутри* этой *функции*. *Параметры функции копируют* *значения аргументов*, *передаваемых* в эту *функцию*.\n\n*Следующий код отработает ожидаемо*: *значение переменной* `foo` *не изменится*.\n```cpp\n/* функция, увеличивающая значение аргумента на 1 */\nvoid increment(int x) {\n  return ++x;\n}\n\nint foo = 0;\n/* результат выполнения функции никуда не присваивается */\nincrement(foo); // 1\ncout << foo; // 0\n/* значение переменной foo не изменилось */\n```\n\n\nИспользование *ссылки* в *качестве параметра* позволяет *изменять значение аргумента примитивного типа прямо* в самой *функции* (путём *изменения параметра ссылочного типа*).\n```cpp\n/* функция, увеличивающая значение аргумента на 1 */\nvoid increment(int &x) {\n  return ++x;\n}\n\nint foo = 0;\n/* результат выполнения функции никуда не присваивается */\nincrement(foo); // 1\ncout << foo; // 1\n/* значение переменной foo изменилось по ссылке */\n```\n\n\n\n### Использование ссылок в различных языках программирования\n\nВ *большинстве языков программирования ссылки* являются лишь *частью внутренней реализации* и *не доступны* для *явного использования* в *коде программы*.\n\nНапример, в *языке JavaScript*, как и в многих других языках, *нет возможности явно* (*напрямую*) *использовать ссылки*, тем не менее *передача* любого *объекта* *осуществляется* по *ссылке* (*внутренняя реализация*, *неявное использование ссылки*).\n```js\n/* создание объекта foo с полем notes */\nconst foo = { notes: 17 }; \n/* передача объекта foo по ссылке в переменную bar */\nconst bar = foo; \n/* изменение объекта foo по ссылке через переменную bar */\nbar.notes = 18;\nconsole.log(foo); // { notes: 18 }\n```\n\n## Указатель\n- [Об указателе](#об-указателе)\n- [Создание и разыменование указателя](#создание-и-разыменование-указателя)\n\n\n### Об указателе\n\n**Указателем** (англ. pointer) называют *объект*, *хранящий* некоторый *адрес* в *памяти компьютера*. \n\n*Значением указателя* является *физический адрес* некоторых *данных*. Говорят, что *указатель* **указывает** (англ. points) на эти *данные*.\n\n### Создание и разыменование указателя\n\n*Получение данных* (*доступ к данным*) по *адресу*, *хранящемуся* в *указателе*, называют **разыменованием указателя** (англ. dereferencing the pointer).\n\n*Ниже* представлен *пример создания* и *разыменования указателя* на *языке C++*. При *создании указателя* *перед названием переменной* добавляется `*`.\n```cpp\n/* объявление переменной value целочисленного типа */\nint value = 17;\n/* создание указателя pointer на переменную value */\nint *pointer = &value;\n/* вывод значения указателя на консоль */\ncout << pointer; // 0017BF22\n```\n\nВ *языке C++* *указатель разыменовывается* с помощью *оператора* `*`.\n```cpp\ncout << pointer; // 0017BF22\ncout << *pointer; // 17\n```\n\nВ *отличии* от *ссылки*, *указатель может быть нулевым*, то есть *может не указывать* ни на один *объект*.\n```cpp\nint *ref = NULL;\n```\n\n<!--\n\n### Динамическое выделение памяти (указатель и массив)\n\n\n### Указатели на указатели\n\n*Указатель* может *указывать* на *другой указатель*.\n\n\n### Сравнение указателя и ссылки\n\nУказатели можно эффективно использовать при *динамическом выделении памяти*.\n\n\n-->\n\n\n\n<!--\n### Сравнение целых чисел\nРекомендуется прочитать: [Операция](), [Отношение]().\n\nВ *отличие математики*, где *сравнение представляет* собой *отношение*, в *программировании сравнение* является *операцией*, то есть оно *обязательно возвращает результат* (обычно *логического типа*).\n-->\n\n\n\n<!--\n# Системы счисления\n\n## Двоичная система счисления\n\n-->\n"
  },
  {
    "path": "Development.md",
    "content": "# Оглавление\n- [SCRUM теория и жестокая реальность](#scrum-теория-и-жестокая-реальность)\n\n# SCRUM теория vs жестокая реальность\n- [О SCRUM](#о-scrum)\n- [Скрам-артефакты](#скрам-артефакты)\n  - [Спринт (`Sprint`)](#спринт-sprint)\n  - [Бэклог (`Backlog`)](#бэклог-backlog)\n  - [Инкремент (`Increment`)](#инкремент-increment)\n- [Скрам-команда](#скрам-команда)\n  - [Владелец продукта (`Product Owner`)](#владелец-продукта-product-owner)\n  - [Скрам-мастер (`Scrum Master`)](#скрам-мастер-scrum-master)\n  - [Команда разработчиков (`Developer Team`)](#команда-разработчиков-developer-team)\n- [Скрам-события](#скрам-события)\n  - [Планирование спринта (`Sprint Planning`)](#планирование-спринта-sprint-planning)\n  - [Ретроспектива спринта (`Sprint Retrospective`, `Retro`)](#ретроспектива-спринта-sprint-retrospective-retro)\n\n## О SCRUM\n\n### Что такое SCRUM\n\n**SCRUM** (англ. `scrum` - \"схватка\") — это методология управления проектами, которая чаще всего встречается при разработке программного обеспечения (англ. `software development`), но может применяться и в других сферах, в том числе в сферах маркетинга (англ. `marketing`), продаж (англ. `sales`) и исследования (англ. `research`) чего-либо.\n\n### Кому стоит читать книгу с SCRUM целиком\nЕсли вы разработчик, то, возможно, вам нет смысла читать книгу о SCRUM - существует множетсво других книг, которые могут принести вам намного больше пользы. \n\nОчень детально изучить SCRUM стоит менеджерам (например, проектным менеджерам), бизнесс-аналитикам. Идеал недостижим, но к нему стоит стремиться. А ещё их об этом очень активно спрашивают на собеседованиях, поскольку эффективное управление - их основная задача. Оснавная же задача программиста - создание чего-либо.\n\nОчень часто программисты, которые доходят до позиции Team Lead или хотя бы Tech Lead и думаю о своём дальнейшем развитии, уходят в менеджмент в последствии. Потому что чем больше людей в твоём подчинении, тем выше твоя позиция и больше доход. Более того, многие задачи можно делигировать, выстраивая иерархию ответственности. И код самому больше писать уже не нужно - сплошные митинги. Не всем такое подходит, но очень многим.\n\n### SCRUM в реальной жизни\n\nВ реальной жизни он в 90% случаев SCRUM используется не больше, чем на 50% от того, что в нём предусматривается.\n\nЧаше всего на небольших проектах с маленькими командами имеется двухнедельных спринт, спринт планинг, доска в Trello с несколькими колонками (Backlog, To do, Doing, Done) и несколько звонков разработчиков с заказчиками в неделю. Вот и весь SCRUM.\n\n\n## Скрам-артефакты\n- [Спринт (`Sprint`)](#спринт-sprint)\n- [Бэклог (`Backlog`)](#бэклог-backlog)\n- [Инкремент (`Increment`)](#инкремент-increment)\n\n### Спринт (`Sprint`)\n\n**Спринт** (англ. `Sprint`) - итерация в скраме, в ходе которой создается инкремент бизнес-продукта; повторяющийся фиксированный временной интервал (repeatable fixed time-box) от одной недели до месяца, в течение которого создаётся \"готовый\" (Done) продукт с максимально возможным значением (value). Спринты имеют одинаковую продолжительность на протяжении всего процесса разработки. Новый Спринт начинается сразу после завершения предыдущего Спринта. Чем короче спринт, тем гибче скрам и тем чаще выходят релизы.\n \n### Бэклог (`Backlog`)\n \n**Бэклог продукта** (англ. `Product Backlog`) - упорядоченный список всего, что может быть нужно продукту; единственный источник требований любых будущих изменений продукта. За бэклог продукта отвечает владелец продукта (содержание, доступность, упорядочивание).\n\n**Бэклог спринта** (англ. `Sprint Backlog`) - набор элементов из бэклога продукта, выбранных для спринта, а также план доставки инкремента продукта и реализации целей спринта.\n\n### Инкремент (`Increment`)\n\n**Инкремент** (англ. `Increment`) - совокупность всех элементов бэклога продукта, выполненных за время спринта, и значение инкрементов всех предыдущих спринтов.\n\n## Скрам-команда\n**Скрам-командом** (англ. `Scrum team`) называют *команду*, которая работает над проектом, разрабатыващимся по методологии SCRUM.\n\nВ скрам-команду входят:\n- [Владелец продукта (`Product Owner`)](#владелец-продукта-product-owner)\n- [Скрам-мастер (`Scrum Master`)](#скрам-мастер-scrum-master)\n- [Команда разработчиков (`Developer Team`)](#команда-разработчиков-developer-team)\n\nвключает в себя владельца продукта, скрам-мастера и команду разработчиков.\n\n### Владелец продукта (`Product Owner`)\n\n**Владельцем продукта** (англ. `Product Owner`) называют *члена скрам-команды*, который *знает* и *понимает желания клиента* (англ. `customer`) и может донести команде их бизнес-значение (business value). \n\nВладелец продукта может консультировать команду, чтобы она корректно выполняла свои задачи.  \n\nВладелец продукта должен принимать все все важные решения для завершения проекта.\n\n*Владелец продукта* отвечает за *бэклог продукта*. Пункты в бэклоге должны быть чёткими и понятными всем, отсортироваными по значимости (value).\n\n### Скрам-мастер (`Scrum Master`)\n\n**Скрам-мастер** (англ. `Scrum Master`) управляет скрам-процессом, помогая команде быть отчётной (accountable) за свои обязательства перед бизнесом и устраняя любые препятствия работе и продуктивности команды. \n\nСкрам-мастер организует митинги.\n\nСкрам-мастеру следует тренировать и мотивировать команду, а не навязывать ей правила.\n\nНа реальных проектах скрам-мастером чаще всего назначают одного из членов других частей скрам-команды. Я участвовал на проектах, на которых скрам-мастером могли быть:\n1) Владелец продукта.\n2) PM/BA. \n3) Опытный разработчик из команды разработчиков с хорошими коммуникационными способностями. \n\nИногда данную в качестве скрам-мастера могут назвачать опытного разработичика с хорошими \n\n## Команда разработчиков (`Developer Team`)\n\nКоманда разработчиков (англ. `Developer Team`, `Dev Team`, `Engineering Team`) стрктурирована и уполномочена на организацию своей работы. Хорошая синергия команды оптимизирует общую эффективность и результативность.\n\nКоманда разработчиков самоорганизуется: никто (даже скрам-мастер) не говорит команде, как превратить бэклог продукта в инкременты.\n\nСкрам не признаёт званий членов команды вне зависимости от их деятельности.\n\nСкрам не признаёт подгруппы в команде разработчиков. Вне зависимости от их областей (тестирование, архитектура, бизнес-анализ). Отдельные члены команды могут обладать специализированными навыками, но отчётность (`accountability`) принадлежит всей команде в целом.\n\n\n## Скрам-события\n\nСкрам-событиями называют митинги, которые команды проводят каждый спринт.\n\n- [Планирование спринта (`Sprint Planning`)](#планирование-спринта-sprint-planning)\n- [Ретроспектива спринта (`Sprint Retrospective`, `Retro`)](#ретроспектива-спринта-sprint-retrospective-retro)\n\n* **Планирование спринта** (англ. `Sprint Planning`) происходит в его начале. Обсуждаются задачи на следующий спринт, выбирающиеся из бэклога проекта.\n* **Ежедневный скрам** (англ. `Daily Scrum`) - 15-минутное событие для команды разработчиков, предназначенное для синхронизации действий и создания плана на ближайшие сутки.\n* **Ревью спринта** (англ. `Sprint Review`) происходит в конце спринта. Проверяется Инкремент спринта и, при необходимости, пополняется бэклог продукта.\n* **Ретроспектива спринта** (англ. `Sprint Retrospective`, `Retro`) предоставляет возможность скрам-команде изучить себя и составить план улучшений, которые будут приняты во время следующего спринта.\n\nНа практике чаще всего\n\n### Планирование спринта (`Sprint Planning`)\n\nПланирование стринта проводится в начале нового спринта на митинге.  \nПродолжительность митинга зависит от продолжительности спринта.\n\nНа первой части митинга участвует вся скрам-команда (владелец продукта, скрам-мастер и команда разработчиков): обсуждаются задачи на текущий спринт. Задачи берутся из бэклога проекта с учётом того, что \nНа второй части митинга участвует только скрам-команда: обсуждаются технические детали реализации, заполняется бэклог спринта.\n\n* Выбираются задачи из бэклога проекта, на основании которых создаётся бэклог спринта.\n* Задачи оцениваются в человеко-часах (не более 12 часов или одного дня на задачу) либо в стори-поинтах. Слишком долгие задачи разбиваются на подзадачи.\n\n### Ретроспектива спринта (`Sprint Retrospective`, `Retro`)\n"
  },
  {
    "path": "DiscreteMath.md",
    "content": "# Оглавление\n- [Введение](#введение)\n- [Теория множеств](#теория-множеств)\n- [Теория мультимножеств](#теория-мультимножеств)\n- [Теория графов](#теория-графов)\n- [Математическая логика](#математическая-логика)\n- [Комбинаторика](#комбинаторика)\n\n\n\n# Введение\n- [Дискретность и непрерывность](#дискретность-и-непрерывность)\n- [О дискретной математике](#о-дискретной-математике)\n- [Наборы элементов, их типы и свойства](#наборы-элементов-их-типы-и-свойства)\n\n## Дискретность и непрерывность\n\n**Дискретностью** (лат. discretus — разделённый, прерывистый) называют *свойство* некоторого *объекта*, *означающее* его *конечность* (прерывность), что *противоположно свойству* **непрерывности**, означающему *бесконечность* *объекта*. \n\nНапример, *дискретное множество* - это *множество* с *конечным числом* *элементов*, то есть *элементы* данного *множества можно пересчитать*, *перечислить*.\n\nК примеру, *дискретным* является *множество планет Солнечной системы*: `{ Меркурий, Венера, Земля, Марс, Юпитер, Сатурн, Уран, Нептун }`.\n\nПримерами *непрерывных объектов* выступают: *прямая* (*бесконечная линия*), такие *бесконечные множества*, как *множество чисел*, *множество слов любой длины*.\n\n## О дискретной математике\n\n**Дискретной математикой** (англ. Discrete math) называют *раздел математики*, в котором *изучаются только дискретные математические объекты*: *конечные множества*, *дискретные функции*, *конечные графы*, *высказывания* и другие.\n\n*Непрерывные объекты* изучаются в *других областях математики* (например, в *математическом анализе*, *теории вероятностей*).\n\n\n## Наборы элементов, их типы и свойства\n\n- [О наборе элементов](#о-наборе-элементов)\n- [Характеристики набора элементов](#характеристики-набора-элементов)\n- [Типы наборов элементов](#типы-наборов-элементов)\n\n\n### О наборе элементов\n\nВ *математике нет чёткого определения* для *набора элементов*.\n\nПод **набором элементов** (англ. collection) *подразумевают* некоторую *совокупность* (группу) *объектов*, *рассматриваемых вместе* в *данный момент* времени (например, для *решения* некоторой *задачи*).\n\n*Объекты*, из которых *состоит* некоторый *набор*, называют **элементами** (англ. elements) *этого* **набора**. *Элементами набора* могут выступать *любые объекты*: звёзды на небе, люди в списке, экспонаты в музее, фигуры на рисунке и так далее.\n\nДля *программиста самыми распространёнными наборами элементов* являются **массив** (англ. array) и **список** (англ. list), для *математика* - **множество** (англ. set) и **кортеж** (англ. tuple).\n\n### Характеристики набора элементов\n\nСуществует *две характеристики*, которые *полностью определяют свойства любого набора элементов*:\n1) **Наличие повторяюшися элементов** в *наборе*.  \n2) **Упорядоченность** *набора*.\n\n#### Наличие повторяющихся элементов в наборе\n\n**Уникальность элементов набора** означает, что данный *набор не может содержать двух* (и более) *одинаковых элементов*. \n\nЕсли *требование уникальности отсутствует*, то *набор элементов может содержать повторяющиеся элементы* и называется **набором элементов с повторениями** (англ. collection with repetitions). \n\n#### Упорядоченность набора\n\n**Упорядоченность набора** означает, что в данном *наборе порядок следования элементов имеет значение*.\n\n**Неупорядоченность набора** означает, что *порядок следования элементов* в *наборе* *не имеет значения*, то есть если *поменять элементы* набора *местами*, то *набор не изменится*. \n\nТаким образом, для *неупорядоченных наборов важно* лишь *наличие* или *отсутствие* того или иного *элемента* в *наборе*, но не *месторасположение элементов относительно друг друга*.\n\n\n### Типы наборов элементов\n\nНа основании *перечисленных ранее характеристик набора* можно *выделить 4 типа наборов элементов*: \n* **Неупорядоченный набор уникальных элементов**. Примеры: *множество*, *сочетание*.\n* **Неупорядоченный набор элементов с повторениями**. Примеры: *мультимножество*, *сочетание с повторениями*.\n* **Упорядоченный набор уникальных элементов**. Примеры: *перестановка*, *размещение*.\n* **Упорядоченный набор элементов с повторениями**. Примеры: *кортеж*, *перестановка мультимножества*, *размещение с повторениями*, *число*, *слово*.\n\n\n# Теория множеств\n- [Почему теория множеств достойна вашего внимания](#почему-теория-множеств-достойна-вашего-внимания)\n- [Множества и их элементы](#множества-и-их-элементы)\n- [Принадлежность к множеству](#принадлежность-к-множеству)\n- [Основные виды множеств](#основные-виды-множеств)\n- [Мощность (кардинальность) множества](#мощность-кардинальность-множества)\n- [Отношения между множествами](#отношения-между-множествами)\n- [Способы задания множества](#способы-задания-множества)\n- [Графическое представление множеств](#графическое-представление-множеств)\n- [Число подмножеств](#число-подмножеств)\n- [Пара и кортеж](#пара-и-кортеж)\n- [Операции над множествами](#операции-над-множествами)\n- [Углубленно об отношнении](#углубленно-об-отношении)\n- [Функция (отображение)](#функция-отображение)\n- [Углубленно об операции](#углубленно-об-операции)\n\nКак вы уже могли догадаться, **теория множеств** изучает *множества*, а точнее их *виды*, *способы представления*, *отношения множеств* и *операции* над ними. \n\n<!-- В данном разделе будут рассмотрены основные понятия теории множеств и способы их применения.-->\n\n## Почему теория множеств достойна вашего внимания\n\n*Теория множеств* вводит такие *понятия*, как *множество*, *отношение*, *функция* (отображение), *упорядоченный набор элементов* и *операция*, которые имеют *невероятно широкое применение* в *математике*, *программировании*, *физике* и других областях. \n\n*Реализационные базы данных* (SQL) полностью *базируются* на *математическом понятии отношения*, где понятие *множества заменяется* понятием *типа данных*.\n\nНекоторые *структуры данных* берут своё начало у *теории множеств*: *упорядоченная пара*, *кортеж* (вектор), *граф*, само *множество* и другие.\n\nВообще говоря, *любые* окружающие нас *объекты* можно *классифицировать* и *представить в виде множеств*, *построить* их *иерархии*, *выявить* их *отношения* и так далее. *Множества* и *окружающие* их *понятия* *помогают* нам *представлять данные*.\n\n## Множества и их элементы\n\n*Рекомендуется* прочитать: [«Наборы элементов, их типы и свойства»](#наборы-элементов-их-типы-и-свойства).\n\n**Множество** (англ. set) — это *неупорядоченный набор* (совокупность) каких-либо *уникальных объектов*, наделённых некоторыми *общими признаками* (характеристиками, чертами, свойствами).\n\n*Объекты*, из которых *состоит множество*, называются **элементами** (англ. elements) этого **множества**. Они могут *представлять* собой *что угодно*: *реальные объекты* (людей, животных, предметы), *абстрактные объекты* (числа, фигуры), *действия*, *события* и так далее.\n\n*Множество* *обозначают* *большой латинской буквой* (`A-Z`), а *элементы множества* - *малыми латинскими буквами* (`a-z`), *перечисляя* их внутри *фигурных скобок* через *запятую*:  \n`A = { x, y, z }`.\n\n*Главными отличительными чертами множества* от *других наборов* являются **уникальность** и **неупорядоченность** его **элементов**.\n\n**Уникальность** означает, что *множество не может содержать двух* (и более) *одинаковых элементов*. При попытке *добавить* уже *существующий элемент множество* должно *остаться неизменным*: `{ x, x } = { x }`. \n\n**Неупорядоченность** означает, что *порядок следования элементов* в *множестве* *не имеет значения*, поэтому *множества* `{ x, y }` и `{ y, x }` считаются *одинаковыми*. \n\n*Примеров множеств* можно придумать бесконечно много: *множество букв латинского алфавита* `{ A, B, C }`, *множество женских имён* `{ Ася, Дина, Сара }`, множество цветов `{ белый, чёрный }`, *множество домашних питомцев* `{ кот, собака, хомяк, кролик }`, *множество языков программирования* `{ JavaScript, Kotlin, Python }` и так далее.\n\n<!--\nОстановимся подробнее на примере с *множеством натуральных чисел* `{ 1, 2, 3 }`. Общим признаком *объектов* (элементов множества) `1`, `2`, `3` выступает *натуральность чисел* (то есть возможность пересчитать с помощью них какие-либо предметы). Эти же объекты могли составлять *множество целых чисел* (общий признак - целостность чисел), *множество чисел* как таковых и так далее.\n-->\n\n## Принадлежность к множеству\n\n**Объект `x` принадлежит множеству** (*содержится* в множестве) **`A`**, если он *является элементом* *множества* `A`. Обозначение: `x ∈ A`. Пример такого множества `A`: `{ x, y }`.\n\n**Объект `x` не принадлежит множеству `A`**, если он *не является элементом множества* `A`. Обозначение: `x ∉ A`. Пример такого множества `A`: `{ y, z }`.\n\nЕщё примеры: символ `i` *принадлежит множествам* символов *белорусского* и *латинского* алфавитов, но *не принадлежит* *множеству* символов *русского* алфавита; число `0` *принадлежит множеству целых* чисел, но *не принадлежит* *множеству* *натуральных* чисел.\n\n## Основные виды множеств\n\n- [Одноэлементное множество](#основные-виды-множеств)\n- [Пустое и универсальное множества](#пустое-и-универсальное-множества)\n- [Конечное и бесконечное множества](#конечное-и-бесконечное-множества)\n\n### Одноэлементное множество\n\n**Одноэлементное множество** (англ. unit set, singleton) — *множество*, *содержащее* всего *один элемент*. \n\nПример *одноэлементного множества*: `E = { 1 }`.\n\n### Пустое и универсальное множества\n\n*Множество*, которое *не содержит ни одного элемента*, называется **пустым множеством** (англ. empty set). \n\n*Пустое множество обозначается* символом `∅` или `{}`.\n\n**Универсальное множество** — *множество*, которое *содержит* в себе *все* остальные *множества* рассматриваемой *задачи*. *Любое множество* в *конкретной задаче* содержится в *универсальном множестве*. \n\n*Универсальное множество обозначается* символом `U`.\n\n### Конечное и бесконечное множества\n\n**Конечным** или **счётным множеством** (англ. finite set) называют *множество*, *содержащее конечное число элементов*.\n\nПримеры *конечных* множеств: *множество булевых значений* `{ 0, 1 }`, *множество основных арифметических операций* `{ +, -, •, ÷ }`, *множество букв алфавита* некоторого *языка*.\n\n**Бесконечным множеством** (англ. infinite set) называют *множество*, содержащее *бесконечное число элементов*.\n\nПример *бесконечного* множества: *множество натуральных чисел* `N = { 1, 2, 3, ... }`. \n\n\n## Мощность (кардинальность) множества\n\n\n**Мощьностью множества `A`** или **кардинальностью** (англ. cardinality) **множества `A`** называют *число элементов* этого *множества*.\n\nОбозначение *мощности множества* `A`, *содержащего* `n` *элементов*: `|A| = n`.\n\nНапример, `|∅| = 0`, `|{ a }| = 1`, `|{ 3, 5 }| = 2` и так далее.\n\n\n\n\n## Отношения между множествами\n- [Пересекающиеся и непересекающиеся множества](#пересекающиеся-и-непересекающиеся-множества)\n- [Подмножество (включение множества)](#подмножество-включение-множества)\n- [Равенство множеств](#равенство-множеств)\n- [Строгое включение множества](#строгое-включение-множества)\n\n### Пересекающиеся и непересекающиеся множества\n\n\n**Множества `A` и `B` пересекаются**, если они *содержат* некоторые *одинаковые* (*общие*) элементы*. То есть множества `A` и `B` *пересекаются*, если *существует* хотя бы один *элемент* `x`, *принадлежащий* и множеству `A`, и множеству `B` *одновременно*.\n\n\n**Множества `A` и `B` не пересекаются**, если у них *нет общих элементов*. То есть *не существует* такого *элемента* `x`, *принадлежащего* и множеству `A`, и множеству `B` *одновременно*.\n\nНапример, *множества символов латинского* и *белорусского алфавитов пересекаются* (имеется *общий элемент* `i`), а *множества символов китайского* и *русского* алфавитов *не пересекаются* (поскольку *не имеют общих элементов*).\n\n### Подмножество (включение множества)\n\n**Множество `A`** называют **подмножеством множества `B`**, если *все элементы* множества `A` *содержатся* в множестве `B`. В этом случае говорят, что множество `A` **содержится** в множестве (**включено в** множество) `B`, а множество `B` **содержит** (**включает** в себя) множество `A`. \n\n*Включение множества* `A` в `B` *обозначается*: `A ⊆ B`. \n\nНапример, *множество деревьев* является *подмножеством множества растений*, а *множество растений* - *подмножеством живых организмов*; *множество планет Солнечной системы* является *подмножеством множества планет нашей галактики* и так далее.\n\n### Задача #1 (подмножества)\n\n«Найти *все подмножества* множества `D = { 3, 6, 9 }`.\n\n#### Ответ\n`∅`, `{ 3 }`, `{ 6 }`, `{ 9 }`, `{ 3, 6 }`, `{ 3, 9 }`, `{ 6, 9 }`, `{ 3, 6, 9 }`\n\n#### Замечание\n*Каждое* из *перечисленных* в ответе *множеств*, а также *множество* `D`, *содержится* в *универсальном множестве* (`⊆ U`).\n\n\n### Равенство множеств\n\n**Множества* `A` и `B` равны**, если *все* их *элементы совпадают*. Иначе говоря, множество `A` *содержит все элементы* множества `B` (`B ⊆ A`), а множество `B` *содержит все элементы* множества `A` (`A ⊆ B`). \n\nОбозначение *равенства множеств* `A` и `B`: `A = B`. \n\nЕсли *совпадают не все элементы* множеств `A` и `B`, то множества `A` и `B` **не равны** между собой. \n\nОбозначение *неравенства множеств* `A` и `B`: `A ≠ B`.\n\n### Строгое включение множества\n\n**Множество `A` строго включено в множество `B`**, если *все элементы* множества `A` *содержатся в* множестве `B` (`A ⊆ B`), но множества `A` и `B` не равны (`A ≠ B`). \n\nОбозначение *строгого включения множества* `A` в множество `B`: `A ⊂ B`.\n\n### Задача #2 (отношения множеств)\n\n«Имеется 4 множества `A = { 1, 3, 5 }`, `B = { 1, 3, 5 }`, `C = { 1, 3 }`, `D = { 5, 7 }`. Как эти *множества относятся* между собой?»\n\n#### Ответ\n`A = B`, `C ⊂ A`, `C ⊂ B`, `A ≠ D`, `B ≠ D`, `C` не пересекается с `D`.\n\n## Способы задания множества\n\n*Множество* считается **заданным**, если *заданы* (известны) *все элементы* этого *множества* или *указан явный способ* их *получения*.\n\n*Задать множество* можно *несколькими способами*:  \n1) При объявлении множества *перечислить все* его *элементы* (это возможно *только* для *конечных множеств*). Пример: `B = { 0, 1 }`.  \n2) При объявлении множества указать **характеристическое свойство** `P(x)` - такое свойство, которым обладает каждый элемент `x` данного множества и не обладает ни один элемент, не входящий в данное множество. Обозначение: `A = { x | P(x) }`. Знак `|` читается “таких, что”, поэтому объявление полностью читается  “множество элементов `x` таких, что они удовлетворяют свойству `P(x)`”. Свойство `P(x)` может задаваться выражением, неравенством, уравнением, а также при помощи слов.  \n3) Задать множество *[графически](#графическое-представление-множеств)*.\n\n### Примеры характеристических свойств\n\nПример *задания множества неравенством*: `K = { x | 3 < x < 7 }` - *множество элементов*`x` таких, что они *больше трёх* и *меньше семи*. В множество `K` попадают как целые числа `4`, `5` и `6`, так и бесконечное множество дробных чисел заданного промежутка по типу `3.14`, `5.7`, `6.333`. \n\nПример *задания множества выражением*, *содержащим* уже *существующий элемент* множества: `N = { 1 ∈ N | \"если x ∈ N, то (x + 1) ∈ N\" }` - *множество натуральных чисел* (`1` - первый элемент, каждый последующий элемент на единицу больше предыдущего).\n\nЕщё один пример *задания множества выражением*: `J = { x | \"если x ∈ J, то -x ∈ J\" }` - *множество симметричных* относительно нуля *элементов*. Например, если *добавить* в множество `J` число `3`, то в него *добавляется* также и `-3`.\n\nПример *задания множества уравнением*. Задание *графика* `G` *функции* `f: X → Y`:  \n`G = { (x,y) ∈ X × Y | f(x) = y }`.\n\n## Графическое представление множеств\n- [Диаграмма Эйлера](#диаграмма-эйлера)\n- [Диаграмма Венна](#диаграмма-венна)\n\n### Диаграмма Эйлера\n\n**Диаграмма Эйлера** - геометрическая *схема*, с помощью которой можно *наглядно изображать отношения множеств* (подмножество и пересечение множеств).\n\nНа *диаграммах Эйлера множества* обычно *представлены кругами* (но могут быть использованы и другие простые фигуры), отчего *диаграммы Эйлера* также называют **кругами Эйлера**.\n\n*Один круг* представляет *одно множество*.\n\nЕсли *круги двух множеств пересекаются*, то эти *множества имеют общие* (одинаковые) *элементы*.\n\nЕсли *круги двух множеств не пересекаются*, то эти *множества не имеют общих* (одинаковых) *элементов*.\n\nЕсли *круг одного множества* полностью *размещён внутри круга другого множества*, то *соответствующее* первому кругу *множество* является *подмножеством множества*, соответствующего второму кругу.\n\n\n### Диаграмма Венна\n\n**Диаграмма Венна** (также диаграмма Эйлера — Венна) — схематичное *изображение основных операций* (объединение, пересечение, разность, симметрическая разность) *над* несколькими *множествами*.\n\nКак мы уже знаем, *каждое множество* в *конкретной задаче* является *подмножеством универсального множества* `U`, поэтому *построение диаграммы Венна начинается* с *построения прямоугольника*, представляющего собой *универсальное множество*.\n\nВсе *остальные множества* задачи *изображаются простыми фигурами* (обычно кругами) *внутри прямоугольника*. *Внутри* этих *фигур* изображены *элементы множества*. Если *множества имеют одинаковые элементы* (пересекаются), то эти *элементы* размещают в *пересечении фигур*.\n\n## Число подмножеств\n\n*Множество всех подмножеств множества* `A` называется **булеаном** (англ. power set, powerset).\n\n**Число всех подмножеств** (*мощность булеана*), некоторого **множества `A` мощности `n`** равно: **`2^n`**. \n\n*Формула выше доказывается комбинаторно*. На *каждый элемент* множества `A` приходится по *2 возможных исхода* (случая): “*элемент* *содержится* в *некотором подмножестве*” (*обозначим* `1`), “*элемент не содержится* в *некотором подмножестве*” (*обозначим* `0`). Для `n` *элементов* по *правилу произведения* имеем `2^n` *исходов*, а значит и `2^n` *подмножеств*.\n\nПусть `A = { x, y, z }`, тогда `n = |A| = 3` и *существует* `2^3 = 8` *подмножеств* множества `A`:\nx | y | z | множество\n:--: | :--: | :--: | :--:\n0 | 0 | 0 | ∅\n0 | 0 | 1 | { z }\n0 | 1 | 0 | { y }\n0 | 1 | 1 | { y, z }\n1 | 0 | 0 | { x }\n1 | 0 | 1 | { x, z }\n1 | 1 | 1 | { x, y, z }\n\n**Число всех подмножеств мощности `m`** некоторого **множества `A` мощности `n`** равно *биноминальному коэффициенту*: **`С(n,k) = n! ÷ (k!(n-k)!)`**. \n\nВ *комбинаторике* это *эквивалентно выборке* `k` *элементов* из `n` *уникальных элементов*.\n\nПусть `A = { h, o, p, e }` и `m = 2`, тогда `n = |A| = 4` и *существует* `4! ÷ (2!2!) = (3 • 4) ÷ 2 = 6` *подмножеств мощности* `2` множества `A`: `{ h, o }`, `{ h, p }`, `{ h, e }`, `{ o, p }`, `{ o, e }`, `{ p, e }`.\n\n## Пара и кортеж\n- [Неупорядоченная пара](#неупорядоченная-пара)\n- [Упорядоченная пара](#упорядоченная-пара)\n- [Кортеж (упорядоченный набор элементов)](#кортеж-упорядоченный-набор-элементов)\n\n### Неупорядоченная пара\n\n**Неупорядоченной парой** (англ. unordered pair), иногда просто **парой** (англ. pair set), называют *множество*, *состоящее* из *двух элементов*.\n\n*Неупорядоченная пара объектов* `x` и `y` *обозначается*: `{ x, y }`.\n\nПоскольку *порядок следования элементов* в *множестве не важен*, *пары* `{ x, y }` и `{ y, x }` считаются *одинаковыми*.\n\nНесмотря на то, что по *определению* *пара* - это *множество*, *наборы* вроде `{ x, x }` так же иногда *называют парами*, хоть они и являются *мультимножествами*. \n\n### Упорядоченная пара\n\n**Упорядоченной парой** (англ. ordered pair) называют такой *набор из двух объектов*, в котором *порядок следования* этих *объектов имеет значение*.\n\n*Упорядоченная пара* объектов `x` и `y` *обозначается* `(x, y)`.  \n\nПо *определению* для *упорядоченной пары объектов* `x` и `y` *справедливо*: `(x, y) ≠ (y, x)`. \n\n\n*Элемент* `x` называют **первым элементом** (англ. first entry), **первой координатой** (англ. first coordinate) или **первой компонентой** (англ. first component) **упорядоченной пары `(x, y)`**, *элемент* `y` – **вторым элементом** (англ. second entry), **второй координатой** (англ. second coordinate) или **второй компонентой** (англ. second component) **упорядоченной пары `(x, y)`**.\n\n\n*Порядок следования* элементов `x` и `y` в *круглых скобках* так же *важен* для *упорядоченной пары*, как *порядок букв* в *словах* (`«да»` и `«ад»`), *порядок цифр* в *числах* (`18`и `81`) и так далее.\n\n\n#### Определение упорядоченной пары в теории множеств\n\nПусть *задана неупорядоченная пара* `{ x, y }` элементов `x` и `y`. Множество `{ { x }, { x, y } }` называют **упорядоченной парой элементов `x` и `y` с первым элементом `x`**, а множество `{ { y }, { x, y } }` называют **упорядоченной парой элементов `x` и `y` с первым элементом `y`**. \n\n### Кортеж (упорядоченный набор элементов)\n\nПонятие *кортежа* является *обобщением* понятия *упорядоченной пары*.  \n\nПусть *заданы* `n` множеств `A1, A2, …, An` (*не обязательно различных*: некоторые из них *могут повторяться*). **Кортежем длины** `n` называют *упорядоченный набор* из `n` *элементов* `x1 ∈ A1, x2 ∈ A2, …, xn ∈ An`.\n\nЭлемент `x1` называют **первым элементом** (первой координатой, компонентой) *кортежа*, элемент `x2` - **вторым элементом** (второй координатой, компонентой) и так далее.\n\n*Обозначение кортежа* из `n` элементов:  \n`(x1, x2, …, xn)`.\n\nПо определению, *нельзя менять элементы* `x1, x2, …, xn` *местами* внутри круглых скобок, иначе *получится другой кортеж*:  \n`(x1, x2, …, xn) ≠ (x2, x1, …, xn)`.\n\n*Кортеж длины 2* также называют **упорядоченной парой**, *кортеж длины 3* - **упорядоченной тройкой** и так далее.\n\n**Два кортежа** `(x1, x2, …, xn)` и `(y1, y2, …, ym)` **равны** лишь тогда, когда *равны* их *длины* (`n = m`) и *попарно равны все* их *элементы* (`x1 = y1, x2 = y2, …, xn = yn`).\n\n*Кортеж длины 3* `(x1, x2, x3)` *представляет* собой *упорядоченную пару*, *состоящую кортежа длины 2* `(x1, x2)` и *элемента* `x3`: `(x1, x2, x3) = ((x1, x2), x3)`. Зная это и *обозначение упорядоченной пары* на *языке множеств*: `(x, y) = { { x }, { x, y } }`, можно *последовательно получить обозначение кортежа любой длины* на *языке множеств*.\n\n## Операции над множествами\n- [Об операциях над множествами](#об-операциях-над-множествами)\n- [Пересечение множеств](#пересечение-множеств)\n- [Объединение множеств](#объединение-множеств)\n- [Разность множеств](#разность-множеств)\n- [Симметрическая разность множеств](#симметрическая-разность-множеств)\n- [Декартово произведение множеств](#декартово-произведение-множеств)\n- [Дополнение множества](#дополнение-множества)\n- [Свойства операций над множествами](#свойства-операций-над-множествами)\n- [Приоритет операций над множествами](#приоритет-операций-над-множествами)\n\n### Об операциях над множествами\n\nБольшинство *операций над множествами* либо *бинарные*, либо *унарные*.\n\n**Бинарная операция** (двуместная операция) — *математическая операция*, *принимающая два аргумента* и *возвращающая один результат*.\n\nСоответственно, **бинарная операция над множествами** *принимает два множества* и *возвращает одно*.\n\n*Бинарные операции над множествами*:\n- [Пересечение множеств](#пересечение-множеств)\n- [Объединение множеств](#объединение-множеств)\n- [Разность множеств](#разность-множеств)\n- [Симметрическая разность множеств](#симметрическая-разность-множеств)\n- [Декартово произведение множеств](#декартово-произведение-множеств)\n\nАналогично, **унарная операция над множеством** *принимает одно множество* и *возвращает одно множество*.\n\n*Унарные операции над множествами*:\n- [Дополнение множества](#дополнение-множества)\n\n### Пересечение множеств\n\n**Пересечением множеств** `A` и `B` называют *множество элементов*, принадлежащих и *множеству* `A`, и *множеству* `B` *одновременно*.\n\n*Обозначение пересечения множеств* `A` и `B`:  \n`A ∩ B = { x | \"x ∈ A и x ∈ B\" }`.\n\nЕсли у *множеств* `A` и `B` *нет общих элементов*, то их *пересечение равно пустому множеству*: `A ∩ B = ∅`.\n\n<!-- Как уже отмечалось ранее, множества букв латинского и белорусского алфавитов пересекаются. Обозначим их буквами -->\n\n### Объединение множеств\n\n\n**Объединением множеств** `A` и `B` называют *множество*, содержащее *все элементы* множества `A` *и все элементы* множества `B`.\n\n*Обозначение объединения множеств* `A` и `B`:  \n`A ∪ B = { x | \"x ∈ A или x ∈ B\" }`.\n\n\nЕсли известно, что *множества* `A` и `B` *не пересекаются* (`A ∩ B = ∅`), то *объединение* можно *обозначить* знаком *сложения*: `A + B = A ∪ B`.\n\n*Мощность объединения множеств* `A` и `B` можно *рассчитать*, используя *[принцип включений-исключений](#принцип-включений-исключений)*:\n`|A ∪ B| = |A| + |B| - |A ∩ B|`.\n\n### Задача #3 (пересечение и объединение)\n«Заданы множества `A = { 1, 3, 5, 7 }`, `B = { 3, 5, 9 }`, `С = { 8, 9 }`. Найти все комбинации объединения и пересечения данных множеств.»\n\nОтвет: `A ∩ B = { 3, 5 }`, `A ∪ B = { 1, 3, 5, 7, 9 }`, `A ∩ C = ∅`, `A ∪ C = { 1, 3, 5, 7, 8, 9 }`, `B ∩ C = { 9 }`, `B ∪ C = { 3, 5, 8, 9 }`, `A ∩ B ∩ C = ∅`, `A ∪ B ∪ C = { 1, 3, 5, 7, 8, 9 }`.\n\n### Разность множеств\n\n**Разностью множеств** `A` и `B` называется *множество*, которое *содержит* только те *элементы* из множества `A`, которые *не содержатся* в множестве `B`.\n\n*Обозначение разности множеств* `A` и `B`:  \n`A / B = { x | \"x ∈ A и x ∉ B\" }`.\n\nEсли множества `A` и `B` *не пересекаются* (`A ∩ B = ∅`), то для их *разности* справедливо:  \n1) `A / B = A`,  \n2) `B / A = B`.\n\nEсли `A ⊆ B`, то `A / B = ∅`.\n\n### Симметрическая разность множеств\n\n**Симметрической разностью множеств** `A` и `B` называется *множество*, которое *содержит* только те *элементы* из множества `A`, которые *не содержатся* в множестве `B`, и только те элементы из множества `B`, которые не содержатся в множестве `A`.\n\n*Обозначение cимметрической разности множеств* `A` и `B`:  \n`A Δ B = { x | \"(x ∈ A и x ∉ B) или (x ∈ B и x ∉ A)\" }`.\n\nФактически, *симметрическая разность* множеств `A` и `B` *эквивалентна* следующим *выражениям*:  \n`A Δ B = (A / B) ∪ (B / A) = (A ∪ B) / (A ∩ B)`.\n\nEсли `A = B`, то `A Δ B = ∅`.\n\n### Задача #4 (разность и симметрическая разность)\n«Заданы множества `A = { 1, 2, 3 }`, `B = { 0, 1 }`. Найти их разность и симметрическую разность.»\n\nОтвет: `A / B = { 2, 3 }`, `A Δ B = { 0, 2, 3 }`.\n\n### Декартово произведение множеств\n\n**Декартово** (прямое) **произведение множеств** `A` и `B` — *множество всех упорядоченных пар элементов* множеств `A` и `B`. *Каждый элемент* из множества `A` *ставится в соответствие* (пару) с *каждым элементом* их множества `B`.\n\nОбозначение *декартова произведения* множеств `A` и `B`:  \n`A × B = { (x, y) ∣ \"x ∈ A и y ∈ B\" }`.\n\nОсновные *свойства декартова произведения* `A × B`:\n1) `(A × B) ≠ (B × A)`, поскольку для *упорядоченых пар справедливо* `(x, y) ≠ (y, x)`.  \n2) `|A × B| = |A|•|B|`, то есть *количество пар* в *декартовом произведении* `A × B` *равняется произведению количества элементов* множества `A` *на количество элементов* множества `B`.  \n3) Если *все множители декартова произведения* - *конечные множества*, то *результатом произведения* станет так же *конечное множество*. Если *хотя бы один множитель* - *бесконечное множество*, то *результат произведения* - так же *бесконечное множество*.\n\n*Декартово произведение трёх* (и более) множеств `A`, `B`, `C` составляется *аналогично*, при этом *элементами произведения* `A × B × C` будут *всевозможные упорядоченные тройки* (кортежи длины `3`) :  \n`A × B × C = { (x, y, z) ∣ \"x ∈ A, y ∈ B и z ∈ C\" }`.\n\n*Декартово произведение не коммутативно*: `(A × B) × C ≠ A × (B × C)`.\n\n### Задача #5 (декартово произведение)\n«Заданы множества `A = { x, y, z }`, `B = { 0, 1 }`. Найти декартовы произведения `A × A`, `A × B`, `B × A`, `B × B`.»\n\nОтвет:  \n`A × A = { (x, x), (x, y), (x, z), (y, x), (y, y), (y, z), (z, x), (z, y), (z, z) }`,   \n`A × B = { (x, 0), (x, 1), (y, 0), (y, 1), (z, 0), (z, 1) }`,  \n`B × A = { (0, x), (0, y), (0, z), (1, x), (1, y), (1, x) }`,   \n`B × B = { (0, 0), (0, 1), (1, 0), (1, 1) }`.\n\n### Дополнение множества\n\nЕсли из контекста задачи следует, что *все рассматриваемые* в ней *множества являются подмножествами* некоторого заданного *универсального множества*, то вводится *унарная операция дополнения*.\n\n**Дополнение множества** `A` - такое множество, которое *дополняет* множество `A` до *универсального множества* `U`, то есть *содержит* все те *элементы универсального множества* `U`, которые *не содержатся* в `A`.\n\nОбозначение *дополнения множества* `A`:\n`¬A = U / A = { x ∣ “x ∉ A и x ∈ U” }`.\n\nОперация *дополнения множества* *близка* по смыслу к операции *логического отрицания*.\n\n*Свойства* операции *дополнения множества* `A`:  \n1) У *множества* и его *дополнения нет пересечений*: `A ∩ ¬A = ∅`.  \n2) *Объединение множества* и его *дополнения составляет универсальное множество*: `A ∪ ¬A = U`.\n3) *Двойное дополнение* множества *составляет само множество*: `¬¬A = A`.\n\n### Свойства операций над множествами\n\n<!--\n«Какими свойствами обладают следующие операции над множествами: `∩`, `∪`, `/`, `Δ`, `×`, `¬` ?»\n∩ ∪ / Δ × ¬\n-->\n\n*Рекомендуется* сперва прочесть: «[Свойства бинарных операций](#свойства-бинарных-операций)».\n\n*Обозначим символом* `◇` *произвольную операцию над множествами*, тогда при изучении свойств *обозначение* `◇ ∈ { ∩, ∪ }` будет *означать*, что *свойство выполняется для операций* `∩` и `∪`.\n\n1) **Идемпотентность**: `A ◇ A = A , ◇ ∈ { ∩, ∪ }`.  \n2) **Коммутативность**: `A ◇ B = B ◇ A , ◇ ∈ { ∩, ∪, Δ }`.  \n3) **Ассоциативность**: `(A ◇ B) ◇ C = A ◇ (B ◇ C) , ◇ ∈ { ∩, ∪, Δ }`.  \n4) **Дистрибутивность**  \n - *пересечения относительно объединения*: `A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C)`,\n - *пересечения относительно симметрической разности*: `A ∩ (B Δ C) = (A ∩ B) Δ (A ∩ C)`,\n - *объединения относительно пересечения*: `A ∪ (B ∩ C) = (A ∪ B) ∩ (A ∪ C)`,\n5) **Свойства пустого множества** `∅`:\n - `A ∪ ∅ = A`,\n - `A ∩ ∅ = ∅`,\n - `∅ / A = ∅`,\n - `A / ∅ = A`,\n - `A Δ ∅ = A`,\n - `A × ∅ = ∅`,\n - `∅ × A = ∅`,\n - `A / A = ∅`,\n - `A Δ A = ∅`.\n6) **Свойства унарного дополнения** `¬`:\n - `¬∅ = U`,  \n - `¬U = ∅`,\n - `¬¬A = A`,\n - `A ∪ ¬A = U`,\n - `A ∩ ¬A = ∅`,\n - `A / ¬A = A`,\n - `A Δ ¬A = U`.\n7) **[Принцип включений-исключений](#принцип-включений-исключений)**: `|A ∪ B| = |A| + |B| - |A ∩ B|`.\n\n### Приоритет операций над множествами\n\n*Рекомендуется* сперва прочесть: «[Приоритет и очерёдность операций](#приоритет-и-очерёдность-операций)».\n\n*Последовательность выполнения операций над множествами* может быть задана **круглыми скобками** `()`.\n\nЕсли в *выражении* с несколькими *различными операциями* *скобки отсутствуют*, то *порядок* (приоритет) *выполнения операций над множествами* следующий:  \n1) **Унарные операции**: *дополнение* (`¬A`).\n2) **Пересечение** (`A ∩ B`). \n3) **Остальные бинарные операции**: *объединение* (`A ∪ B`), *разность* (`A / B`) и *симметрическая разность* (`A Δ B`).\n\n*Операции над множествами одного приоритета выполняются слева направо*. \n\n*Декартово произведение* `×` используется несколько для *других целей*, поэтому обычно *не появляется* в одних и *тех же выражениях* с *остальными операциями над множествами* и *в «гонке приоритетов» не участвует*.\n\n## Углубленно об отношении\n- [Об отношении и примеры отношений](#об-отношении-и-примеры-отношений)\n- [Бинарное отношение](#бинарное-отношение)\n- [Свойства бинарных отношений](#свойства-бинарных-отношений)\n- [Задача (свойства отношений между множествами)](задача-свойства-отношений-между-множествами)\n- [Область определения и область значений отношения](#область-определения-и-область-значений-отношения)\n- [Композиция бинарных отношений](#композиция-бинарных-отношений)\n\n### Об отношении и примеры отношений\n\nГоворя простыми словами, **отношение** — такая *математическая структура*, которая *выражает* то, *как два* или более каких-либо *объектов относятся* друг к другу, *как* (чем) они друг с другом *связаны*.\n\nНапример, *равенство* `x = y` является одним из видов *отношений* между *объектами* `x` и `y`, *параллельность* `a || b` является одним из видов *отношений* между *прямыми* `a` и `b` и так далее.\n\nПусть *заданы* `n` множеств `A1, A2, …, An` (*не обязательно различных*: некоторые из них *могут повторяться*). Тогда **n-арным** (n-местным) **отношением** `R`, *заданном на множествах* `A1, A2, …, An`, называется *подмножество декартового произведения этих множеств* (`R ⊆ A1 × A2 × … An`). \n\n*Отношения обладают* некоторыми *свойствами* и *наделяют* ими свои *элементы* (упорядоченные пары), но об этом *чуть позже*.\n\n*Отношения*, как и другие множества, *обозначают большими латинскими буквами* (часто `R`, `S`, `T`).\n\n*Связь элементов* `x1 ∈ A1, x2 ∈ A2, …, xn ∈ An` *n-арным отношением* `R` *обозначается двумя способами*:  \n1) `R(x1, x2, …, xn)`,  \n2) `(x1, x2, …, xn) ∈ R`.\n\nПри `n = 1` *отношение* называется **унарным**. Оно *отождествляется* со *свойством* одного *объекта* и обычно *не рассматривается*.\n\nПри `n = 2` *отношение* называется **бинарным**. Этот вид отношений *встречается чаще всего*, поэтому далее мы уделим ему *особое внимание*.\n\nПри `n = 3` *отношение* называется **тернарным** и так далее.\n\n### Бинарное отношение\n\n**Бинарное** (двухместное) **отношение** `R` — *отношение между двумя множествами* `A` и `B`, то есть всякое *подмножество декартова произведения* множеств `A` и `B`: `R ⊆ A × B`.\n\n*Связь элементов* `x ∈ A` и `y ∈ B` *бинарным отношением* `R` обычно *обозначают* так: `xRy`, но записи `R(x, y)` и `(x,y) ∈ R` из *определения n-арного отношения* так же *применимы*. \n\n**Бинарное отношение** `R` **на множестве** `A` — *отношение множества* `A` самого с собой, то есть *любое подмножество декартова произведения* множества `A` самого с собой `R ⊆ A^2 = A × A`. \n\n*Бинарные отношения на множестве чаще всего используются* в математике. Такими являются *равенство*, *неравенство*, *эквивалентность* и многие другие. \n\n\nДля *полного понимания* ещё раз взглянем на *определение*. Вспомним, что *декартово произведение* `A × B` - это *множество всех упорядоченных пар* `(x, y)` элементов `x ∈ A` и `y ∈ B`. Значит некоторое *подмножество* `R` *декартового произведения* - множество, *содержащее определённое количество* этих *упорядоченных пар*. Запись `(x, y) ∈ R` наглядно показывает *принадлежность упорядоченной пары* к множеству `R`. Эта принадлежность и означает, что элементы `x` и `y` *связаны отношением* `R`. \n\n### Свойства бинарных отношений\n<!--\nВ данном разделе используются символы математической логики (с пояснениями). \n\nРекомендуется ознакомиться до прочтения: Квантификаторы (математическая логика).\n-->\n\n\n*Бинарное отношение* `R`, *заданное* на множестве `A`, может *обладать некоторыми* из *следующих свойств*:  \n* **Рефлексивность**: `∀x ∈ A : (xRx)`, то есть *для любого* элемента `x` из множества `A` *справедливо*, что `x` *связан отношением* `R` *сам с собой* (`xRx`). Пример: `x = x`.\n* **Антирефлексивность**: `∀x ∈ A : ¬(xRx)`, то есть *для любого* элемента `x` из множества `A` *справедливо*, что `x` *не связан отношением* `R` *сам с собой* (`¬(xRx)`). Пример: *неверно*, что `x < x`.\n* **Симметричность**: `∀x, y ∈ A : (xRy ⇒ yRx)`, то есть *для любых* элементов `x` и `y` из множества `A` *справедливо*, что из *выполнения* `xRy` *следует выполнение* `yRx`. Пример: если `x = y`, то `y = x`.\n* **Асимметричность**: `∀x, y ∈ A : (xRy ⇒ ¬(yRx))`, то есть *для любых* элементов `x` и `y` из множества `A` *справедливо*, что из *выполнения* `xRy` *следует невыполнение* `yRx` (`¬(yRx)`). Пример: если `x < y`, то *неверно*, что `y < x`.\n* **Антисимметричность**: `∀x, y ∈ A : (xRy ∧ yRx ⇒ x = y)`, то есть *для любых* элементов `x` и `y` из множества `A` *справедливо*, что из *выполнения* `xRy` и `yRx` следует, что `x = y`. Пример: если `x ≤ y` и `y ≤ x`, то `x = y`.\n* **Транзитивность**: `∀x, y, z ∈ A : (xRy ∧ yRz ⇒ xRz)`, то есть *для любых* элементов `x`, `y` и `z` из множества `A` *справедливо*, что из *выполнения* `xRy` и `yRz` *следует справедливость* `xRz`. Пример: если `x = y` и `y = z`, то `x = z`.\n\n*Не все свойства отношений совместимы* друг с другом: *отношение не может* быть *одновременно симметричным* и *асимметричным*, *рефлексивным* и *антирефлексивным*.\n\n**Отношение эквивалентности** — *бинарное отношение* `R` между объектами `x` и `y`, которое *рефлексивно*, *симметрично* и *транзитивно*. \n\nНапример, отношение `=` является *отношением эквивалентности*, поскольку *для любых* объектов `x`, `y` и `z` выполняется:  \n1) `x = x`,  \n2) `(x = y) ⇒ (y = x)`,  \n3) `(x = y) ∧ (y = z) ⇒ (x = z)`.\n\n**Отношение порядка** — *бинарное отношение* `R` между объектами `x` и `y`, которое *обладает лишь некоторыми* из *свойств отношения эквивалентности*.\n\nНапример, на *множестве вещественных чисел* отношения `≤` (рефлексивно и транзитивно, но не симметрично), `≠` (симметрично и транзитивно, но не рефлексивно), `<` (транзитивно, но не рефлексивно и не симметрично) являются *отношениями порядка*. \n\n*Отношения не обязательно обозначаются символами*, *связь объектов* можно *передать словами*. Например, `a - потомок b`, `m левее n`, `x севернее y` - все перечисленные *отношения антирефлексивны, асимметричны*, но *транзитивны*, а вот *отношение* `отец` к тому же *не транзитивно* (если `x - отец y`, то `y не отец x`).\n\n*Множества* можно *рассматривать как элементы другого множества*, поэтому *сказанное выше* также *применимо* и к *отношениям между множествами*. \n\n\n### Задача (свойства отношений между множествами)\n«Какими свойствами обладают отношения, отвечающие за равенство, наличие пересечений и включение множеств?»\n\nОтвет:\n\nВыше уже *было показано*, что отношение `=` для элементов `x` и `y` является *отношением эквивалентности*, а отношение `≠` - *отношением порядка*. Это так же *справедливо* и *для* множеств `A` и `B`.\n\nОтношение `пересекается с` является *отношением эквивалентности*:\n1) *Рефлексивно*: *верно*, что `A пересекается с A` (есть общие элементы).  \n2) *Симметрично*: если `A пересекается с B`, то *верно*, что `B пересекается с A`.  \n3) *Транзитивно*: если `A пересекается с B`, `B пересекается с C`, то *верно*, что `A пересекается с C`.  \n\nОтношение `не пересекается с` является *отношением порядка*:\n1) *Антирефлексивно*: *неверно*, что `A не пересекается с A` (есть общие элементы).  \n2) *Симметрично*: если `A не пересекается с B`, то *верно*, что `B не пересекается с A`.  \n3) *Транзитивно*: если `A не пересекается с B`, `B не пересекается с C`, то *верно*, что `A не пересекается с C`.  \n\nОтношение `⊆` (`включено в `, `является подмножеством`) является *отношением порядка*:\n1) *Рефлексивно*: *верно*, что `A ⊆ A`.  \n2) *Асимметрично*: если `A ⊆ B`, то *неверно*, что `B ⊆ A`.  \n3) *Транзитивно*: если `A ⊆ B`, `B ⊆ C`, то *верно*, что `A ⊆ C`.  \n\nОтношение `⊂` (`строго включено в `) является *отношением порядка*:\n1) *Антирефлексивно*: *неверно*, что `A ⊂ A`.  \n2) *Асимметрично*: если `A ⊂ B`, то *неверно*, что `B ⊂ A`. \n3) *Транзитивно*: если `A ⊂ B`, `B ⊂ C`, то *верно*, что `A ⊂ C`. Пример: `{ 1 } ⊂ { 1, 2 } ⊂ { 1, 2, 3 }`.\n\n\n### Область определения и область значений отношения\n\n*Множество* всех *первых компонент* `x` *упорядоченных пар* `(x, y)` из множества `R` называют **областью определения отношения** `R`.\n\n*Обозначение области определения*: `Dom R = { x ∣ ∃y ((x, y) ∈ R) }`, то есть *множество таких* элементов `x`, *для которых найдутся такие* элементы `y`, что *упорядоченная пара* `(x, y)` будет *принадлежать* множеству `R`.\n\n*Множество* всех *вторых компонент* `y` *упорядоченных пар* `(x, y)` из множества `R` называют **областью значений отношения** `R`.\n\n*Обозначение области значений*: `Im R = { y ∣ ∃x ((x, y) ∈ R) }`, то есть *множество таких* элементов `y`, *для которых найдутся такие* элементы `x`, что *упорядоченная пара* `(x, y)` будет *принадлежать* множеству `R`.\n\nТаким образом, запись `xRy` означает *связь отношением* `R` *между* элементом `x` из *области определения* `Dom R` и элементом `y` из *области значений* `Im R`.\n\n### Композиция бинарных отношений\n\n\n**Композицией** (произведением, суперпозицией) **бинарных отношений** `R ⊆ A × B` и `S ⊆ B × C` (composition of binary relations) называется *такое отношение* `(R ○ S) ⊆ A × C`, что *для любых* элементов `a ∈ A, c ∈ C` *существует связь* `a(R ○ S)c ` *тогда и только тогда*, когда *существует* элемент `b ∈ B` такой, что *одновременно существуют* связи `aRb` и `bSc`. \n\nЗапись *композиции отношений* `R` и `S` на *языке символов*: `∀a∈A,∀c∈C: a(R ○ S)c ⇔ ∃b∈B: (aRb)∧(bSc)`.\n\n\n## Функция (отображение)\n- [Функция одной переменной](#функция-одной-переменной)\n- [Область определения и область значений функции](#область-определения-и-область-значений-функции)\n- [Непрерывная, разрывная и дискретная функции](#непрерывная-разрывная-и-дискретная-функции)\n- [Сюръективность, инъективность и биективность](#сюръективность-инъективность-и-биективность)\n- [Функция нескольких переменных](#функция-нескольких-переменных)\n- [Композиция функций](#композиция-функций)\n- [Индикаторная функция](#индикаторная-функция)\n\n### Функция одной переменной\n\n**Функция одной переменной** (англ. function) — это такое *бинарное отношение* `R ⊆ X × Y`, что *каждому* элементу `x ∈ X` *соответствует единственный* элемент `y ∈ Y`. \n\n*Функцию именуют* *малыми латинскими буквами* (обычно `f`, `h`, `g`). \n\n*Элемент* `x ∈ X` называют **аргументом функции** `f` (англ. the argument of the function f), а *элемент* `y ∈ Y` - **значением** **функции** `f` (англ. the value of the function f). \n\nГоворят, что *функция* `f` *принимает аргумент* `x` и *возвращает значение* `y`.\n\n<!--\nInput, output\n-->\n\n*Обозначение функции* `f`, *принимающей аргумент* `x` и *возвращающей значение* `y`: **`f(x) = y`**. Так же *допустимы обозначение* **`y = f(x)`** и *сокращённые обозначения*: **`f(x)`**, **`f`**.\n\n#### Функциональное бинарное отношение `R`\n«*Пусть* `x ∈ X`, `y, z ∈ Y`, тогда *бинарное отношение* `R` называют **функциональным**, если *выполняется* **`(xRy ∧ xRz) → (y = z)`**, то есть, если *одновременно существуют связи* `xRy` и `xRz`, *не может существовать двух разных значений* `y` и `z` для *одного аргумента* `x` и тогда `y = z`.»\n\n#### Отображение\n*Функцию* `f(x) = y, x ∈ X, y ∈ Y` также называют **отображением множества `X` в множество `Y`** и *обозначают* **`f: X → Y`**.\n\nГоворят, что *функция* `f` **отображает** *множество* `X` *в множество* `Y`.  \n \n\n### Область определения и область значений функции\n\nМножество `X` называют **областью определения** или **областью задания** **функции** `f` (англ. the domain of the function f) и *обозначают* **`D(f)`**.\n\nМножество `Y` называют **областью значений функции** `f` (англ. the codomain of the function f).\n\n*Множество таких* элементов `y ∈ Y`, *для которых существует упорядоченная пара* `(x, y) ∈ f`, `x ∈ X`, называют **множеством значений функции** `f` и *обозначают* **`E(f)`**.\n\nГоворят, что **функция задана** (определена) **на множестве** `X` и **принимает значения из множества** `Y`. \n\n<!--\nТаким образом, *функция* позволяет *по заданному* (переданному ей) аргументу `x` *однозначно определить* значение `y`.\n-->\n\n### Способы задания функции\n\n### Непрерывная, разрывная и дискретная функции\n\nВ *математике* чаще всего рассматриваются *числовые функции*. \n\n**Числовая функция** - это *функция*, которая *ставит* одни *числа в соответствие* другим *числам*, то есть *принимает числовой аргумент* `x` и *возвращает числовое значение* `y`. \n\nГоворя простыми словами, **непрерывная функция** - это такая *функция*, у которой *между двумя любыми двумя значениями* `y1` и `y3` *всегда найдётся ещё одно значение* `y2` при *некотором аргументе* `x2`. Например, *между* значениями `y1 = 1` и `y3 = 2` *существует* значение `1.5`, между `1` и `1.5` - `1.25`, между `1` и `1.25` - `1.125` и так далее.\n\n*Графически непрерывная функция* представляется *непрерывной линией*.\n\nПонятно, что для *непрерывности функции необходимо*, чтобы *область значений* была *бесконечным множеством*, *иначе средняя точка* на каком-то этапе *не найдётся*.\n\nНапример, *линейная функция* `f(x) = 3x - 7, x ∈ R`, *графиком* которой является прямая*, *непрерывна*.\n\nНапример, функция, возвращающая *модуль числа*, *непрерывна*:\n```\nf(x) = |x| = {\n  x, x >= 0;\n -x, x < 0;\n}\n```\n\nЕсли *между некоторыми двумя значениями* `y1` и `y3` *не существует среднего значения* `y2`, то *функция* называется **разрывной функцией**, то есть *функцией*, *имеющей* **точки разрыва**. \n\n<!--\nПри наличии разрывов в функции появляются условия\n\nОбозначение разрывной функции\n-->\n\n\n*Функция* может быть *разрывна в одной точке* и *непрерывна* во всех *остальных*. Например, функция `sgn(x): R → { -1, 0, 1 }`, *возвращающая знак числа*, *разрывна* лишь в *одной точке* `0`:\n```\nsgn(x) = {\n  1, x > 0;\n  0, x = 0;\n -1, x < 0;\n}\n```\n\n\nЕсли *область значений* и *область определения функции* являются *конечными множеством*, то эта *функция* **разрывна во всех точках**. *Такую функцию* будем называть **дискретной функцией**.\n\n*Непрерывные* и *разрывные в некоторых точках функции* изучаются в *курсе математического анализа*. Эти понятия затрагивают *бесконечные множества* и *далее* мы их *рассматривать не будем*, поскольку *дискретная математика* занимается лишь *дискретными объектами*.\n\n<!--\n*Пример числовой функции* `f`, *заданной* на *множестве чисел* из *промежутка* `[-1,1]` и *принимающей значения* из *множества действительных чисел* `R`: `f: [-1, 1] → R, f(x) = 4x - 1`. Если *передать* в функцию `f` *аргумент* `x = 0.5`, получим значение `y = f(0.5) = 3`.\n-->\n\n*Пример дискретной функции* `g`, *заданной* на *множестве* `{ 0, 1 }` и *принимающей значения* из множества `{ ложь, истина }`:  \n```\ng(x) = {\n  ложь, x = 0;\n  истина, x = 1;\n}\n```\n\n<!--\nВ *первом примере* множества `[-1,1]` и `R` являются *бесконечными* (бесконечно много элементов), во *втором примере* множества `{ 0, 1 }` и `{ ложь, истина }` *конечны*.\n-->\n\n### Сюръективность, инъективность и биективность\n\nФункция `f` называется **сюръективной** (*сюръекцией*), если *каждому элементу* `y ∈ Y` может быть *поставлен в соответствие хотя бы один* элемент `x ∈ X`. Иначе говоря, *не существует* таких `y ∈ Y`, которым *не соответствует хотя бы один* `x ∈ X`.\n\n*Сюръективное отображение допускает существование двух разных* аргументов `x1, x2 ∈ X`, которым *соответствует* один и *тот же* `y ∈ Y` (`y = f(x1) = f(x2)`). Таким образом, если множество `X` *содержит больше элементов*, чем множество`Y`, то *отображение* всё ещё *может быть сюръективным*. Но если множество `Y` *содержит больше элементов*, чем множество `X`, то оно *не может быть сюръективным* по *определению функции*.\n\nФункция `f` называется **инъективной** (*инъекцией*), если *любым двум разным* элементам `x1, x2 ∈ X` *соответствуют различные* элементы `y1, y2 ∈ Y` (если `x1 ≠ x2`, то `f(x1) ≠ f(x2)`). Другими словами, *для любых* элементов `x1, x2 ∈ X` *верно*: если *выполняется* `f(x1) = f(x2)`, то также *выполняется* и `x1 = x2`.\n\nФункция `f` называется **биективной** (*биекцией*), если она *сюръективна* и *инъективна одновременно*.\n\nИначе говоря, **биекция** (*взаимно-однозначное отношение*) — такое *бинарное отношение* `R ⊆ X × Y`, что *каждому* значению `x ∈ X` *соответствует единственное* значение `y ∈ Y`, а *каждому* значению `y ∈ Y` *соответствует единственное* значение `x ∈ X`.\n\n### Задача (сюръективность, инъективность, биективность)\n\n<!--\nЗа примерами далеко ходить не нужно :)\n-->\n\n«Даны функции `f, g: R → R; f(x) = x + 1; g(x) = |x|`. *Сюръективны*, *инъективны*, *биективны* ли они?»\n\nОтвет: `f` *биективна*, `g` *не инъективна* и *не сюръективна*.\n\nЗначение `f(x)` *всегда на единицу больше* значения `x`, а множество действительных чисел `R` *бесконечно*, тогда:  \n1) На `R` *для любого* значения `f(x)` *найдётся соответствующий* `x` - функция `f` *сюръективна*.\n2) При `x1 ≠ x2` всегда будет выполняться `f(x1) ≠ f(x2)`, поскольку `x1 + 1 ≠ x2 + 1`, - функция `f` *инъективна*.\n\nТогда функция `f(x)` *биективна*.\n\n*“Модуль”* всегда *возвращает только положительные значения* при любых аргументах, поэтому:\n1) *Не существует* такого `x ∈ R`, чтобы `y ∈ R` *принял* какое-либо *отрицательное значение* из множества `R` - `g` *не сюръективна*.\n2) Доказательство *от противного*. *Подберём* такой *пример*, чтобы *инъективность не соблюдалась*: `g(1) = |1| = 1 = |-1| = g(-1)`. Так как `1 ≠ -1`, а `g(1) = g(-1)`, - `g` *не инъективна*.\n\nИтак, функция `g(x)` *не сюръективна*, *не инъективна*, а значит и *не биективна*.\n\n### Функция нескольких переменных\n\nЕсли *множество* `X` *представляет* собой *декартово произведение* `n` множеств `X = X1 × X2 × … × Xn`, то *отображение* `f: X → Y` называется **n-местным отображением** (*функцией n переменных*).\n\n*Элементы* `x1 ∈ X1, x2 ∈ X2, …, xn ∈ Xn` *упорядоченного набора* `x = (x1, x2, … , xn)` называются **аргументами** *функции n переменных*.\n\nВ таком случае *запись* `y = f(x)` *эквивалентна* `y = f(x1, x2, … , xn)`. \n\nПример *функции двух переменных*: `f: R × R → R, f(x,y) = x + y` - *числовая функция суммы двух значений*.\n\n### Композиция функций\n\nДля начала введём *аналогичное* определению *композиции бинарных отношений* определение *композиции функций*, *отличающееся лишь обозначниями*.\n\n**Композицией** (суперпозицией) **функций** `f: X → Y` и `g: Y → Z` называется *такая функция* `(g ○ f): X → Z`, что *для любых* элементов `x ∈ X, z ∈ Z` *существует связь* `(g ○ f)(x) = z` *тогда и только тогда*, когда *существует* элемент `y ∈ Y` такой, что *одновременно существуют* связи `f(x) = y` и `g(y) = z`. \n\nЗапись *композиции функций* `f` и `g` на *языке символов*: `∀x∈X,∀z∈Z: [(g ○ f)(x) = z] ⇔ ∃y∈Y: [f(x) = y]∧[g(y) = z]`.\n\nМожно заметить, что *при композиции функций* `f` и `g` *результат* выполнения *функции* `f` *передаётся аргументом* в *функцию* `g`, то есть `g(f(x)) = z`. \n\nТогда *определение упрощается*: *композицией* называется *функция* `(g ○ f): X → Z`, *определённая равенством*: `(g ○ f)(x) = g(f(x)), x ∈ X`. \n\nЗапись `g ○ f` *читается* как «`g` *после* `f`».\n\nТаким образом, с помощью *композиции сложные действия* можно *представлять* как *последовательность (цепочку) простых действий*.\n\n*Композиция большего числа функций составляется аналогично*. Если *добавить* в неё ещё одну функцию `h: Z → P`, то `(h ○ g ○ f)(x) = h(g(f(x))), x ∈ X`.\n\nНапример, *составную функцию* `h(z) = 3z + 2` можно *представить* как *композицию* двух *простых функций* `f(x) = 3x` и `g(y) = y + 2`.\n\n*Композиция* функций *некоммутативна*: `(g ○ f) ≠ (f ○ g)`. *Порядок следования функций имеет значение*: `f(g(x) ≠ g(f(x))`.\n\nНа *примере выше* несложно показать *некоммутативность композиции*: `f(g(1)) = 3 • (1 + 2) = 9`, `g(f(1)) = (3 • 1) + 2 = 5`.\n\n*Композиция* функций *ассоциативна*: `h ○ g ○ f = (h ○ g) ○ f = h ○ (g ○ f)`.\n\n*Композицию* интуитивно можно *использовать* и в *других областях*, если *рассматривать функцию* как *некоторое действие*. Например, *регистрация пользователя* может состоять из *композиции* следующих *действий* (функций): *проверки правильности ввода* данных, *проверки существования пользователя* в базе данных, *создания нового пользователя* в базе данных.\n\n### Индикаторная функция\n\nПусть *имеется некоторое множество* `A` и *некоторое* его *подмножество* `B ⊆ A`.\n\n**Индикаторной функцией** (англ. indicator function), **индикатором** или **характеристической функцией** (англ. characteristic function) **подмножества `B` множества `A`** называют *дискретную функцию*, *определенную* на множестве `A`, которая *указывает принадлежность элемента* `x ∈ A` к *подмножеству* `B` следующим образом:\n1) *Функция возвращает значение* `1` для *аргумента* `x`, если *значение аргумента принадлежит* `B`.\n2) *Функция возвращает значение* `0` для *аргумента* `x`, если *значение аргумента не принадлежит* `B`.\n\n*Индикаторную функцию обозначают символами* `I`, `χ` или `1`.\n\n*Формальное определение индикаторной функции*: `I: A → { 0, 1 }, B ⊆ A,`\n```\nI(x) = {\n  1, x ∈ B;\n  0, x ∉ B;\n}\n```\n\nЕсли `B = A`, то `I(x) ≡ 1` (*индикаторная функция тождественно равна единице*, то есть её *значение равно* `1` для *любого аргумента* `x`). Если  `B = ∅`, то `I(x) ≡ 0` (*индикаторная функция тождественно равна нулю*).\n\nНапример, если `A = { 3, 1, 7 }`, `B = { 1, 7 }` то `I(3) = 0`, `I(1) = I(7) = 1`.\n\n\n## Углубленно об операции\n- [Определение операции и обозначения](#определение-операции-и-обозначения)\n- [Бинарная операция и формы её записи](#бинарная-операция-и-формы-её-записи)\n- [Свойства бинарных операций](#свойства-бинарных-операций)\n- [Унарная операция, её формы и свойства](#унарная-операция-её-формы-и-свойства)\n- [Cвойства операций над множествами](#свойства-операций-над-множествами)\n- [Приоритет и очерёдность операций](#приоритет-и-очерёдность-операций)\n\n### Определение операции и обозначения\n\n**Операция** (operation) - это *функция*, которая принимает *любое количество входных значений* (input values) и имеет *чётко определённое выходное значение* (output value).\n\nФактически, *определение операции* совпадает с *определением функции нескольких переменных*. *Главным различием* между ними выступает *обозначение*: *операции обозначаются специальными символами*, имеют свои *формы записи* и *способы передачи аргументов*.  \n\nПусть имеется `n` *непустых множеств* `X1, X2, …, Xn`. Тогда если *множество* `X` *представляет* собой *декартово произведение* этих множеств `X = X1 × X2 × … × Xn`, то *отображение* `f: X → Y` называется **n-арной операцией**.\n\n*Входные значения* `x1 ∈ X1, x2 ∈ X2, …, xn ∈ Xn` называют  **аргументами** (arguments) **операции** или **операндами** (operands), *выходное значение* `y ∈ Y` называют **результатом операции** (operation result).\n\nГоворят, что *операция* `f` **совершается над** своими **операндами**.\n\n*В отличие* от *функций* и *отношений*, *каждая операция имеет* своё *уникальное символьное представление* (*иногда* используется *несколько символов*). Например, *операция сложения обозначается* знаком `+`; *операция умножения* - знаками `•`, `*`, `×`; *логическое отрицание* - знаком `¬` и так далее. \n\nНапример, *выражение* `1 + 3 = 4` *целиком* является *операцией*, `1` и `3` - *операндами*, `4` - *результатом выполнения операции*.\n\n*Символ*, использующийся в *операции* (например, `+`), называют **оператором** (operator). В *отличии от операции*, *оператор* может *менять своё предназначение* в *зависимости* от *типа операндов*. Например, в математике *произведение чисел* и *произведение множеств* - это совершенно *разные операции*, использующие *один бинарный оператор* `×`. В *программировании* аналогичным *примером* могут послужить *сложение чисел* и *сложение* (конкатенация) *строк* - *разные операции*, *один бинарный оператор* `+`.\n\n*Количество операндов* `n` называют **арностью** (arity) **операции**.\n\nПри `n = 0` *операция* является **константой**: `f() = 3` или `y = 3`.\n\nПри `n = 1` *операция* называется **унарной** (unary), при `n = 2` - **бинарной** (binary, лат. `bi` - “два\"), при `n = 3` - **тернарной** (ternary) и так далее.\n\nАбсолютное *большинство операций* - *унарные* и *бинарные*, поэтому мы уделим им *особое внимание*.\n\nНапоследок хочется сказать, что в *каждой области знаний* изучаются и используются *свои операции*. В *программировании* - *арифметические*, *логические* и *строковые операции*, в *математическом анализе* - *операции дифференцирования и интегрирования* и так далее.\n\n<!--\n### Арифметические операции\n\n\n**Алгебраической операцией** называется *операция*, в которой *вместо* `Y, X1, X2, …, Xn` *используется одно и то же множество* `A = Y = X1 = X2 = … = Xn`: `f: A^n → A`.\n\nTODO\n\nСложение и вычитание являются элементарными арифметическими операциями. Все остальные, более сложные операции, получаются в результате гиперопераций. Так, сложение и вычитание относят к операциям первой ступени; умножение и деление — к операциям второй ступени; возведение в степень, извлечение корня и логарифмирование — к операциям третьей ступени; \n\n\nК арифметическим операциям относят:\n1) \n\n-->\n\n### Бинарная операция и формы её записи\n\nГоворя простыми словами, **бинарная операция** (*двумеестная операция*) — *операция*, которая *принимает два аргумента* и *возвращающая один результат*. \n\nПусть имеется *три непустых множества* `A`, `B` , `C`. **Бинарной операцией на паре** `A`, `B` **со значениями в** `C` называется *отображение* `f: A × B → C`.\n\nПусть имеется *непустое множество* `A`. Тогда **бинарной операцией на множестве** `A` называют *отображение* `f: A × A → A`. \n\n*Самой распространённой формой записи бинарных операций* является **инфиксная форма** - такая, при которой *символ операции* (например, `◇`), ставится *между* её *операндами*: `x ◇ y`.\n\nТем не менее существуют также **префиксная запись** (*польская нотация*), где *символ операции* ставится *перед* её *операндами*: `◇ x y`, и **постфиксная запись** (*обратная польская нотация*), где *символ операции* ставится *после* её *операндов*: `x y ◇`.\n\n\n### Свойства бинарных операций\n\nКак *разновидность отношения*, *операция* может *обладать* или *не обладать* некоторыми **свойствами**, которые *определяют её поведение*. \n\nТем не менее, *свойства операций* несколько *отличаются* от *свойств отношений*. Это *связано* с тем, что обычно *у отношений нет результата* `x R y` (например, `x - потомок y`), а *у операций есть результат* `x ◇ y = z` (например, `4 • 5 = 20`). Таким образом, *свойства операций* будут *строятся вокруг* знака *равенства*.\n\nДля *рассмотрения свойств условно обозначим произвольную операцию символом* `◇`, *ещё одну операцию* (при её необходимости) *обозначим* символом `◆`.\n\n1) **Идемпотентность** — такое *свойство* операции, при которотом *повторное применение операции не имеет* никакого *эффекта*. Например, операции `∩`, `∪` *идемпотентны*: *повторное пересечение* или *объединение* с *тем же множеством не даст эффекта* (`A ∩ B = A ∩ B ∩ B`).\n2) **Коммутативность** (*переместительное свойство*) — такое *свойство* операции, при котором *порядок следования операндов неважен*: `x ◇ y = y ◇ x`. Например, операция *сложения коммутативна*: `1 + 7 = 7 + 1 = 8`.  \n3) **Антикоммутативность** — такое *свойство* операции, при котором `(x ◇ y) = -(y ◇ x)`. Например, операция *вычитания антикоммутативна*: `8 − 1 = −(1 − 8) = 7`.  \n4) **Ассоциативность** (*сочетательное свойство*) — такое *свойство* операции, при котором *результат последовательного применения операции* (`x1 ◇ x2 ◇ … ◇ xn`) *не зависит* от *очерёдности вычисления*, то есть *расстановка скобок не имеет эффекта* и *скобки можно опустить*: `(x ◇ y) ◇ z = x ◇ (y ◇ z) = x ◇ y ◇ z`. Например, операция *сложения ассоциативна*: `(3 + 2) + 2 = 3 + (2 + 2) = 3 + 2 + 2 = 7`.  \n5) **Дистрибутивность** (*распределительное свойство*). Операция `◆` - **дистрибутивна относительно операции** `◇`, если выполняется `(x ◇ y) ◆ z = x ◆ z ◇ y ◆ z`. Например, операция *умножения дистрибутивна относительно* операции *сложения*: `(1 + 7) • 3 = 1 • 3 + 7 • 3 = 24`.  \n\n### Унарная операция, её формы и свойства\n\nПусть имеется *непустое множество* `A`. **Унарной операцией на множестве** `A` называется *отображение* `f: A → A`.\n\n*Унарная операция* может быть **префиксной** `◇ x` и **постфиксной** `x ◇`.\n\nОт *формы унарной операции зависят смысл* и *результат операции*, поэтому *сменять* её *нельзя* - *форма унарной операции фиксирована*. Например, `x!` - *факториал* в *математике*, `!x` - *логическое отрицание* в *программировании*; в *программировании* `++x` - *инфиксный инкремент*, `x++` - *постфиксный инкремент*, `--x` - *инфиксный декремент*, `x--` - *постфиксный декремент*.\n\n*Унарная операция* из *перечисленных* ранее *свойств бинарных операций* *может обладать* лишь *свойством идемпотентности*: `◇ x = ◇ (◇ x)`. Например, *модуль числа идемпотентен*: `|-1| = ||-1|| = |||-1||| = 1`. \n\nОчевидно, что *логическое отрицание не идемпотентено*: `¬x ≠ ¬¬x`, впрочем как и *унарный минус*: `-1 ≠ -(-1)`.\n\n### Приоритет и очерёдность операций\n\n**Приоритет операции** - *формальное свойство операции*, которое *влияет на очередность выполнения* данной *операции* в *выражении* с *несколькими различными операциями* при *отсутствии явного указания* (с помощью *круглых скобок*) на *порядок* их *выполнения*. Пример такого *выражения*: `x ◆ y ◇ z`. *Приоритет влияет* на то, что *выполнится в первую очередь*: `x ◆ y` или `y ◇ z`.\n\n**Очерёдность операций** (the order of operations) - *набор правил*, *определяющий последовательность* (очерёдность) *выполнения операций*. Другими словами, эти *правила определяют* (задают) *приоритет операций*.\n\n*Очерёдность арифметических операций* в *науках* и большинстве *языков программирования*:  \n1) *Возведение в степень* (`x^z`) и *извлечение корня* (`√x`).  \n2) *Умножение* (`x • y`) и *деление* (`x / y`).  \n3) *Сложение* (`x + y`) и *вычитание* (`x - y`).  \n\nЧем *выше по списку* находится *операция*, тем *выше её приоритет* и тем *раньше* она *выполнится* в выражении.\n\nНапример, в *выражении* `7 • 3^2 + 1` *сначала* выполнится `3^2 = 9`, *затем* `7 • 9 = 63` и только *затем* `63 + 1 = 64`.\n\nЧтобы *явно указать порядок выполнения операций* (повысить приоритет какой-то операции), можно использовать **групприровку операций** при помощи *круглых скобок* `()`. Тогда *первым выполнится* та *операция*, которая находится *в скобках*. Например, в *выражении* `(3 + 2) • 2` *первым выполнится сложение* `3 + 2 = 5`, а затем *умножение* `5 • 2 = 10` несмотря на то, что *изначально* у *умножения приоритет* был *выше*.\n\nЕсли *несколько операций* имеют *одинаковый приоритет*, то они *выполняются слева направо*. Например, *умножение* и *деление имеют одинаковый приоритет*, поэтому в *выражении* `6 / 3 • 7` *сперва выполнится* операция *слева* `6 / 3 = 2`, а *затем* операция *справа* `2 • 7 = 14`.\n\nС *операциями*, которых *нет в списке выше*, всё аналогично: в тех *областях*, где эти *операции применяются*, создаются *похожие списки* - *в противном случае операции выполняются* последовательно, *слева направо*.\n\n<!--\n**Очерёдность операций** в программировании — *последовательность выполнения операций*, *определённая синтаксисом* конкретного *языка программирования* для *случая*, когда *операции имеют одинаковый приоритет* и *отсутствует явное указание* на *очерёдность* их *выполнения* (обычно указывается при помощи круглых скобок).\n-->\n\nВ *программировании* *определение ассоциативности операции* немного *отличается*. **Ассоциативность** (в *программировании*) — *свойство* операций, *позволяющее восстановить последовательность* (очерёдность) их *выполнения* в *выражении* с *несколькими различными операциями* при *одинаковом приоритете операций* и *отсутствии явных указаний* (при помощи круглых скобок) *на очерёдность* их *выполнения*. Различают *левую* и *правую ассоциативность*. При **левой ассоциативности** *вычисление выражения* происходит *слева направо*, а при **правой ассоциативности** — *справа налево*. \n\n<!--\nНапример, *оператор группировки* `()` имеет *самый высокий приоритет* и может *наделять приоритетом другие операции*\n\n*Отношение равенства* `=` имеет *очень низкий приоритет*: ниже только у оператора перечисления (запятая) `,`.\n\n-->\n\n\n# Теория мультимножеств\n- [Мультимножество](#мультимножество)\n- [Основные понятия теории мультимножеств](#основные-понятия-теории-мультимножеств)\n- [Число мультимножеств](#число-мультимножеств)\n- [Отношения между мультимножествами](#отношения-между-мультимножествами)\n- [Операции над мультимножествами](#операции-над-мультимножествами)\n\n\n## Мультимножество\n\nПонятие *мультимножества* получено *исключением* из понятия *множества требования уникальности* элементов.\n\n**Мультимножеством** (англ. multiset, bag) называют *неупорядоченный набор объектов* (*не обязательно уникальных*), *обладающих схожими признаками*.\n\n*Обозначение мультимножества совпадает* с *обозначением множества*. Примеры *мультимножеств*: `{ 3, 2, 2 }`, `{ белый, красный, белый }`.\n\n## Основные понятия теории мультимножеств\n- [Множественность элемента](#множественность-элемента)\n- [Представление мультимножества как множества](#представление-мультимножества-как-множества)\n- [Функция множественности](#функция-множественности)\n- [Принадлежность элемента мультимножеству](#принадлежность-элемента-мультимножеству)\n- [Мощность (кардинальность) мультимножества](#мощность-кардинальность-мультимножества)\n- [Саппорт мультимножества](#саппорт-мультимножества)\n\n### Множественность элемента\n\n**Множественностью** (англ. multiplicity) **элемента `x` в мультимножестве `A`** называется *число вхождений элемента* `x` в *мультимножество* `A`. \n\nНапример, в *мультимножестве* `{ x, x, y, z, z, z }`: *элемент* `x` имеет *множественность* `2`, *элемент* `y` - *множественность* `1`, *элемент* `z` - *множественность* `3`.\n\n### Представление мультимножества как множества\n\n*Мультимножество* можно *определить* как *множество особой структуры*.\n\n**Мультимножеством `A`** называют *множество упорядоченных пар* `(x, m(x))` вида `{ (x1, m(x1)), (x2, m(x2)), ..., (xn, m(xn)) }`, где `x` - *элемент мультимножества*, а `m(x)` - *множественность элемента* `x`.\n\nПри *таком определении мультимножества*, *мультимножество* `{ x, x, y, z, z, z }` можно *представить* в *виде* `{ (x, 2), (y, 1), (z, 3) }`, что достаточно *удобно*.\n\n*Существует* другое, *интуитивное обозначение*: `{ x1^m(x1), x2^m(x2), ..., xn^m(xn) }`. Пример: `{ x, x, y, z, z, z } = { x^2, y, z^3 }`.\n\n\n### Функция множественности\n\nФункция `m(x)`, отображающая *элементы мультимножества* в *целые неотрицательные числа* (`m: A → Z+`), называется **функцией множественности** (англ. multiplicity function).\n\n*Добавим* *дополнительный аргумент* в *функию множественности*: `m(x, A)`, где `A` - *название мультимножества*, которому *принадлежит элемент* `x`. Это позволит нам *различать множественность* одних и *тех же элементов* в *разных множествах* и при этом *не задавать каждый раз новую функцию* (`m: A → Z+`, `m: B → Z+` и так далее)\n\nНапример, если `A = { y, y, y }` и `B = { x, y }`, тогда `m(y, A) = 3`, `m(y, B) = 1`, `m(x, A) = 0`, `m(x, B) = 1`.\n\n### Принадлежность элемента мультимножеству\n\n**Элемент `x` принадлежит мультимножеству** (*содержится в мультимножестве*) **`A`**, если `m(x, A) > 0`. *Обозначение*: `x ∈ A`.\n\n**Элемент `x` не приналежит мультимножеству** (*не содержится в мультимножестве*) **`A`**, если `m(x, A) = 0`. *Обозначение*: `x ∉ A`.\n\n### Мощность (кардинальность) мультимножества\n\n**Мощностью** или **кардинальностью** (англ. cardinality) **мультимножества** `A` называют *число всех элементов* мультимножества `A`, *включающее* так же *все повторения* этих *элементов*. Иначе говоря, *кардинальностью* называют *сумму* *множественностей всех элементов мультимножества* `A`: `|A| = Σ m(x,A), x ∈ A`.\n\n\n*Кардинальность мультимножества* `A` *обозначается*: `|A| = z`, где `z` - *неотрицательное целое число*.\n\nНапример, *мультимножество* `A = { y, z, x, y, z }` имеет *кардинальность* `|A| = m(x) + m(y) + m(z) = 1 + 2 + 2 = 5`.\n\n\n### Саппорт мультимножества\n\n**Саппортом** (англ. support) **мультимножества** `A` называют *множество уникальных элементов* мультимножества `A`.\n\nОбозначение *саппорта мультимножества* `A`:  \n`Supp(A) = { x ∈ U ∣ m(x, A) > 0 }`.\n\nПусть `A = { x, x, x, y, y, z }`, тогда его *саппорт*: `Supp(A) = { x, y, z }`.\n\n## Число мультимножеств\n\nВообще говоря, **число мультимножеств** для **любого непустого конечного множества** `A` **бесконечно**.\n\nНапример, для *множества* `{ x }` существует *бесконечное число мультимножеств*: `{ x, x }`, `{ x, x, x }`, `{ x, x, x, ..., x }`.\n\n**Число мультимножеств** (англ. multiset number) **мощности `k`**, состоящих **из элементов** некоторого **множества `A` мощности `n`** *обозначается* `(( n k ))` и *равняется числу подмножеств мощности* `k` в некотором *множестве мощности* `n + k - 1`, то есть:  \n**`(( n k )) = C(n + k - 1, k)  = (n + k - 1)! ÷ (k!(n - 1)!)`**.\n\nОбозначение `(( n k ))` *читается* как *“множественный выбор `k` из `n`”* (англ. n multichoose k).\n\nНапример, если `A = { ежевика, голубика, черника }`, тогда `(( 3 2 )) = C(4, 2)  = 4! ÷ (2!2!) = (3 • 4) ÷ 2 = 6` *мультимножеств*: `{ ежевика, ежевика }`, `{ ежевика, голубика }`, `{ ежевика, черника }`, `{ голубика, голубика }`, `{ голубика, черника }`, `{ черника, черника }`.\n\n## Отношения между мультимножествами\n\n### Пересечение мультимножеств\n\n**Мультимножества `A` и `B` пересекаются**, если они *имеют одинаковые элементы* и *не обязательно одинаковые множественности* этих *элементов*.\n\n### Равенство мультимножеств\n\n**Мультимножеств `A` и `B` равны** , если *совпадают все элементы* этих *мультимножеств*, а также *множественности каждого* отдельно взятого *элемента*, *в противном случае* **мультимножества `A` и `B` не равны**.\n\n### Включение мультимножества\n\nГоворят, что **мультимножество `A` включено в мультимножество `B`** (англ. `A` included in `B`), если `Supp(A) ⊆ Supp(B)` и для *любого элемента* `x ∈ A` *выполняется* `m(x, A) <= m(x, B)`, то есть если *каждый элемент* `x` мультимножества `A` *содержится* в мультимножестве `B` и его *множественность* в `A` *меньше*, чем в `B`.\n\n*Существует* так же *краткая версия определения* с использованием *универсального множества* `U`.\n\nГоворят, что **мультимножество `A` включено в мультимножество `B`**, если для *любого элемента* `x ∈ U` *выполняется* `m(x, A) <= m(x, B)`, то есть *множественность любого элемента универсального множества* в `A` *меньше*, чем в `B`.\n\n\nОбозначение *включения* `A` в `B`: `A ⊆ B`.\n\nНапример, если `A = { x, y }`, `B = { x, z }`, `C = { x, y, x }`, то `A ⊆ C`.\n\nДля *любого мультимножества* `A` *выполняется* `Supp(A) ⊆ A`.\n\n\n## Операции над мультимножествами\n- [Пересечение мультимножеств](#пересечение-мультимножеств)\n- [Объединение мультимножеств](#объединение-мультимножеств)\n- [Сложение мультимножеств](#сложение-мультимножеств)\n\n\n\n### Пересечение мультимножеств\n\n**Пересечением** (англ. intersection) **мультимножеств `A` и `B`** называют такое *мультимножество* `C`, что для *любого* элемента `x ∈ U` *выполняется* `m(x, C) = min{ m(x, A), m(x, B) }`.\n\nНапример, `A = { x, y, y }` в *пересечении* с `B = { x, x, y, z }` даёт `C = { x, y }`.\n\n```\nA: { (x, 1), (y, 2), (z, 0) }\nB: { (x, 2), (y, 1), (z, 1) }\nC: { (x, 1), (y, 1), (z, 0) }\n```\n\n### Объединение мультимножеств\n\n**Объединением** (англ. union) **мультимножеств `A` и `B`** называют такое *мультимножество* `C`, что для *любого* элемента `x ∈ U` *выполняется* `m(x, C) = max{ m(x, A), m(x, B) }`.\n\nНапример, `A = { x, y, y }` в *объединении* с `B = { x, x, y, z }` даёт `C = { x, x, y, y, z }`.\n\n```\nA: { (x, 1), (y, 2), (z, 0) }\nB: { (x, 2), (y, 1), (z, 1) }\nC: { (x, 2), (y, 2), (z, 1) }\n```\n\n### Сложение мультимножеств\n\n**Сложением** (англ. sum) **мультимножеств `A` и `B`** называют такое *мультимножество* `C`, что для *любого* элемента `x ∈ U` *выполняется* `m(x, C) = m(x, A) + m(x, B)`.\n\nНапример, `A = { x, y, y }` при *сложении* с `B = { x, x, y, z }` даёт `C = { x, x, x, y, y, y, z }`.\n\n```\nA: { (x, 1), (y, 2), (z, 0) }\nB: { (x, 2), (y, 1), (z, 1) }\nC: { (x, 3), (y, 3), (z, 1) }\n```\n\n\n\n# Теория графов\n\n<!--\n## О теории графов\n-->\n\n## Понятие графа\n\n**Графом** `G` (англ. graph) называют такую *упорядоченную пару* `(V, E)`, в которой компонента `V` - это некоторое *конечное* (счётное) *непустое* *множество*, *состоящее* из *объектов произвольной природы*, а комнонента `E` - *множество неупорядоченных пар* `{ x, y }`, *состоящих* из *элементов* `x`, `y`, *принадлежащих* множеству `V` (причём *не обязательно различных*).\n\n*Формально определение графа* `G` можно записать вот так:  \n1) `G = (V, E)`.  \n2) `V ≠ ∅`.  \n3) `|V| ≠ ∞`.\n4) `E = { { x, y } } : x, y ∈ V`.\n\n\nМножество `V` называют **множеством вершин** (*множеством узлов*) **графа** `G`, а его *элементы* - **вершинами** (англ. vertices), **узлами** (англ. nodes). \n\n*Обозначение множества вершин* `V` *графа* `G`: `V(G)`.\n \nМножество `E` называют **множеством рёбер** **графа** `G`, а его *элементы* - **рёбрами** (англ. edges). \n\n*Обозначение множества рёбер* `E` *графа* `G`: `E(G)`.\n\n*Обозначение графа* `G` c *множеством вершин* `V` и *множеством рёбер* `E`: `G(V, E)`.\n\n*Объекты*, *являющиеся элементами множества* `V` (вершинами), могут *преставлять собой что угодно* - это *не имеет значения*. Важно лишь, чтобы эти *объекты обладали парными связями*. Например, *вершинами* могут выступать *точки на карте*, тогда *наличие дуги* (связи) *между двумя вершинами* может *означать наличие маршрута* между *двумя заданными точками на карте*.\n\nВ *примерах* и *задачах* для *простоты*  в качестве *вершин графа используют натуральные числа* (`1, 2, 3, ...`), *подразумевая* при этом, что *вместо чисел* может быть *что угодно*.\n\nПример *графа* `G(V, E)`:\n```\nV = { 1, 2, 3 }\n\nV × V = {\n  (1, 1), (1, 2), (1, 3),\n  (2, 1), (2, 2), (2, 3),\n  (3, 1), (3, 2), (3, 3)\n}\n\nE = { (1, 3), (2, 1), (3, 2) }\n```\n\n<!--\n\n## Ориентированный и неориентированный граф\n\n**Ориентированным графом** \n\n## Основные понятия теории графов\n\n### Простой граф\n\n**Простым графом** называют *граф* `G`, в котором *для любых двух вершин* `a` и `b` *содержится* *не более одного ребра* `(a, b)`.\n\n\n\n\n\n\n## Графическое представление графа\n\n*Вершины графа графически представляют* в виде *кружков* с *цифрами* внутри, а *рёбра* - *произвольным линиями* между *парами кружков*.\n\n## Способы представления графа\n\n-->\n\n\n\n\n# Математическая логика\n\n- [Высказывание и истинностные значения](#высказывание-и-истинностные-значения)\n- [Квантор всеобщности и квантор существования](#квантор-всеобщности-и-квантор-существования)\n- [Логические операции](#логические-операции)\n\n<!--\n\n## О пользе математической логики в программировании\n\nЛогика высказываний послужила основным математическим инструментом при создании компьютера, поскольку она легко преобразуется в битовую логику: истинность высказывания обозначается одним битом (0 — ЛОЖЬ, 1 — ИСТИНА). Тогда операция `¬` приобретает смысл вычитания из единицы; ∨ — сложения; & — умножения; ↔ — равенства; ⊕  — в буквальном смысле сложения по модулю 2 (исключающее Или — XOR);\n\nЛогические операции легли в основу создания компьютеров. \n\n### Почему в компьютерах используется двоичная система счисления? \n1) На *устройствах* с *двумя устойчивыми состояниями* проще реализовать, чем на устройствах с большим количеством состояний. Например, *на проводе подаётся ток* - это *логическая единица* (истина), *ток отсутствует* - *логический ноль* (ложь). В случае работы с десятичной системой счисления необходимо было бы иметь 10 различимых состояний.\n2) Арифметические действия проще выполнить в двоичной системе счисления, чем в десятичной.\n\nСамым большим минусом двоичной системы является её некомпактность. Например, для записи десятичного числа `256` необходимо 8 единиц.\n\nКак компьютер понимает логические значения? Если *на проводе подаётся ток* - это *логическая единица* (истина), если *ток отсутствует* - *логический ноль* (ложь). Это очень удобно. Самый простейший компьютер, хранящий всего 1 бит информации (`0` - ложь, `1` - истина) - это обычный переключатель: хранится 1, если включен, и 0, если выключен.\n-->\n\n## Высказывание и истинностные значения\n\n**Высказывание** (*логическое высказывание*) - *предложение*, о котором можно сказать, что *оно истино  или ложно*.\n\nНапример, *высказывание* `«Луна вращается вокруг Земли.»` является *истиным*, а *высказывание* `«Я никогда не перестану любить тебя.»` - *ложным* ;)\n\n*Высказываниями* могут быть *только повествовательные предложения*, но никак *не побудительные* (`«Выходи гулять.»`) и *не вопросительные* (`«Для кого я это пишу?»`), поскольку *их истинность невозможно оценить*.\n\nТакже *не могут быть высказываниями* те повествовательные *предложения*, *об истинности* которых мы *ничего не можем сказать*. Например, предложение `«Зима будет холодной»` *не является высказыванием*, поскольку *не хватает информации*, чтобы *подтвердить или опровергнуть* его. Предложение `x < 3` так же *не является высказыванием*, поскольку *недостаточно информации* об `x`. \n\n*Высказывания обозначаются малыми латинскими буквами* (`a-z`). Например, `p - «Рыбы не умеют ходить.»`.\n\n*Истинность высказывания* передаётся **логической единицей** `1` (*истина*), *ложность* - **логическим нулём** `0` (*ложь*). Данные *значения* называют **истинностными** (*логическими*) **значениями высказывания**.\n\n*Истинностное значение* является *основной характеристикой высказывания*.\n\nУсловимся, что *высказывание не может быть истино* и *ложно одновремено*, то есть мы *будем рассматривать только* **двоичную** (бинарную) **логику**.\n\n*Истинность высказывания* `p` *обозначается*: `|p| = 1`, *ложность*: `|p| = 0`.\n\n<!--\n## Элементарные и составные высказывания\n\nКак в русском есть простые и составные предложения, так и высказывания делятся на элементарные и составные.\n\n-->\n\n## Квантор всеобщности и квантор существования\n\n**Квантор всеобщности** `∀` (перевёрнутое английское “A” - \"all\") — это *условие*, которое *выполняется для всех указанных элементов*.\n\n\nСимвол `∀` *читается* как *“для всех…”*, *“для любого…”*, *“для каждого…”*, а также *“все…”*, *“любой…”* и *“каждый…”*. \n\nВыражение `(∀x ∈ X)P(x)` *читается* как *“каждый (всякий, любой) `x` из множества `X` обладает свойством `P(x)`”* или *“для любого `x` из множества `X` справедливо (выполняется) `P(x)`”*.\n\nСимвол `:` *читается* как *“такого (таких), что\"*.\n\nПример: `∀x ∈ X: x > 0` - *“Всякий `x` из множества `X` такой, что `x > 0`”*.\n\n**Квантор существования** `∃` (перевёрнутое английское “E” - “exist\") - это *условие*, которые *выполняется хотя бы для одного из указанных элементов*.\n\nСимвол `∃` *читается* как *“существует”*, *“некоторый”* или *“для некоторого”*.\n\nВыражение `(∃y ∈ Y)Q(y)` *читается* как *“некоторый `y` из множества `Y` обладает свойством `Q(y)`”* или *“существует `y` из множества `Y` справедливо (выполняется) `Q(y)`”*.\n\n*Квантор существования гарантирует существование*, но *не единственность элемента* (что часто *требуется* в *доказательствах теормем*), поэтому существует его *модификация*: запись `∃!` *читается* как *“существует единственный”*.\n\n## Логические операции\n\n\n*Рекомендуется* почитать: «[Углубленно об операциях](#углубленно-об-операциях)».\n\n- [О логических операциях](#о-логических-операциях)\n- [Логическое повторение](#логическое-повторение)\n- [Логическое отрицание](#логическое-отрицание)\n- [Конъюнкция](#конъюнкция)\n- [Дизъюнкция](#дизъюнкция)\n- [Штрих Шеффера](#штрих-шеффера)\n- [Стрелка Пирса](#стрелка-пирса)\n- [Эквивалентность](#эквивалентность)\n- [Сложение по модулю два](#сложение-по-модулю-два)\n- [Прямая импликация](#прямая-импликация)\n- [Обратная импликация](#обратная-импликация)\n- [Декремент](#декремент)\n- [Инкремент](#инкремент)\n- [Свойства логических операций](#свойства-логических-операций)\n\n### О логических операциях\n\n**Булево множество** - *двухэлементное множество* `{ 0, 1 }` или `{ ложь, истина }`.\n\n**Логическая операция** - это *операция над элементами булева множества* `B`.\n\nВсе *логические операции* - *унарные* или *бинарные*.\n\n**Унарная логическая операция** имеет *один операнд* (аргумент) и *один результат*.\n\n*Унарные логические операции*:\n- [Логическое повторение](#логическое-повторение)\n- [Логическое отрицание](#логическое-отрицание)\n\n**Бинарная логическая операция** имеет *два операнда* (аргумента) и *один результат*.\n\n*Бинарные логические операции*:\n- [Конъюнкция](#конъюнкция)\n- [Дизъюнкция](#дизъюнкция)\n- [Штрих Шеффера](#штрих-шеффера)\n- [Стрелка Пирса](#стрелка-пирса)\n- [Эквивалентность](#эквивалентность)\n- [Сложение по модулю два](#сложение-по-модулю-два)\n- [Прямая импликация](#прямая-импликация)\n- [Обратная импликация](#обратная-импликация)\n- [Декремент](#декремент)\n- [Инкремент](#инкремент)\n\n\n\n### Логическое повторение\n\n**Логическое повторение** (*логическое “да”*) - *унарная операция*, которая *возвращает* в качестве результата *входное значение* `x`.\n\n\n| `x`  | `x (буферизованное)` |\n|:--:|:--:|\n| 0 | 0 |\n| 1 | 1 |\n\nЭто *самая простая логическая операция*, которую *параметрически можно задать* так:\n```\nx (буферизованное) = {\n   1, если x = 1;\n   0, если x = 0;\n}\n```\n\nВ *программировании* с помощью *логического повторения* реализуется *повторитель*.\n\n### Логическое отрицание\n\n**Логическое отрицание** (*логическое “не”*, *инверсия*) `¬x` (`NOT x`)  — *унарная операция*, которая *меняет значение* `x ∈ { 0, 1 }` *на противоположное*, то есть *преобразует* `0` в `1`, а `1` в `0`.\n\n\n| `x`  | `¬x` |\n|:--:|:--:|\n| 0 | 1 |\n| 1 | 0 |\n\nЗапись `¬x` *читается* как *“не `x`”*.\n\n*Параметрически логическое отрицание* можно записать так:\n```\n¬x = {\n   1, если x = 0;\n   0, если x = 1;\n}\n```\n\n**Закон двойного отрицания**: `¬¬x = x`.\n\nВ *программировании логическое отрицание обозначают символом* `!`: `!true = false`.\n\nВ *программировании* с помощью *логического отрицания* реализуется *инвертор*.\n\n### Конъюнкция\n\n**Конъюнкция** (*логическое “и”*, *логическое умножение*) `x ∧ y` (`x & y`, `x AND y`) - *бинарная операция*, которая *возвращает истинное значение* *тогда и только тогда*, когда её оба *операнда* (`x` и `y`) *истинны*.\n\n\n| `x`  | `y` | `x ∧ y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 0 |\n| 0 | 1 | 0 |\n| 1 | 0 | 0 |\n| 1 | 1 | 1 |\n\nЗапись `x ∧ y` *читается* как “`x` *и* `y`.\n\n*Параметрически конъюнкцию* можно представить так:\n```\nx ∧ y = {\n   1, если x = 1 и y = 1;\n   0, иначе;\n}\n```\n\nВ *программировании логическое “и” обозначается*: `x && y`.\n\n### Дизъюнкция\n\n**Дизъюнкция** (*логическое “или”*, *логическое сложение*) `x ∨ y` (`x OR y`) - *бинарная операция*, которая *возвращает истинное значение тогда и только тогда*, когда хотя бы *один из* её *операндов* `x` или `y` содержит *истинное значение*.\n| `x`  | `y` | `x ∨ y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 0 |\n| 0 | 1 | 1 |\n| 1 | 0 | 1 |\n| 1 | 1 | 1 |\n\nЗапись `x ∨ y` *читается* как “`x` *или* `y`.\n\n\n*Параметрически дизъюнкцию* можно представить так:\n```\nx ∨ y = {\n   1, если x = 1 или y = 1;\n   0, иначе;\n}\n```\n\nВ *программировании логическое “или” обозначается*: `x || y`.\n\n\n### Штрих Шеффера\n\n**Штрих Шеффера** (*операция “и-не”*, инверсия конъюнкции) `x ∣ y` (`x NAND y`) - *бинарная операция*, которая *возвращает истинное значение тогда и только тогда*, когда *один из* её *операндов* `x` или `y` *ложен*.\n\n| `x`  | `y` | `x ∣ y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 1 |\n| 0 | 1 | 1 |\n| 1 | 0 | 1 |\n| 1 | 1 | 0 |\n\n*Параметрически штрих Шеффера* можно представить так:\n```\nx ∣ y = {\n   1, если x = 0 или y = 0;\n   0, иначе;\n}\n```\n\n### Стрелка Пирса\n\n**Стрелка Пирса** (*операция “или-не”*, инверсия дизъюнкции) `x ↓ y` (`x NOR y`) - *бинарная операция*, которая *возвращает истинное значение тогда и только тогда*, когда *оба* её *операнда* (`x` и `y`) *ложны*.\n| `x`  | `y` | `x ↓ y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 1 |\n| 0 | 1 | 0 |\n| 1 | 0 | 0 |\n| 1 | 1 | 0 |\n\n*Параметрически стрелку Пирса* можно представить так:\n```\nx ↓ y = {\n   1, если x = 0 и y = 0;\n   0, иначе;\n}\n```\n\n### Эквивалентность\n\n**Эквивалентность** (*исключающее “или-не”*, *логическая равнозначность*, *эквиваленция*, *тождество*) `x ↔ y` (`x ~ y`, `x ≡ y`, `x XNOR y`) - *бинарная операция*, которая *возвращает истинное значение* *тогда и только тогда*, когда *значения операндов* `x` и `y` *совпадают*.\n| `x`  | `y` | `x ↔ y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 1 |\n| 0 | 1 | 0 |\n| 1 | 0 | 0 |\n| 1 | 1 | 1 |\n\n \n*Параметрически эквивалентность* можно представить так:\n```\nx ↔ y = {\n   1, если x = y;\n   0, иначе;\n}\n```\n\n### Сложение по модулю два\n\n**Сложение по модулю два** (*исключающее “или”*, *неравнозначность*, инверсия равнозначности) `x ⊕ y` (`x XOR y`) - *бинарная операция*, которая *возвращает истинное значение* *тогда и только тогда*, когда *значения операндов* `x` и `y` *не совпадают*.\n| `x`  | `y` | `x ⊕ y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 0 |\n| 0 | 1 | 1 |\n| 1 | 0 | 1 |\n| 1 | 1 | 0 |\n\n\n*Параметрически сложение по модулю два* можно представить так:\n```\nx ⊕ y = {\n   1, если x ≠ y;\n   0, иначе;\n}\n```\n\n### Прямая импликация\n\n**Прямая импликация** (*импликация* от `x` к `y`, инверсия декремента) `x → y` - *бинарная операция*, которая *возвращает истинное значение* *тогда и только тогда*, когда *значение операндов* `x` и `y` *удовлетворяют условию*: `x ≤ y`.\n| `x`  | `y` | `x → y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 1 |\n| 0 | 1 | 1 |\n| 1 | 0 | 0 |\n| 1 | 1 | 1 |\n\n*Логический смысл импликации от* `x` *к* `y`: *если из* `x` *следует* `y` и `x` *истинно*, то `y` *не может быть ложно*, поэтому *выражение* `x → y` при таких значениях операндов *ложно*.\n\n*Параметрически импликацию от* `x` *к* `y` можно представить так:\n```\nx → y = {\n   1, если x ≤ y;\n   0, иначе;\n}\n```\n\n### Обратная импликация\n\n**Обратная импликация** (*импликация* от `y` к `x`, инверсия инкремента) `x ← y` - *бинарная операция*, которая *возвращает истинное значение* *тогда и только тогда*, когда *значение операндов* `x` и `y` *удовлетворяют условию*: `x ≥ y`.\n| `x`  | `y` | `x ← y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 1 |\n| 0 | 1 | 0 |\n| 1 | 0 | 1 |\n| 1 | 1 | 1 |\n\n*Логический смысл импликации от* `y` *к* `x`: *если из* `y` *следует* `x` и `y` *истинно*, то `x` *не может быть ложно*, поэтому *выражение* `x ← y` при таких значениях операндов *ложно*.\n\n\n*Параметрически импликацию от* `y` *к* `x` можно представить так:\n```\nx ← y = {\n   1, если x ≥ y;\n   0, иначе;\n}\n```\n\n### Декремент\n\n**Декремент** (*запрет импликации по* `y`, инверсия прямой импликации) `x ↛ y` - *бинарная операция*, которая *возвращает истинное значение* *тогда и только тогда*, когда *значение операндов* `x` и `y` *удовлетворяют условию*: `x > y`.\n| `x`  | `y` | `x ↛ y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 0 |\n| 0 | 1 | 0 |\n| 1 | 0 | 1 |\n| 1 | 1 | 0 |\n\n\n*Параметрически декремент* можно представить так:\n```\nx ↛ y = {\n   1, если x > y;\n   0, иначе;\n}\n```\n\n*Не путать* с *декрементом* в *программировании* - *унарной операцией*, которая *уменьшает* текущее *значение переменной* `x` *на единицу*: `x--` или `--x`.\n\n### Инкремент\n\n\n**Инкремент** (*запрет импликации по* `x`, инверсия обратной импликации) `x ↛ y` - *бинарная операция*, которая *возвращает истинное значение* *тогда и только тогда*, когда *значение операндов* `x` и `y` *удовлетворяют условию*: `x < y`.\n| `x`  | `y` | `x ↚ y` |\n|:--:|:--:|:--:|\n| 0 | 0 | 0 |\n| 0 | 1 | 1 |\n| 1 | 0 | 0 |\n| 1 | 1 | 0 |\n\n\n*Параметрически инкремент* можно представить так:\n```\nx ↚ y = {\n   1, если x < y;\n   0, иначе;\n}\n```\n\n\n*Не путать* с *инкрементом* в *программировании* - *унарной операцией*, которая *увеличивает* текущее *значение переменной* `x` *на единицу*: `x++` или `++x`.\n\n\n### Свойства логических операций\n\nРекомендую прочитать: «[Свойства бинарных операций](#свойства-бинарных-операций)”.\n\nСоберём *все свойства* и *законы* в одном месте:  \n1) **Идемпотентность**: `x ◇ x = x , ◇ ∈ { ∧, ∨ }`.  \n2) **Коммутативность**: `x ◇ y = y ◇ x , ◇ ∈ { ∧, ∨, ⊕, ∼, ∣ , ↓ }`.  \n3) **Ассоциативность**: `(x ◇ y) ◇ z = x ◇ (y ◇ z) , ◇ ∈ { ∧, ∨, ⊕, ∼ }`.  \n4) **Дистрибутивность**  \n - *конъюнкции относительно дизъюнкции*: `x ∧ (y ∨ z) = (x ∧ y) ∨ (x ∧ z)`.  \n - *конъюнкции относительно сложения по модулю два*: `x ∧ (y ⊕ z) = (x ∧ y) ⊕ (x ∧ z)`.  \n - *дизъюнкции относительно конъюнкции*: `x ∨ (y ∧ z) = (x ∨ y) ∧ (x ∨ z)`.  \n5) **Инволютивность отрицания** (*закон снятия двойного отрицания*): `¬¬x = x`.\n6) **Дополнительность**:  \n - `a ∧ ¬a = 0`,  \n - `a ∨ ¬a = 1`.  \n7) **Законы де Моргана**:  \n - `¬(x ∧ y) = ¬x ∨ ¬y`,  \n - `¬(x ∨ y) = ¬x ∧ ¬y`.  \n8) **Законы поглощения**:  \n - `x ∧ (x ∨ y) = x`,  \n - `x ∨ (x ∧ y) = x`.\n9) **Свойства констант** `0` и `1`:  \n - `¬0 = 1`,\n - `¬1 = 0`,  \n - `a ∧ 0 = 0`,  \n - `a ∧ 1 = a`,  \n - `a ∨ 0 = a`,  \n - `a ∨ 1 = 1`.  \n\n\n\n# Комбинаторика\n- [О комбинаторике и её объектах](#о-комбинаторике-и-её-объектах)\n- [Чем может быть интересна комбинаторика](#чем-может-быть-интересна-комбинаторика)\n- [Простейшие комбинаторные задачи](#простейшие-комбинаторные-задачи)\n- [Основные комбинаторные правила и принципы](#основные-комбинаторные-правила-и-принципы)\n- [Сочетания](#сочетания)\n- [Перестановки](#перестановки)\n- [Размещения](#размещения)\n- [Партиция и композиция натуральных чисел](#партиция-и-композиция-натуральных-чисел)\n- [Метод “звёзды и столбцы” (stars and bars)](#метод-звёзды-и-столбцы-stars-and-bars)\n\n\n\n## О комбинаторике и её объектах\n\n<!--\n**Комбинаторика** изучает всевозможные **комбинации**, которые можно *составить* из *элементов* некоторого *конечного (счётного) множества*. При этом *основной задачей комбинаторики* является *подсчёт количества* тех *комбинаций*, которые *удовлетворяют условию задачи*.\n-->\n\n**Комбинаторика** (англ. Combinatorics) занимается *подсчётами количества (числа) элементов* во всевозможных *наборах элементов* (*упорядоченных* и *неупорядоченных*, с *уникальными* и *с повторяющимися элементами*). \n\n*Элементы* рассматриваемого *набора* чаще всего *выбираются* из некоторого *множества* или *мультимножества*, то есть *набор представлен* **выборкой элементов** (англ. selection) **из** некоторого **множества** (*мультимножества*).\n\n\n*Объекты*, являющиеся *элементами набора*, могут быть *представлены чем угодно*. Для *комбинаторики важно* лишь *соблюдение условия* **дискретности набора**, то есть *возможности перечислить*, *пересчитать* его *элементы*.\n\n<!--\nСами *объекты* (элементы множества) могут быть *чем угодно* - важно лишь *соблюдение двух условий*:\n* их **дискретность** (возможность перечислить, пересчитать объекты),\n* их **уникальность** (отсутствие повторяющихся объектов).\n-->\n\n\n<!-- Комбинаторика позволяет вычислять, *сколько элементов множества соответствуют* заданному *условию*. -->\n\n## Чем может быть интересна комбинаторика\n\nКомбинаторика *зародилась* и получила своё *развитие* в те времена, когда были *популярны азартные игры* (карты, кости), *ставки* и *лотереи*, поэтому эти виды развлечений будут часто *встречаться* в *условиях комбинаторных задач*.\n\nЕсли вы *любитель* подобных *развлечений*, то *подсчёт* различных *комбинаций* (вариантов) может быть вам не только *интересен*, но и *полезен*.\n\nТем не менее, *развлечения - не единственная область применения* комбинаторики.\n\n*Без* элементов *комбинаторики* не было бы возможным *изучение теории вероятностей*, поскольку *для вычисления вероятности* нужно уметь *вычислять* имеющиеся *комбинации*.\n\nОдно из *применений комбинаторики*: *подсчёт количества информации* (*размера файла*).\n\n<!-- всевозможные варианты исхода таких игр, как карты, кости и других. -->\n\n## Простейшие комбинаторные задачи\n- [Задача #1 (достань карандаш)](#задача-1-достань-карандаш)\n- [Задача #2 (достань шарик)](#задача-2-достань-шарик)\n- [О формулировках комбинаторных задач](#о-формулировках-комбинаторных-задач)\n\nРассмотрим *пару простых задач*.\n\n### Задача #1 (достань карандаш)\n\n«В коробке лежит *4 разноцветных* карандаша. Сколько способов *достать один любой* карандаш?»\n\n#### Ответ\n`4`\n\n#### Объяснение\nПусть в коробке лежали `красный`, `синий`, `зелёный` и `жёлтый` карандаши, тогда мы можем *достать* `либо красный, либо синий, либо зелёный, либо жёлтый` карандаш.\n\n### Задача #2 (достань шарик)\n\n«В мешке лежит *3 белых*, *2 зелёных* и *2 красных* шарика. Сколько способов *достать один белый* шарик?»\n\n#### Ответ\n`3`\n\n#### Объяснение\nВ мешке лежит *3 белых шарика*, значит *можно достать каждый* из них. \n\n### О формулировках комбинаторных задач\n\nХочется ещё раз *отметить* на *примерах выше*, что сам *класс объектов не важен* в *комбинаторных задачах*. В *задачах #1, #2* (как и во многих последующих) могли быть использованы *любые другие объекты*.\n\nНапример, следующая *задача эквивалентна задаче #1*:  \n«В мешочке лежит *3 различных* бочонка для лото. Сколько способов *достать один* бочонок?»\n\nА *задача* ниже *эквивалентна задаче #2*:  \n«В колоде лежит *3 туза*, *2 короля* и *2 дамы*. Сколько способов *достать один* туз?»\n\n\n## Основные комбинаторные правила и принципы\n- [Правило суммы (rule of sum)](#правило-суммы-rule-of-sum)\n- [Правило произведения (rule of product)](#правило-произведения-rule-of-product)\n- [Принцип включений-исключений](#принцип-включений-исключений)\n\n\n### Правило суммы (rule of sum)\n\n<!--\n«Если некоторый объект `А` *можно выбрать* `n` *способами*, а объект `В` - `m` *способами*, то объект `А или В` (`либо A, либо B`) *можно выбрать* `n + m` *способами*. »\n-->\n\n«Если *существует* **`n` способов** *совершить* **действие `A`** и **`m` способов** *совершить* **действие `B`**, причём *невозможно совершить оба действия вместе (одновременно)*, то *существует* **`n + m` способов** *совершить* **действие `А или В`** (`либо A, либо B`). »\n\nЧто значит «*нельзя совершить оба действия одновременно*»? Например, *нельзя одновременно включить* и *выключить свет*; *нельзя одновременно встать*, *сесть* и *лечь* - *результаты* этих *действий несовместимы*, *не зависят друг от друга*.\n\n*Для большего числа действий* *правило суммы* работает *аналогично*. Например, если имеется **действие `C`** и **`k` способов** его *совершить*, то *существует* **`n + m + k` способов** совершить **действие `A или B или C`** при *условии*, что *действия* `A, B, C` *нельзя совершить вместе*.\n\n#### Правило суммы на языке теории множеств\n«Пусть имеется **`n` взаимно непересекающиеся множеств** `A1, A2, ..., An` (`A1 ∩ A2 ∩ ... ∩ An = {}`), тогда *мощность* их *объединения равна сумме мощностей* этих *множеств*:  \n**`|A1| + |A2| + ... + |An| = |A1 ∪ A2 ∪ ... ∪ An|`**.»\n\n### Задача #3 (достань карандаш ещё раз)\n\n«В мешке лежит *3 белых*, *2 зелёных* и *2 красных* шарика. Сколько способов *достать один любой* карандаш?»\n\n#### Ответ\n`7`\n\n#### Объяснение\n\n*Число способов* достать *один белый* карандаш: `3`, *один зелёный*: `2`, *один красный*: `2`. \n\nПо *правилу суммы*: `3 + 2 + 2 = 7`. \n\n### Правило произведения (rule of product)\n\n<!--\n«Если некоторый объект `A` *можно выбрать* `n` *способами*, а *после каждого выбора* объекта `A` *можно выбрать* (независимо от объекта `А`) объект `В` `m` *способами*, то объект `А и В` *можно выбрать* `n • m` *способами*. »\n-->\n\n«Если *существует* **`n` способов** *совершить* **действие `A`** и **`m` способов** *совершить* **действие `B`**, то *существует* **`n • m` способов** *совершить* **действие `А и В`** (*оба действия одновременно*). »\n\n\n*Для большего числа действий правило произведения* работает *аналогично*. Например, если имеется **действие `C`** и **`k` способов** его *совершить*, то *существует* **`n • m • k` способов** совершить **действие `A и B и C`**.\n\n*Действия* в *правиле произведения не обязательно различны*. Если **действие `A`**, которое можно *совершить* **`n` способами**, *выполняется* **`m` раз подряд**, то по *правилу произведения* *существует* **`n^m` способов** сделать это.\n\n<!--\n Если мы `m` *раз подряд выберем один* и тот же *объект* `A` `n` способами, то наша *формула упростится*: `n ^ m` (произведение между `m` объектами `A`).\n\n-->\n\n#### Правило произведения на языке теории множеств\n\n«Пусть имеется **`n` множеств** `A1, A2, ..., An`, тогда *мощность* их *декартова произведения равна произведению мощностей* этих *множеств*:  \n**`|A1| • |A2| • ... • |An| = |A1 × A2 × ... × An|`**.»\n\n### Задача #4 (подбрось монетку)\n\n«*Монетку подбрасывают 5 раз подряд*. Сколько *возможных комбинаций* может выпасть?»\n\n\n#### Ответ\n`32`\n\n#### Объяснение\n\nУ *монетки две стороны* `{ орёл, решка }` - `2` *возможных исхода* при *одном (каждом) подбрасывании*. \n\n*Совершаем* одно и то же *действие* `n = 2` *способами* `m = 5` *раз*. По *правилу произведения*, имеем `n^m = 2^5 = 32` *способа* сделать это.\n\n*Почему* используется *правило произведения*, а не правило суммы? Пусть *после первого* броска выпал `орёл` `и` *после второго* выпала `решка`, `и` *после третьего* - `орёл`, `и` *после четвёртого* - `решка`. Именно `и`, а *не* `или`.\n\n### Задача #5 (сыграем в кости?)\n\n«Подбрасываем *кубик два раза подряд*. Сколько *возможных комбинаций* может выпасть?»\n \n#### Ответ\n`36`\n\n#### Объяснение\nУ кубика *6 граней* с *разных количеством точек* на них { 1, 2, 3, 4, 5, 6 } - `6` *возможных исходов* при *одном (каждом) броске*. \n\n*Совершаем* одно и то же *действие* `n = 6` *способами* `m = 2` *раза*. По *правилу произведения*, имеем `n^m = 6^2 = 36`.\n\n\n### Задача #6 (комбинации пароля)\n\n«Сколько существует *возможных комбинаций* для *составления 8-символьного пароля* из *букв латинского алфавита* и *цифр*?»\n\n#### Ответ\n`62^8`\n\n#### Объяснение\n\nВ *латинском алфавите* `26` *заглавных* (`A-Z`) и `26` *прописных* (`a-z`) *букв*, *цифр* всего `10` (`0-9`). И того *существует* `26 + 26 + 10 = 62` *способа* задать *один символ пароля*.\n\nТогда для того, чтобы *задать 8 символов пароля*, используем *правило произведения*: `62 • 62 • ... • 62` (*8 раз*) = `62^8`.\n\n\n## Принцип включений-исключений\n\n**Принципом включений-исключений** (англ. inclusion-exclusion principle) называется *техника подсчёта мощности объединения* `n` *множеств* `| A1 ∪ A2 ∪ ... ∪ An |` *сложением мощностей всех множеств* `|A1| + |A2| + ... + |An|` и *исключением из* этой *суммы* всего *лишнего*: *элементы* из *пересечений* некоторых *множеств* вошли *в сумму несколько раз*.\n\nВ простейшем случае, когда *объединяются два множеств* `A` и `B`, применение *принципа включений-исключений* имеет вид:  \n`|A ∪ B| = |A| + |B| - |A ∩ B|`.  \n\nЕсли множества `A` и `B` *пересекаются*, то *каждое* из множеств *содержит* в себе *пересечение* `A ∩ B`, которое *при сложении* `|A| + |B|` *учитывается дважды* и поэтому *одно* из *пересечений* необходимо *исключить* из *рассчёта*.\n\nВ случае *трёх множеств* `A, B, C` применение *принципа включений-исключений* имёт вид:  \n`|A ∪ B ∪ C| = |A| + |B| + |C| - |A ∩ B| - |A ∩ C| - |B ∩ C| + |A ∩ B ∩ C|`.\n\nСперва *включается всё*, затем *исключается всё лишнее* (*лишние пересечения*), затем *включается ошибочно исключённое*.\n\nДля *большего числа множеств*, например, `A1, A2, ..., An` *алгоритм применения принципа включения-исключения* следующий:\n1) *Включаем мощность кадого множества*:  \n`|A1| + |A2| + ... + |An|`.\n2) *Исключаем мощность каждого объединения двух множеств*:  \n`- |A1 ∩ A2| - |A1 ∩ A3| - ...`.\n3) *Включаем мощность каждого объединения трёх множеств*:  \n`+ |A1 ∩ A2 ∩ A3| + |A1 ∩ A2 ∩ A4| + ...`.\n4) *Исключаем мощность каждого объединения четырёх множеств*:  \n`- |A1 ∩ A2 ∩ A3 ∩ A4| - |A1 ∩ A2 ∩ A3 ∩ A5| - ...`\n5) И так далее *включаем-исключаем-включаем-исключаем* `n` *раз*.\n\n\n\n## Сочетания\n- [Сочетание](#сочетание)\n- [Сочетание с повторениями](#сочетание-с-повторениями)\n\n<!--\n- [Задача (выбор героев в игре)](#задача-выбор-героев-в-игре)\n- [Число сочетаний](#число-сочетаний)\n- [Число сочетаний с повторениями](#число-сочетаний-с-повторениями)\n-->\n\n### Сочетание\n\n**Сочетанием из `n` по `k`** (англ. k-combination of a set A) называют *неупорядоченный набор* из `k` *уникальных элементов*, *выбранных* из некоторого *множества* `A`, *содержащего* `n` *элементов*. *Каждый элемент множества* `A` может *попасть* в *одно сочетание* только *один раз*. Другими словами, *сочетанием* из `n` по `k` называют *всякое подмножество мощности* `k` *множества мощности* `n`.\n\nПоскольку *понятие сочетания не отличается* от *понятия подмножества*, будем *обозначать сочетания как множества*.\n\nЧисло `k` *не может превышать* число `n`, так как *элементы не могут повторяться*: `k <= n`.\n\n<!--\n*Неупорядоченность набора элементов* означает, что *порядок следования элементов* в нём *не важен*: `{ x, y } = { y, x }`.\n-->\n\n\nНапример, если `A = { 1, 3, 7 }`, то *сочетаниями из трёх по два* являются *неупорядоченные пары*: `{ 1, 3 }`, `{ 1, 7 }`, `{ 3, 7 }`.\n\n#### Число сочетаний\n\n**Числом сочетаний из `n` по `k`** (англ. number of k-combinations) называют *количество всевозможных сочетаний* для *заданных параметров* `k` (*количество элементов* в *сочетании*) и `n` (*мощность множества* `A`, из которого делается *выборка*). \n\n*Число сочетаний равнается биномиальному коэффициенту*:  \n`С(n,k) = n! ÷ ((n-k)!k!)`.\n\nПусть, например, *множество* `A = { a, b, c, d }`, тогда `n = |A| = 4` и тогда: \n1) `C(0, 4) = 4! ÷ (4!0!) = 1` *пустое сочетание*: `{}`.\n2) `C(1, 4) = 4! ÷ (3!1!) = 4` *одноэлементных сочетаний*: `{ a }`, `{ b }`, `{ c }`, `{ d }`.\n3) `C(2, 4) = 4! ÷ (2!2!) = (3 • 4) ÷ 2 = 6` *парных сочетаний*: `{ a, b }`, `{ a, c }`, `{ a, d }`, `{ b, c }`, `{ b, d }`, `{ c, d }`.\n4) `C(3, 4) = 4! ÷ (1!3!) = 4` *трёхэлементных сочетаний*: `{ a, b, c }`, `{ a, b, d }`, `{ a, c, d }`, `{ b, c, d }`.\n5) `С(4, 4) = 4! ÷ (0!4!) = 1` *сочетание*, содержащее *все элементы множества*: `{ a, b, c, d }`.\n6) *Проверка*: `2^4 = C(0, 4) + C(1, 4) + C(2, 4) + C(3, 4) + C(4, 4)`, `1 + 4 + 6 + 4 + 1 = 16 = 2^4`, значит посчитали *правильно*.\n\n\nНапример, в *множестве* из *7 элементов число сочетаний* из *трёх элементов равняется* `C(3, 7) = 7! ÷ (4!3!) = (5 • 6 • 7) ÷ (2 • 3) = 35`.\n\n\n### Сочетание с повторениями\n\n**Сочетанием с повторениями из `n` по `k`** (англ. k-combination with repetitions of a set A, k-multicombination of a set A) называют *неупорядоченный набор* из `k` *элементов*, *выбранных* (*не обязательно единожды*) из *множества* `A`, содержащего `n` *элементов*. Одни и *те же элементы множества* `A` *могут выбираться несколько раз* в *одно сочетание с повторениями*. \n\nФактически, *сочетание с повторениями* из `n` по `k` *эквивалентно мультимножеству мощности* `k`, *состоящему* из *элементов множества мощности* `n`. Поэтому будем *обозначать сочетания с повторениями* как *мультимножества*: `{ 3, 2, 2 }`.\n\nЧисло `k` *может* быть *больше* числа `n`, поскольку *элементы могут повторяться*.\n\nНапример, если *множество* `A = { парень, девушка }`, то *сочетаниями с повторениями из двух по три* являются: `{ парень, парень, парень }`, `{ парень, парень, девушка }`, `{ парень, девушка, девушка }`, `{ девушка, девушка, девушка }`.\n\n\n#### Число сочетаний с повторениями\n\n**Числом сочетаний с повторениями из `n` по `k`** (англ. number of combinations with repetitions) называют *количество всевозможных сочетаний с повторениями* для *заданных параметров* `k` (*количество элементов* в *сочетании с повторениями*) и `n` (*мощность множества* `A`, из которого делается *выборка*). \n\n*Число сочетаний с повторениями обозначается* `(( n k ))` и *выражается* через *биномиальный коэффициент*:  \n`(( n k )) = С(n+k-1,k) = (n+k-1)! ÷ ((n-1)!k!)`.\n\nНапример, в *множестве* из `7` *элементов число сочетаний с повторениями* из *трёх элементов равняется* `C(3, 7 + 3 - 1) = C(3, 9) = 9! ÷ (6!3!) = (7 • 8 • 9) ÷ (2 • 3) = 84`.\n\n\n### Задача (выбор героев в игре)\n«В игре соперничают *две команды*. Сколько существует *способов выбрать 5 героев* из *100 возможных* в *одну команду* и *затем столько же героев* в *другую команду* при *условии*, что *один герой не может браться дважды*?»\n\n#### Ответ\n`С(100, 5) • С(95, 5)`\n\n#### Решение\nВыбор `5` *героев* (*уникальных объектов*) из `100` - *сочетание* из `100` по `5`, то есть `C(100, 5)`.\n\nПоскольку `5` *героев* уже *выбрано* и *герои не могут повторяться*, следующая команда выбирает из `100 - 5 = 95` героев, то есть `C(95, 5)`.\n\nТогда, чтобы *обе команды одновременно сделали выбор* героев, используем *правило произведения*: `С(100, 5) • С(95, 5)`.\n\n## Перестановки\n- [Перестановка множества](#перестановка-множества)\n- [Перестановка мультимножества](#перестановка-мультимножества)\n- [Круговая перестановка](#круговая-перестановка)\n\n### Перестановка множества\n\n**Перестановкой множества `A`** (англ. permutation of a set A) называют *упорядоченный набор*, *содержащий все элементы* (*без повторений*) множества `A` в *определённом порядке*.\n\nИз *определения* следует, что *перестановки* могут *содержать только* **уникальные элементы**.\n\n*Перестановки* удобно *обозначать* как *кортежи*, но они *не являются кортежами* из-за *требования уникальности*. Например, для *множества* `A = { 3, 5, 7 }` существуют *перестановки*: `(3, 5, 7)`, `(3, 7, 5)`, `(5, 3, 7)`, `(5, 7, 3)`, `(7, 5, 3)`, `(7, 3, 5)`.\n\nДля *обозначения переменных*, *содержащих перестановки*, принято использовать *малые буквы греческого алфавита*: `α`, `β`, `σ`, `τ`, `π`. Например, `σ = (5, 7, 3)`.\n\n#### Перестановка как биекция\n\nИногда *перестановку* рассматривают как *биективное отображение* между *элементами множества* `A` и *натуральными числами* `N` (от `1` до *мощности множества* `A`), которые *обозначают номер элемента* в *перестановке*. Таким образом, *каждому числу соответствует один элемент*, а *каждому элементу* - *одно число*. \n\nНапример, в *перестановке* `(снег, град, дождь)` *элементу* `дождь` *соответствует число* `3`, а *числу* `3` *соответствует элемент* `дождь`, но при этом в *перестановке* `(дождь, град, снег)` *элементу* `дождь` уже *соответствует* *число* `1`.\n\n#### Число перестановок множества\n\n**Числом перестановок множества `A` мощности `n`** (англ. number of permutations of a set A) называют *количество всех возможных перестановок* множества `A`. Это число *обозначают* **`P(n)`** и оно *равняется факториалу мощности множества `A`*, то есть **`P(n) = n!`**. \n\nНапример, для *множества* `A = { 8, 16, 32, 64 }` *существует* `P(4) = 4! = 1 • 2 • 3 • 4 = 24` возможных *перестановки*.\n\n\n\n### Перестановка мультимножества\n\n**Перестановкой мультимножества `A`** (англ. multiset permutation) называют *упорядоченный набор элементов конечного мультимножества* `A`, в котором *каждый элемент* `x` *мультимножества* `A` *встречается* ровно *столько раз*, какова его *множественность* `m(x)` в мультимножестве `A`.\n\nНапример, *перестановками мультимножества* `A = { x, x, y }` являются наборы: `(x, x, y)`, `(x, y, x)`, `(y, x, x)`.\n\n#### Число перестановок мультимножества\n\nПусть *мультимножество* `A` *содержит элементы* `x1, x2, ..., xk` и *множественности* этих *элементов равны* `m(x1), m(x2), ..., m(xk)` *соответственно*, тогда `|A| = m(x1) + m(x2) + ... + m(xk) = n` и **число перестановок мультимножества `A`** *равно* **`n! ÷ (m(x1)! • m(x2)! • ... m(xk)!)`**.\n\nНапример, если `A = { x, y, x, z }`, то `|A| = m(x) + m(y) + m(z) = 2 + 1 + 1 = 4 = n` и *число перестановок мультимножества* `A` *равно* `n! ÷ (m(x)! • m(y)! • m(z)!) = 4! ÷ (2! • 1! • 1!) = 3 • 4 = 12`.\n\n\n### Круговая перестановка\n\n*Обычно перестановки* **линейно упорядочены** (англ. linearly ordered), то есть в *каждой* из них *имеется первый*, *второй*, *третий*, ..., *`n`-ый* (*последний*) *элементы*.\n\nТем не менее бывают случаи, когда *реальные объекты расположены по кругу*. *Расположение таких объектов* называется **круговой перестановкой** (англ. circular permutation).\n\nВ *круговой перестановке нет* понятий *первого* и *последнего элемента*: *счёт* можно *начать* с *любого элемента*.\n\n\n#### Преобразование линейно упорядоченной перестановки в круговую\n\n*Линейно упорядоченная перестановка* может быть *преобразована* в *круговую перестановку*, если *условиться*, что за *последним элементом* линейной перестановки *снова следует первый элемент* этой перестановки (*счёт зацикливается*, каждый раз начинаясь сначала).\n\nНапример, *линейно упорядоченная перестановка* `(красный, жёлтый, зелёный)` может быть *преобразована* в *круговую перестановку*, тогда *очерёдность элементов* следующая: `красный, жёлтый, зелёный, красный, жёлтый, зелёный, красный, ...`.\n\n#### Эквивалентность круговых перестановок\n\n*Две круговые перестановки* считаются **эквивалентными**, если *из одной перестановки* можно *получить другую поворотами* по *часовой стрелке*.\n\nТо есть *круговая перестановка* `(a, b, c)` *эквивалентна круговым перестановкам* `(c, a, b)` и `(b, c, a)`.\n\n#### Число круговых перестановок\n\nПоскольку *выбор первого элемента круговой перестановки не важен*, **число круговых перестановок `n` элементов** *равно* **`(n - 1)!`** (в то время, как *число линейно упорядоченных перестановок* равно `n!`).\n\nНапример, *число круговых перестановок* множества `{ a, b, c }` равно `(3 - 1)! = 2! = 2`: `(a, b, c)` и `(a, c, b)`.\n\nНапример, *число круговых перестановок* множества `{ a, b, c, d }` равно `(4 - 1)! = 3! = 6`:  \n```\n(a, b, c, d),\n(a, b, d, c),\n(a, c, b, d),\n(a, c, d, b),\n(a, d, b, c),\n(a, d, c, b).\n```\n\n## Размещения\n- [Размещение](#размещение)\n- [Размещение с повторениями (слово в алфавите)](#размещение-с-повторениями-слово-в-алфавите)\n\n### Размещение\n\n**Размещением из `n` по `k`** (англ. k-permutation of n) называют *упорядоченный набор*, *содержащий* в *определённом порядке* `k` *элементов* (*без повторений*) некоторого множества `A` *мощности* `n`.\n\n*Размещения* тоже будем *обозначать* как *упорядоченные наборы*: `(1, 2, 3)`.\n\nНапример, если `A = { a, b, c, d }`, то `n = |A| = 4` и *размещения из 4 по 2*:  \n```\n(a, b), (b, a),\n(a, c), (c, a),\n(a, d), (d, a),\n(b, c), (c, b),\n(b, d), (d, b),\n(c, d), (d, c).\n```\n\n#### Число размещений\n\n*Число размещений из `n` по `k` обозначается* **`A(n, k)`** в *русскоязычной* литературе, **`P(n, k)`** в *англоязычной* литературе и *равно* **`A(n, k) = P(n, k) = n! ÷ (n - k)! = n • (n - 1) • ... • (n - k + 1)`**.\n\nНапример, существует `A(5, 3) = 5! ÷ 2! = 3 • 4 • 5 = 60` *размещений* из `5` по `2`. Таким образом, в некотором *множестве* `A = { a, b, c, d, e }` можно *выбрать* и *расставить 3 элемента* *60*-ю *способами*.\n\n#### Связь между размещениями, комбинациями и перестановками\n\nПонятие *размещения* является *обобщением понятия перестановки*: *переставляются местами* не все элементы множества `A`, а *заданное количество* `k <= n` этих *элементов*:  \n**`A(n, n) = P(n)`**.\n\n\nМежду *комбинациями*, *перестановками* и *размещениями* существует *связь*. \n\nГоворя простыми словами, *размещение* - это *выбор* `k` элементов из `n` *и перестановка* этих `k` элементов. То есть каждому *размещению* `k` *элементов* из `n` `A(n, k)` *соответствует комбинация* `k` *элементов* из `n` `C(n, k)` и некоторая *перестановка выбранных* `k` *элементов* `P(k)`.\n\n\n*Учитывая* сказанное *выше* , по *правилу произведения* имеем следущее *выражение*:  \n**`A(n, k) = C(n, k) • P(k)`**`= n! ÷ ((n - k)! • k!) • k! = n! ÷ (n - k)!`.\n\n<!--\nНапример, если `A = { a, b, c, d }`, то `n = |A| = 4` и\n1) `A(4, 0) = 1\n2) `A(4, 1) =\n\n-->\n\n### Размещение с повторениями (слово в алфавите)\n\n**Размещением с повторениями множества `A`** (англ. permutation with repetitions of a set A) называют *упорядоченный набор элементов* множества `A`, в котором *некоторые элементы* множества `A` могут *повторяться*, *использоваться несколько раз*.\n\nНапример, для *множества* `{ x, y, z }` *существуют размещения с повторениями* `(x, z, y, x)`, `(y, y, z)` и так далее. \n\nПонятие *“размещение с повторениями”* является *частным случаем кортежа*, *все элементы* которого *принадлежат одному* и тому же *множеству*.\n\n<!--\n, и обычно *не употребляется*, поскольку *не несёт в себе смысла перестановки* (*упорядочивания*) *элементов множества*. \n-->\n\n\n#### Слово длины `k` в алфавите `A`\n\nСамо понятие *“размещение с повторениями” используется* довольно *редко*. Тем не менее но *существует другое*, *более практичное понятие* с *тем же определением*: **слово длины `k` в алфавите `A`, состоящем из `n` символов**.\n\nНапример, *слова* `area`, `rare` являются *некоторыми* из *слов длины* `4` в *алфавите* `A = { a, e, r }`.\n\n#### Число размещений с повторениями\n\n**Число размещений с повторениями длины `k` множества `A` мощности `n`** или, что то же самое, **число слов длины `k` в алфавите `A` мощности `n`** *равняется* **`n^k`**.\n\nНапример, для *множества* `A = { a, b, c }` *существует* `3^2 = 9` *размещений с повторениями длины* `k = 2`: `(a, a)`, `(a, b)`, `(a, c)`, `(b, a)`, `(b, b)`, `(b, c)`, `(c, a)`, `(c, b)`, `(c, c)`.\n\nНапример, для *алфавита* `B = { h, e }` *существует* `2^3 = 8` *слов длины* `k = 3`: `hhh`, `hhe`, `heh`, `ehh`, `hee`, `eeh`, `ehe`, `eee`.\n\n\n## Партиция и композиция натуральных чисел\n- [Партиция натурального числа](#партиция-натурального-числа)\n- [Композиция натурального числа](#композиция-натурального-числа)\n\n### Партиция натурального числа\n\n**Партицией натурального числа `n`** (англ. partition of a positive integer n, integer partition) называют *представление натурального числа `n`* как *суммы натуральных чисел*, в которой *порядок следования слагаемых не важен*, то есть *партиции* `3 + 1 = 4` и `1 + 3 = 4` считаются *одинаковыми*.\n\n*Слагаемое* в *партиции* называют **частью** (англ. part) этой **партиции**. \n\nНапример, для *натурального числа* `n = 3` существует `4` *партиции*:  \n```\n3\n2 + 1 (или 1 + 2)\n1 + 1 + 1\n```\nВ партиции `2 + 1` *слагаемые* `2` и `1` называют *частями* этой *партиции*.\n\n*Части* в одной партиции *могут повторяться* и *порядок следования частей* в партиции *не важен*, поэтому *партицию* можно *рассматривать* как *неупорядоченный набор с повторениями*.\n\nДля удобства *партиции* можно *обозначать* как *мультимножества*. Например, для *натурального числа* `n = 4` существует `5` *партиций*: `{ 4 }`, `{ 3, 1 }`, `{ 2, 2 }`, `{ 2, 1, 1 }`, `{ 1, 1, 1, 1 }`.\n\n<!--\n, или в *другой форме*: `{ (4, 1) }`, `{ (3, 1), (1, 1) }`, `{ (2, 2) }`, `{ (2, 1), (1, 2) }`, `{ (1, 4) }`.\n-->\n\n#### Число партиций натурального числа\n\n**Функцией партиций** (англ. partition function) называют *функцию* **`p(n)`**, которая *принимает натуральное число* `n` и *возвращает число партиций*, *соответствующее числу* `n`.\n\n*Не существует явной формулы*, которая позволяет *вычислить число партиций* (значение функции `p(n)`) для *произвольного* `n`, но существуют *рекуррентные соотношения*, *позволяющие сделать* это их *последовательным применением* `n` *раз*.\n\n*Ниже* представлены *значения функции* `p(n)` при *значениях аргумента* `n` от `1` до `10`.\n`n` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10\n:--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--:\n`p(n)` | 1 | 2 | 3 | 5 | 7 | 11 | 15 | 22 | 30 | 42\n\n### Композиция натурального числа\n\n**Композицией натурального числа `n`** (англ. composition of a positive integer n) называют *представление натурального числа `n`* как *суммы натуральных чисел*, в которой *порядок следования слагаемых имеет значение*, то есть *композиции* `3 + 1 = 4` и `1 + 3 = 4` считаются *различными*.\n\nНапример, для *натурального числа* `n = 4` существует `7` *композиций*:  \n```\n4,\n3 + 1, 1 + 3,\n2 + 1 + 1, 1 + 2 + 1, 1 + 1 + 2,\n1 + 1 + 1 + 1.\n```\n\n*Композицию числа* можно *рассматривать* как *упорядоченный набор с повторениями*, поэтому её бывает удобно *обозначать* как *кортеж*. Например, *обозначать композицию* `1 + 3` как `(1, 3)`, а `3 + 1` как `(3, 1)`.\n\nБудем называть *слагаемое* в *композиции* её **компонентой** (англ. component). Например, в *композиции* `1 + 3` *слагаемое* `1` - это *первая компонента*, а *слагаемое* `3` - *вторая компонента*.\n\n#### Число композиций натурального числа\n\n**Число композиций** **натурального числа `n`** (англ. number of compositions of a positive integer n) *равно* **`2^(n - 1)`**.\n\n#### Слабая композиция\n\n**Слабой композицией натурального числа `n`** (англ. weak composition of a positive integer n) называют *представление натурального числа `n`* как *суммы целых неотрицательных чисел*, то есть *помимо натуральных чисел* в качестве *компоненты может использоваться ноль* `0`.\n\nДля *любого натурального числа существует бесконечно* много *различных слабых композиций* (*если не ограничить число компонент* композиции). Например, `4 = 4 + 0 + 0 + ... + 0`.\n \nНапример, *слабые композиции натурального числа* `3` из `2` *компонент*:\n```\n3 + 0, 0 + 3,\n2 + 1, 1 + 2.\n```\n\nНапример, *слабые композиции натурального числа* `3` из `3` *компонент*, *представленные* в виде *кортежей*:\n```\n(3, 0, 0), (0, 3, 0), (0, 0, 3),\n(2, 1, 0), (2, 0, 1),\n(1, 2, 0), (1, 0, 2),\n(0, 2, 1), (0, 1, 2),\n(1, 1, 1).\n```\n\n\n## Метод “звёзды и столбцы” (stars and bars)\n- [О методе “звёзды и столбцы”](#о-методе-звёзды-и-столбцы)\n- [Теорема #1 (число композиций натурального числа n из k компонент)](#теорема-1-число-композиций-натурального-числа-n-из-k-компонент)\n- [Теорема #2 (число слабых композиций натурального числа n из k компонент)](#теорема-2-число-слабых-композиций-натурального-числа-n-из-k-компонент)\n- [Решение задач методом “звёзды и столбцы”](#решение-задач-методом-звёзды-и-столбцы)\n\n### О методе “звёзды и столбцы”\n\nВ комбинаторике **“звёзды и столбцы”** (англ. stars and bars) - это *графический метод доказательства* некоторых важных *комбинаторных теорем*, который получил *широкое распространение* благодаря *Уильяму Феллеру*.\n\n*Метод* так же *подходит* для *решения* некоторых *классических комбинаторных задач*. Например, *размещение* `k` *одинаковых шаров* в `n` *различных лунках*.\n\n\n### Теорема #1 (число композиций натурального числа n из k компонент)\n«Для *любой пары натуральных чисел* `n` и `k` *число кортежей*, *состоящих* из *`k` натуральных чисел*, *сумма* которых даёт *`n`*, то есть **число композиций натурального числа `n` из `k` компонент** (англ. number of k-compositions), *равно числу подмножеств мощности* `k - 1`, *выбранных* из *множества мощности* `n - 1`, то есть *равно* **`C(n-1, k-1)`**.»\n\nНапример, для `n = 5` и `k = 2` существует `C(n-1, k-1) = C(4, 1) = 4` *композиции*:  \n```\n4 + 1\n3 + 2\n2 + 3\n1 + 4\n```\n\nНапример, для `n = 5` и `k = 3` существует `C(n-1, k-1) = C(4, 2) = 6` *композиций*:  \n```\n3 + 1 + 1\n1 + 3 + 1\n1 + 1 + 3\n2 + 2 + 1\n2 + 1 + 2\n1 + 1 + 2\n```\n\n#### Доказательство методом “звёзды и столбцы”\n\nПредставим, что *натуральное число* `n` - это *число* каких-либо *объектов* (например, *звёзд* `★`). Тогда *число* `n = 5` будет *означать*, что *имеется набор объектов* `★★★★★`.\n\nДля *составления композиции числа* `n = 5` из `k` *компонент*, необходимо *представить число* `5` в виде *суммы нескольких натуральных чисел*, что *эквивалентно разбиению набора* `★★★★★` на `k` *наборов меньшей длины*, *каждый* из которых *представляет* некоторое *натуральное число* и поэтому *должен содержать не менее одного объекта* `★`.\n\nБудем *разбивать набор объектов столбцами* `|`. Тогда *композиция* `2 + 3` *эквивалентна* `★★|★★★`, *композиция* `3 + 1 + 1` *эквивалентна* `★★★|★|★` и так далее.\n\nМожно заметить, что для *составления композиции* из `k` *компонент необходимо расставить* **`k - 1` столбцов**.\n\nПоскольку *каждый набор содержит не менее одного объекта*, *столбец* можно разместить *только между двумя звёздами* (*наборы* `|★★|★★★` и `★★||★★★` нам *не подходят*). Тогда *между* `n` *звёздами существует* ровно **`n - 1` ячеек**, в которые можно *разместить столбцы*. Например, если *обозначить ячейку символом* `•`, то *доступные ячейки обозначаются* `★•★•★•★•★`.\n\nТаким образом, необходимо *разместить* `k - 1` *столбцов* в `n - 1` *ячеек*, иначе говоря, *выбрать* `k - 1` *элементов* из `n - 1`, что равно *числу сочетаний из `n - 1` по `k - 1`*, то есть **С(n-1,k-1)**.\n\n#### Замечание\n\nЕсли *сложить все композиции натурального числа* `n` из `k` *компонент* от `k = 1` до `k = n`, получится **число композиций натурального числа `n`**, которое *равно* **`2^(n - 1)`**.\n\n### Теорема #2 (число слабых композиций натурального числа n из k компонент)\n\n«Для *любой пары натуральных чисел* `n` и `k` *число кортежей*, *состоящих* из *`k` целых неотрицательных чисел*, *сумма* которых даёт *`n`*, то есть **число слабых композиций натурального числа `n` из `k` компонент**, *равно числу мультимножеств мощности* `k - 1`, *выбранных* из *множества мощности* `n + 1`, то есть *равно* **`(( n+1, k-1 )) = С(n+k-1, k-1)`**.»\n\nНапример, для `n = 5` и `k = 2` существует `C(n+k-1, k-1) = C(6, 1) = 6` *слабых композиций*:  \n```\n5 + 0\n4 + 1\n3 + 2\n2 + 3\n1 + 4\n0 + 5\n```\n\nНапример, для `n = 5` и `k = 3` существует `C(n+k-1, k-1) = C(7, 2) = 21` *слабая композиция*.\n\n#### Доказательство методом “звёзды и столбцы”\n\n*Доказательство этой теоремы* очень *похоже* на *доказательство предыдущей*. *Натуральное число* `n = 5` так же *представляется набором объектов* `★★★★★`, а в случае *композиции* этот *набор объектов разделяется* на *несколько наборов столбцами* `|`. \n\n*Основное отличие* заключается в том, что в *слабой композиции* в качестве *компонент* могут использоваться *нули*, а значит *наборы могут* быть *пустыми* (могут *не содержать объектов* `★`). Например, *обозначение* `|★★|★★★` *соответствует композиции* `0 + 2 + 3`, а *обозначение* `★★||★★★` *соответствует композиции* `2 + 0 + 3`. \n\nИтак, чтобы *составить композицию* из `k` *компонент*, мы, как и прежде, *расставляем* **`k - 1` столбцов**. \n\n<!--\nЕсли не понятно, почему так, то представьте, что вы режете торт: 1 надрез даёт 2 кусочка, 2 надреза - 3 кусочка и так далее.\n-->\n\nПоскольку *набор может не содержать объектов*, *столбец* `|` можно разместить *где угодно*. Тогда *между* `n` *звёздами существует* ровно **`n + 1` ячеек**, в которые можно *разместить столбцы*. Например, если *обозначить ячейку символом* `•`, то *доступные ячейки обозначаются* `•★•★•★•★•★•`. Более того, в *одну ячейку* может быть *размещено несколько столбцов*. Например, `★★||★★★`.\n\nТаким образом, необходимо *разместить* `k - 1` *столбцов* в `n + 1` *ячеек*, причём *одна ячейка* может *содержать несколько столбцов*. Иначе говоря, необходимо сделать *множественный выбор* `k - 1` *элементов* из `n + 1`, что равно *числу сочетаний с повторениями из `n + 1` по `k - 1`*, то есть **`(( n+1, k-1 )) = С(n+k-1, k-1)`**.\n\n### Решение задач методом “звёзды и столбцы”\n\n*Доказанные выше теоремы* позволяют *решать* многие важные *комбинаторные задачи*. Например, *деление пирога* или *деление прибыли* между *несколькими людьми*, *расстановка вещей* на *полки*, а также многие другие задачи. Все *эти задачи решаются* либо использованием *доказанных формул*, либо *аналогично* тому, как *доказывались теоремы*.\n\n\nПримерная *формулировка задач* для *теоремы #1*:  \n«Имеется `n` *одинаковых объектов* и `k` *различных ячеек*. Сколько существует *способов разместить объекты в ячейки*, если *каждая ячейка содержит не менее одного объектаф*»\n\nПримерная *формулировка задач* для *теоремы #2*:  \n«Имеется `n` одинаковых объектов и `k` различных ячеек. Сколько существует *способов разместить объекты в ячейки*, если *каждая ячейка* может *содержать ни одного, один* или *несколько объектов*?»\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.\n"
  },
  {
    "path": "Docker.md",
    "content": "- [Контейнеры и контейнеризация](#контейнеры-и-контейнеризация)\n  - [Сравнение контейнера и виртуальной машины](#сравнение-контейнера-и-виртуальной-машины)\n  - [Преимущества контейнеров](#преимущества-контейнеров)\n- [Docker](#docker)\n  - [Архитектура Docker](#архитектура-docker)\n  - [Этапы докеризации приложения](#этапы-докеризации-приложения)\n  - [Хранение данных в Volume](#хранение-данных-в-volume)\n- [Dockerfile](#dockerfile)\n  - [Использование образов с помощью FROM](#использование-образов-с-помощью-from)\n  - [Копирование файлов с помощью ADD И COPY](#копирование-файлов-с-помощью-add-и-copy)\n  - [Запуск команд с помощью RUN и CMD](#запуск-команд-с-помощью-run-и-cmd)\n  - [Рабочая директория WORKDIR](#рабочая-директория-workdir)\n  - [Аргументы ARG](#аргументы-arg)\n  - [Переменные окружения ENV](#переменные-окружения-env)\n  - [Порты и инструкция EXPOSE](#порты-и-инструкция-expose)\n  - [Пример Dockerfile для NodeJS](#пример-dockerfile-для-nodejs)\n- [Docker Compose](#docker-compose)\n  - [Композиция контейнеров](#композиция-контейнеров)\n  - [Сервисы](#сервисы)\n  - [Порядок запуска сервисов](#порядок-запуска-сервисов)\n  - [Порты](#порты)\n  - [Пример композиции контейнеров трёхуровнего приложения](#пример-композиции-контейнеров-трёхуровнего-приложения)\n  - [Переменные ENVIRONMENT и ARGS](#переменные-environment-и-args)\n  - [Передача и использование аргументов в Docker Compose](#передача-и-использование-аргументов-в-docker-compose)\n  - [Проверка конфигурации](#проверка-конфигурации)\n\n# Контейнеры и контейнеризация\n\n**Контейнер** (Container) — стандартная единица ПО, в которую упаковывается приложение со всеми необходимыми для его полноценной работы зависимостями (кодом, средой запуска, библиотеками и настройками).\n\n## Сравнение контейнера и виртуальной машины\n\nБудем называть **хост-машиной** (host machine) компьютер (сервер), ресурсы которого выделяются под контейнер или виртуальную машину.\n\n*Контейнер* — *процесс* или *сервис*, который *напрямую запущен на хост-машине*. \n\n*Docker-демон* следит за тем, чтобы контейнер запускался в полной изоляции от операционной системы хост-машины. Ничего подобного виртуальной машине при этом не создаётся.\n\n**Виртуальная машина** (Virtual Machine) — *изолированная операционная подсистема на хост-машине*.\n\n С помощью виртуальной машины можно внутри Windows ОС запустить Linux и наоборот. Существует множество инструментов, чтобы работать с виртуальными машинами (например, Virtual Box).\n\n## Преимущества контейнеров\n* Возможность упаковать приложение вместе с его средой запуска. Это позволяет контейнеру запускаться одинаково в разных окружениях (операционных системах) и решает пооблему их настройки (подготовки к запуску приложения), а значит на каждом компьютере запуск контейнера происходит одинаково. \n* Поскольку в контейнерах содержится только самое необходимое (ничего лишнего), им свойственны легковесность, быстродействие и простота настройки.\n\n# Docker\n\n## Архитектура Docker\n\n*Docker* использует *клиент-серверную архитектуру*. \n\nС клиента, который называется **Docker-клиент** (Docker client), поступают CLI-команды. \n```js\n/* примеры CLI-команд */\ndocker build\ndocker ps\ndocker run\n```\n\nКлиент при помощи REST API передаёт команды серверу, который называется **Docker-демон** (Docker daemon).\n\n*Docker-демон* собирает, запускает и раздаёт (distribute) контейнеры.\n\n## Этапы докеризации приложения\n\n- [Создание Dockerfile](#создание-dockerfile)\n- [Построение образа](#построение-образа)\n- [Создание и запуск контейнера](#создание-и-запуск-контейнера)\n- [Композиция нескольких контейнеров](#композиция-нескольких-контейнеров)\n\n### Создание Dockerfile\n\n**Dockerfile** содержит **инструкции** (instructions) — последовательность действий, которые нужно выполнить, чтобы [построить образ](#построение-образа). \n\nПростой пример Dockerfile для NodeJS-приложения.\n```Dockerfile\n# Dockerfile\nFROM node:latest\nEXPOSE 3001\n\nWORKDIR /app\n\nCOPY ./package.json .\nCOPY ./src ./src\n\nRUN npm install\nRUN npm run build\n\nCMD npm run start\n```\n\nКонкретные инструкции Dockerfile разобраны [здесь](#dockerfile). \n\n### Построение образа\n\n**Образ** (Image) — доступный только для чтения шаблон с инструкциями о том, как запустить какой-то Docker-контейнер, содержащий внутри себя всё необходимое для выполнения этих инструкций. \n\nОдин образ может расширять другой.\n\n**Базовый образ** (Base Image) — образ, который *не имеет родительского образа*.\n\nДля *построения образа* используется команда `docker build`, которая принимает **контекст** (context) — путь к папке, с которой будет происходить работа в Dockerfile.\n\n```js\n/* узнать текущую папку консоли */\nls\n\n/* \".\" означает, что текущая папка консоли взята в качестве контекста */\ndocker build . \n```\n\nОбраз строится на основании Dockerfile, который по умолчанию берётся из корня контекста.\n\nКаждая инструкция в Dockerfile создаёт новый слой (layer) в образе. \n\n<!-- Когда Dockerfile изменяется и образ пересобирается (rebuild), пересобираются только изменённые слои. -->\n\nЕсли Dockerfile лежит не в корне контекста, то можно явно указать путь к файлу.\n```cmd\ndocker build -f /path/to/a/Dockerfile .\n```\nМожно задать явное название образа.\n```cmd\ndocker build -t your_image_name .\n```\n\nНайти созданный образ можно среди других образов при помощи команды `docker images`.\n```js\ndocker build -t test .\ndocker images\n\n/*\n  REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE\n  test                   latest              9468e6677939        22 seconds ago      730MB\n  mongo                  3.4                 aeaac14e1ffb        5 months ago        429MB\n  redis                  4.0                 04c446bf216f        5 months ago        89.2MB\n  node                   10.15.3             5a401340b79f        10 months ago       899MB\n*/\n```\n\nОбразы хранятся в Docker-реестре (Docker registry). \n\nОдним из публичных реестров является Docker Hub. Он используется по умолчанию.\n```js\n// загрузить image из реестра\ndocker pull <image>\n// загрузить image в реестр\ndocker push <image>\n```\n\n### Создание и запуск контейнера\n\n**Контейнер** (Container) — *запускаемый экземпляр образа*. \n\nДля создания и последующего запуска контейнера по образу можно спользовать команду `docker run`.\n```js\ndocker run -d image_name\n```\nФлаг `-d` используется для запуска контейнера в фоновом режиме (in background), таким текущая консоль не будет занята контейнером и можно будет вводить в неё другие команды.\n\nМожно также задать явное имя контейнеру при создании.\n```js\ndocker run -d --name container_name image_name\n```\n\nКоманда `docker run` объединяет в себе две команды: `docker create` и `docker start`.\n```js\n/* создание контейнера */\ndocker create image_name\n/* запуск ещё не запущенного контейнера */\ndocker start container_id\n```\n\nДля просмотра списка всех запущенных контейнеров и информации о них используется команда `docker ps`.\n```js\ndocker ps\n\n/*\nCONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES\nb56a528cbe78        test                \"docker-entrypoint.s…\"   2 days ago          Up About a minute   3001/tcp           fervent_brattain\n*/\n```\nДля просмотра всех контейнеров (в том числе и незапущенных) используется флаг `-a`.\n```js\ndocker ps -a\n```\n\nЕсли есть необходимость посмотреть, что лежит внутри запущенного контейнера, можно зайти в него при помощи команды `docker exec`.\n```js\ndocker exec -i -t container_id bash\n/* осуществляется переход в интерактивный режим */\n```\nПример работы в интерактивном режиме.\n```js\n/* вывод названий файлов и папок в текущей директории \"app\" */\nroot:/app# ls\n\n/* вывод файла \"package.json\" в консоль */\nroot:/app# cat package.json\n\n/* переход в папку \"src\" */\nroot:/app# cd ./src\n\n/* выход из интерактивного режима */\nroot:/app/src# exit \n```\n\nФлаг `-i` отвечает за переход в интерактивный режим, флаг `-t` позволяет эмулировать терминал.\n\nДля остановки контейнера используется команда `docker stop`.\n```js\ndocker stop container_id\n```\n\n### Композиция нескольких контейнеров\n\nДля написания приложения чаще всего не достаточно одного контейнера. \n\nЕсли есть необходимость иметь несколько контейнеров в одном приложении, то нужно каждый из них настроить, как указано в этапах выше.\n\nОбычно контейнеры зависят друг от друга, поэтому их запуск должен осуществляться в строгой последовательности. Такой запуск называется композицией контейнеров.\n\nЧтобы было проще запускать композицию контейнеров, её шаги описывается в отдельном файле при помощи [Docker Compose](#docker-compose).\n\n## Хранение данных в Volume\n\n**Volume** — предпочитительный механизм для хранения данных (persisting data), используемых в Docker-контейнере.\n\nVolume даёт контейнеру доступ к какой-то локальной папке на хост-машине, на которой этот контейнер запущен. Файлы из Volume нельзя использовать на этапе сборки (build-time), то есть в Dockerfile нельзя использовать файлы из Volume, они доступны лишь во время выполнения (run-time).\n\n### Почему следует использовать Volume\n\n* Volume хранится вне контейнера, поэтому он не увеличивает размер контейнера и не подвержен влиянию жизненного цикла контейнера.\n\n\n# Dockerfile\n\n## Использование образов с помощью FROM\n\n**Инструкция FROM** инициализирует новый этап сборки и устанавливает *базовый образ*, функциональность которого может быть использована в последующих инструкциях. \n```dockerfile\n# Dockerfile\nFROM ubuntu:latest\n```\n```dockerfile\n# Dockerfile\n\n# образ для NodeJS\nFROM node:latest \n\n# ...\n\n# npm доступен благодаря образу node\nRUN npm i \n```\n\n*Валидный Dockerfile* должен *содержать* как минимум *одну инструкцию FROM* и она должна быть *первой инструкцией* в файле.\n\nДля создания базового образа используется инструкция `FROM scratch`.\n\n## Копирование файлов с помощью ADD И COPY\n\n**Инструкция COPY** позволяет скопировать локальный файл или папку с хост-машины в образ. Она принимает два параметра: относительный путь на хост-машине, откуда копировать, и абсолютный путь, по которому данные будут доступны в контейнере.\n```dockerfile\n# Dockerfile\nCOPY ./package.json /app/\nCOPY ./src /app/src\n```\n**Инструкция ADD** может делать то же самое, но помимо этого она может принять URL как источник для копирования или разархивировать локальный `.tar` файл, а затем поместить в образ.\n```dockerfile\n# Dockerfile\nADD ./package.json /app/\nADD ./src /app/src\n\nADD archive.tar.gz /\nADD http://some_url.here /\n```\n\nЕсли нет явной необходимости в ADD, лучше использовать COPY.\n\n## Запуск команд с помощью RUN и CMD\n\n**Инструкция RUN** позволяет запускать команды внутри образа (image). Эти команды запускаются один раз во время сборки (build) и записываются в образ как новый слой (layer).\n```dockerfile\n# Dockerfile\nRUN echo \"Install modules\"\nRUN npm install\n```\n\n**Инструкция CMD** описывает *команду по умолчанию*, которая должна *запускаться при запуске готового образа, то есть контейнера*. \n```dockerfile\n# Dockerfile\nCMD npm start\n```\nТаким образом, несмотря на то, что CMD является инструкцией Dockerfile, он запускается не во время сборки, а уже в запущенном контейнере. Чаще всего командой в CMD выступает запуск сервера.\n\n## Рабочая директория WORKDIR\n\n**Инструкция WORKDIR** устанавливает *рабочую директорию* для инструкций `RUN`, `CMD`, `COPY`, `ADD`. Все действия, связанные с перечисленными инструкциями, будут происходить в заданной при помощи `WORKDIR` директории.\n```dockerfile\n# Dockerfile\nWORKDIR /app\n```\nПо умолчанию используется `WORKDIR /`.\n \nИнструкция `WORKDIR` может быть использована несколько раз.\n```dockerfile\nWORKDIR /\nCOPY ./package.json /app/\n\nWORKDIR /app\nCOPY ./src ./src\n```\n\n## Аргументы ARG\n**Инструкция ARG** определяет переменную, которую можно передать во время сборки (build-time) контейнера.\n\n* Объявление аргументов в Dockerfile.\n```dockerfile\n# Dockerfile\nARG argument_name\nARG another_argument_name=default_value # со значением по умолчанию\n```\n* Использование аргументов в Dockerfile.\n```dockerfile\n# Dockerfile\nRUN echo ${argument_name}\nRUN echo ${another_argument_name}\n```\n* Передача аргументов в команду сборки контейнера.\n```cmd\ndocker-compose build --build-arg port=3000 --build-arg env=\"local\"\n```\n```dockerfile\n# Dockerfile\nARG port\nARG env\n```\n\n## Переменные окружения ENV\n\n**Инструкция ENV** сохраняет переменную внутри контейнера. Таким образом переменная в контейнере доступна во время выполнения (run-time).\n* Объявление переменных окружения в Dockerfile. \n```dockerfile\n# Dockerfile\nENV env=production\n```\n* Передача в команду запуска контейнера.\n```cmd\ndocker run -e env=production\n```\n\nЕсли есть необходимость передать аргумент как переменную окружения, то можно сделать это следующим образом.\n```dockerfile\n# Dockerfile\nARG port\nENV port=${port}\n```\n\n## Порты и инструкция EXPOSE\n\nРанее уже отмечалось, *контейнеры* достаточно *изолированы от окружающего мира*, но иногда это *можно контролировать*.\n\nЕсли *внутри контейнера запущено приложение* на каком-то порте (например, `3000`), то оно будет *доступно только внутри контейнера*. Проверить, что оно действительно запущено в контейнере можно, сделав запрос на URL из консоли контейнера.\n```cmd\ndocker exec -it container_id bash\ncurl \"http://localhost:3000\"\n```\nПри этом *у хост-машины нет доступа* к *приложению*, запущенному *в контейнере*.\n\n**Инструкция EXPOSE** используется в целях документации, позволяя явно указать, какие порты используются внутри контейнера.\n```Dockerfile\n# Dockerfile\nFROM node:12.13.1\n\nEXPOSE 3000\n```\n\nЕсли *порт контейнера выставляется наружу*, то он называется **выставленным** (exposed).\n\nМожно также вместо *инструкции EXPOSE* *выставить порт* при помощи *флага* `--expose`.\n```cmd\ndocker run --expose 3000 your_image\n```\n\n*Выставление порта* *не* является *обязательным*, поскольку оно *не предоставляет хост-машине доступ к приложению*.\n\nЧтобы *предоставить доступ хост-машине*, необходимо **опубликовать** (publish) *порт*. В таком случае *порт* называют **опубликованным** (published).\n\nПри *создании контейнера* у него *по умолчанию* *нет опубликованных портов*. \n\nДля *публикации порта* используется *флаг* `--publish`, `-p`. Флаг принимает *порт хост-машины* и *порт контейнера* в формате `hostPort:containerPort`.\n```cmd\ndocker run --publish 4000:3000 your_image\n```\nВ примере выше приложение, которое запущено внутри контейнера на порте `3000`, также доступно и на хост-машине на порте `4000` (`http://localhost:4000`).\n\nМожно также *опубликовать* сразу *все выставленные порты контейнера* на *случайные порты хост-машины* при помощи *флага* `--publish-all`, `-P`.\n```cmd\ndocker run --publish-all your_image\n```\n*Публикация* на *случайные порты* хост-машины *не удобна* из-за трудности конфигурации приложений, которые от этих портов зависят.\n\n*Несколько портов лучше публиковать* следующим образом.\n```cmd\ndocker run -p 3000:3000 -p 3001:3001 your_image\n```\n\n## Пример Dockerfile для NodeJS\n```Dockerfile\n# Dockerfile\nFROM node:12.13.1\n\nARG port\nARG env\n\nEXPOSE ${port}\nCOPY ./package.json /app/\nCOPY ./src /app/src\n\nWORKDIR /app\n\nRUN npm install\nRUN NODE_ENV=${env} npm run build\n\nCMD npm run start\n```\n```cmd\ndocker build --build-arg port=3001 --build-arg env=staging .\n```\n\n# Docker Compose\n\n## Композиция контейнеров\n\nЧаще всего приложения можно разделить на несколько контейнеров, которые зависят друг от друга. Понятно, что, чтобы приложение заработало, эти контейнеры нужно запускать вместе, причём в определённом порядке. Такой запуск называется **композицией контейнеров**.\n\n**Docker Compose** — инструмент, позволяющий *составлять композицию контейнеров* (запускать приложения, состоящие из нескольких контейнеров).\n\nDocker Compose использует файлы формала YAML (`.yml`).\n\n## Сервисы\n\nDocker Compose файл состоит из **сервисов** (services). Сервис содержит в себе все данные, необходимые для запуска конкретного контейнера (с предварительным созданием образа для него при необходимости). \n\nЕсть два способа запустить сервис.\n* Можно указать готовый образ в поле `image` (можно скачать его с Docker Hub или создать самому).\n```yaml\n# docker-compose.yml\nversion: '3.7'\nservices:\n  foo:\n    image: image_name\n```\n* Можно настроить этап построения в поле `build`, указав там путь к Dockerfile, по которому должен быть построен образ.\n```yaml\n# docker-compose.yml\nversion: '3.7'\nservices:\n  bar:\n    build:\n      context: ./bar\n      dockerfile: Dockerfile\n```\n\n## Порядок запуска сервисов\n\nDocker Compose запускает и останавливает контейнеры в порядке их зависимостей. \n\nЕсли зависимости между контейнерами не указаны, то контейнеры запускаются последовательно.\n\nНастроить зависимость можно при помощи поля `depends_on`.\n\nВ примере ниже запуск сервиса `server` произойдёт раньше, чем запуск `client`.\n```bash\ndocker-compose up\n```\n```yml\n# docker-compose.yml\nversion: '3.7'\nservices:\n  client:\n    image: image_name\n    depends_on: server\n  server:\n    image: another_image_name\n```\nВ случае остановки происходит обратная ситуация: `client` останавливается раньше, чем `server`.\n```bash\ndocker-compose stop\n```\n\nЗдесь важно отметить, что Docker Compose дожидается лишь окончания запуска контейнера, но не полной готовности того, что лежит внутри него. К примеру, база данных в контейнере может быть не готова к соединениям в тот момент, когда контейнер только запустился. В таких случаях нужно конфигурировать приложение таким образом, чтобы подключение к базе данных повторялось через некоторое время после каждой неудачной попытки.\n\n<!-- Когда запускается Docker Compose, он сначала обрабатывает все `build` -->\n\n## Порты\n\nМожно указать порты на хост-машине и внутри контейнера, по которым будет доступно приложение.\n\nСервис `foo` будет доступен на порте 3000 внутри контейнера.\n```yaml\n# docker-compose.yml\nversion: '3.7'\nservices:\n  foo:\n    image: image_name\n    ports:\n     - \"3000\"\n```\nСервис `bar` будет доступен на порте 4000 внутри контейнера и на порте 3001 на хост-машине.\n```yaml\n# docker-compose.yml\nversion: '3.7'\nservices:\n  bar:\n    image: image_name\n    ports:\n     - \"3000:4000\"\n```\nКонфигурация сервиса `baz` эквивалетна конфигурации сервиса `foo`.\n```yaml\n# docker-compose.yml\nversion: '3.7'\nservices:\n  baz:\n    image: image_name\n    ports:\n     - target: 4000 # порт, выставленный внутри контейнера\n       published: 3000 # опубликованный порт (на хост-машине)\n```\n\n\n## Пример композиции контейнеров трёхуровнего приложения\n\n*Трёхуровневое* (3-tier) *приложение* состоит из клиента, сервера и базы данных. Для каждого уровня необходим отдельный контейнер, а поскольку они связаны друг с другом, создаётся их композиция.\n\nПервым подключается база данных (сервис `db`), поскольку её может использовать сервер. Вторым подключается сервер (сервис `server`), поскольку его может использовать клиент. Последним подключается клиент (сервис `client`).\n\nВ docker-compose может указываться уже собранный образ (builded image) вместе с командой, которая должна быть запущена в контейнере; или `Dockerfile`, по котому образ будет создаваться.\n```yaml\n# docker-compose.yml\nversion: '3.7'\nservices:\n  db:\n    command: mongod\n    image: mongo:3.6.3\n    ports:\n      - \"27017:27017\"\n  server:\n    build:\n      context: \"./server\"\n      dockerfile: Dockerfile\n    ports:\n      - \"4001:4001\"\n  client:\n    build:\n      context: \"./client\"\n      dockerfile: Dockerfile\n    ports:\n      - \"4000:4000\"\n```\n\n## Переменные ENVIRONMENT и ARGS\n\n**Перееменные окружения ENVIRONMENT** передаются в уже запущенные контейнеры. \n```yaml\n# docker-compose.yml\nclient:\n  environment:\n    - NODE_ENV: production\n    - SERVER_URL: xxx\n```\n\n<!-- *Docker Compose* поддерживает объявление значений по умолчанию для переменных окружения в `.env` файле. -->\n\n**Переменные ARGS** доступны во время построения образа (build image).\n\n```yaml\n# docker-compose.yml\nclient:\n  build:\n    args:\n      - port: 3000\n      - env: production\n```\n\n## Передача и использование аргументов в Docker Compose\n* Передача любых аргументов осуществляется при запуске Docker Compose в формате `argument=value`.\n```cmd\nCLIENT_PORT=3000 ENV=production docker-compose up --build\n```\n* Переданные аргументы доступны для использования в YAML-файле в формате`${argument}`. Есть возможность задать значение по умолчанию: `${argument:-defaultValue}`. Без некоторых значений по умолчанию (например, для портов) может возникать ошибка приведения типов.\n```yml\n# docker-compose.yml\nversion: '3.7'\nservices: \n  client:\n    build:\n      context: \"./client\"\n      dockerfile: Dockerfile\n      args:\n        port: ${CLIENT_PORT}\n        env: ${ENV}\n    environment:\n      NODE_ENV: ${ENV}\n    ports:\n      - \"${CLIENT_PORT:-3000}:${CLIENT_PORT:-3000}\" # по умолчанию 3000:3000\n```\n\n## Проверка конфигурации\nМожно проверить правильность настройки, а также посмотреть все установленные переменные при помощи следующей команды.\n```cmd\ndocker-compose config\n```\n"
  },
  {
    "path": "Elasticsearch.md",
    "content": "- [Базовые понятия Elasticsearch](#базовые-понятия-elasticsearch)\n  - [Индекс, тип, документ](#индекс-тип-документ)\n  - [Полнотекстовый поиск и индексирование](#полнотекстовый-поиск-и-индексирование)\n  - [Реплики и шарды](#реплики-и-шарды)\n  - [Перевёрнутые индексы](#перевёрнутые-индексы)\n- [Маппинги и типы данных](#маппинги-и-типы-данных)\n  - [Создание индекса](#создание-индекса)\n  - [Простые типы данных](#простые-типы-данных)\n  - [Составные типы данных](#составные-типы-данных)\n  - [Специализированные типы данных](#специализированные-типы-данных)\n  - [Мультиполя](#мультиполя)\n  - [Получение текущих маппингов](#получение-текущих-маппингов)\n- [Работа с данными](#работа-с-данными)\n  - [Создание индекса](#создание-индекса)\n  - [Создание типа](#создание-типа)\n  - [Удаление индекса](#удаление-индекса)\n  - [Создание документа](#создание-документа)\n  - [Обновление документа по ID](#обновление-документа-по-id)\n  - [Удаление документа по ID](#удаление-документа-по-id)\n  - [Группировка нескольких запросов в один (bulk)](#группировка-нескольких-запросов-в-один-bulk)\n- [Поиск документов](#поиск-документов)\n  - [Вид запроса, параметры запроса и ответ](#вид-запроса-параметры-запроса-и-ответ)\n  - [Query DSL](#query-dsl)\n  - [Основные запросы Elasticsearch](#основные-запросы-elasticsearch)\n  - [Поиск по нескольким полям](#поиск-по-нескольким-полям)\n  - [Пагинация](#пагинация)\n  - [Релевантность, контекст запроса и контекст фильтра](#релевантность-контекст-запроса-и-контекст-фильтра)\n  - [Фильтрация](#фильтрация)\n  - [Сортировка](#сортировка)\n  - [Обработка больших объёмов данных (scroll)](#обработка-больших-объёмов-данных-scroll)\n- [Анализаторы](#анализаторы)\n  - [Встроенные анализаторы](#встроенные-анализаторы)\n  - [Пользовательские анализаторы](#пользовательские-анализаторы)\n  - [Составляющие анализатора](#составляющие-анализатора)\n  - [Граф токенов](#граф-токенов)\n  - [Нормализация](#нормализация)\n  - [Анализатор индекса и анализатор поиска](#анализатор-индекса-и-анализатор-поиска)\n- [Токенизаторы](#токенизаторы)\n  - [Ориентированные на слова](#ориентированные-на-слова)\n  - [Токенизаторы частичных слов](#токенизаторы-частичных-слов)\n  - [Токенизаторы структурированного текста](#токенизаторы-структурированного-текста)\n- [Настройки индекса (settings)](#настройки-индекса-settings)\n  - [Получение текущих настроек индекса](#получение-текущих-настроек-индекса)\n- [Установка Elasticsearch на ПК](#установка-elasticsearch-на-пк)\n  \n\n# Базовые понятия Elasticsearch\n\n**Elasticsearch** (эластичный поиск) — распределённый (distributed), RESTful поисковой движок по всему тексту (full-text search).\n\n*Elasticsearch* использует JSON-документы без схемы. Эти документы передаются при помощи REST API для сохранения их в хранилище и поиска.\n\n*Elasticsearch* построен поверх поискового движка **Apache Lucene**, написанном на *Java*.\n\n## Индекс, тип, документ\n**Индекс** (Index) — эквивалент *базы данных* в SQL или NoSQL.\n\n**Тип** (Type) — эквивалент *таблицы* в SQL или *коллекции* в NoSQL.\n\nИмеется *тип по умолчанию*, который *создаётся автоматически* (`_doc`) при *создании индекса*.\n\n**Документ** (Document) — эквивалент *строки* в SQL или *документа* в NoSQL.\n\nДокументы хранятся в JSON-формате\n```js\n {\n    \"_index\": \"notes\",\n    \"_type\": \"_doc\",\n    \"_id\": \"sYKRT3EBYbOH8y9AHDYY\",\n    \"_source\": {\n      \"name\": \"Elasticsearch\",\n      \"author\": \"Max-Starling\",\n      \"date\": \"2020-04-07T23:15:50Z\"\n    }\n}\n```\n\n## Полнотекстовый поиск и индексирование\n\n**Полнотекстовый поиск** (Full text searching) — *поиск документов* не по их идентификаторам, а *по их содержимому*.\n\n**Индексирование** (Indexing) в *поисковых системах* — процесс *добавления сведений* (о сайте) *роботом поисковой машины* в *базу данных*, которая используется для *полнотекстового поиска информации* на *проиндексированных сайтах*.\n\nВ рамках *Elasticsearch* **индексированием** называют *запись данных* в *индекс*.\n\n## Реплики и шарды\n\n*Индексы* обычно *разредяются* на *несколько подиндексов* (sub-indices), называемые **осколками**, **шардами** (shards). *Каждый шард хранит* какую-то *часть документов индекса*. \n\n**Шардинг** (Sharding) — процесс разбиения на шарды и одна из стратегий масштабирования баз данных.\n\n*Шарды распределяются* между *несколькими узлами, экземплярами приложения* (Elasticsearch nodes). \n\n*Каждый шард* является *экземпляром дижка Lucene*. Таким образом *все данные хранятся в Lucene*, а *Elasticsearch распределённо управляет* этими *данными*.\n\n*Количество шардов* указано в *настройках индекса* в *свойстве* `number_of_shards`.\n\n*Резервная копия всех шардов* называется **репликой** (replica). Если *один экземпляр приложения падает* вместе с данными, которые на нём хранились, *реплика* позволяет *не терять* эти *данные*.\n\n**Репликация** (Sharding) — процесс, при котором данные постоянно **реплицируются** (копируются) на один или несколько других серверов. Репликация тоже является стратегией масштабирования баз данных.\n\nКоличество *реплик* указано в *настройках индекса* в *свойстве* `number_of_replicas`.\n\n## Перевёрнутые индексы\n\n*Elasticsearch хранит данные* как **перевёрнутые индексы** (inverted indexes) *на диске*, что позволяет очень быстро искать данные.\n\nПоисковой движок Apache Lucene реализует перевёрнутый индекс, используя структуру данных [**список с пропусками**](https://github.com/Max-Starling/Notes/blob/master/Algorithms-Structures.md#список-с-пропусками) (Skip List). Эта структура данных основана на связных списках, но по трудоёмкости сравнима с **двоичным деревом поиска** (B-tree).\n\nРазработчики *Lucene* выбрали *список с пропусками вместо двоичного дерева*, поскольку он *требует меньшее число обращений* к *диску*. \n\nИндекс в Lucene очень похож на индекс в конце книги: указываются определённый список ключевых слов (важных определений) и рядом страницы, на которых они упоминаются.\n![Index example](https://i.imgur.com/yIglkAe.jpg)\n\nАналогично работает и Google. Рядом с проиндексированными словами сохраняются ссылки на страницы. Таким образом их можно найти при поиске. Это же касается и Elasticsearch, только вместо ссылок на страницы используются ссылки на документы.\n\n### Пример с разбиением на слова\n\nПосмотрим, как обратные индексы работают на примере. \n\nПусть у нас есть два текста.\n* `I don't like to work alone`.  \n* `I often work on weekends`.\n\nРазобьём их на слова и выберем только уникальные слова из обоих текстов.\n\nМножество уникальных слов будет следующим: `I`, `don't`, `like`, `to`, `work`, `alone`, `often`, `on`, `weekends`.\n\nСохраним каждое уникальное слово в память, а рядом с ним — ссылки на те документы, которые используют это слово.\n\n| Слово     | Документ #1 | Документ #2 |\n| --------- | ----------- | ----------- |\n| I         |     +       |      +      |\n| don't     |     +       |             |\n| like      |     +       |             |\n| to        |     +       |             |\n| work      |     +       |      +      |\n| alone     |     +       |             |\n| often     |             |      +      |\n| on        |             |      +      |\n| weekends  |             |      +      |\n\nТеперь произведём поиск по словам. Документ либо содержит слово, либо не содержит.\n\nНапример, поиск по слову `work` выдаст оба документа, по слову `like` — только первый, по слову `often` — только второй.\n\nЕсли ввести искать по двум словам одновремено `often` и `alone`, то выдадутся оба документа.\n\n| Слово     | Документ #1 | Документ #2 |\n| --------- | ----------- | ----------- |\n| alone     |     +       |             |\n| often     |             |      +      |\n\n\n# Маппинги и типы данных\n\n**Маппинг** (mapping) — процесс, определяющий, *как документ* и *поля в нём хранятся* и *индексируются*.\n\n*Маппинги задаются* при *создании индекса* в *поле* `mappings`. Они *содержат свойства документов* и *типы* их *значений*.\n\nПоскольку маппинги могут быть разными для разных типов `type` индекса, они привязываются не к самому индексу, а к самим типам (например, к типу по умолчанию `_doc`). Поэтому при создании маппинга нужно всегда указывать тип.\n\nЕсли `mappings` не задаётся при создании индекса, то Elasticsearch создаёт его автоматически в режиме реального времени на основании данных индексируемых документов.\n\n## Создание индекса\n\nДля создания индекса используется PUT-запрос с его названием.\n```http\nPUT <ELASTICSEARCH_URL>/index_name\n```\n```js\n/* response body */\n{\n  \"acknowledged\": true,\n  \"shards_acknowledged\": true,\n  \"index\": \"index_name\"\n}\n```\n\n## Простые типы данных\n* **Строковые** (string): `text`, `keyword`.\n* **Числовые** (Numberic): `long`, `integer`, `short`, `byte`, `double`, `float`, `half_float`, `scaled_float`.\n* **Логический** (Boolean): `boolean`.\n* **Дата** (Date): `date`.\n* **Бинарный** (Binary): `binary`.\n* **Диапазон** (Range): `integer_range`, `float_range`, `long_range`, `double_range`, `date_range`.\n\n### Тип данных text\n\nСтроковый тип `text` используется для индексирования **полнотекстовых значений** (full-text values). Примерами полнотекстовых значений являются поля: название, сообщение, описание.\n\n*Полнотекстовые значения* **анализируются**, то есть обрабатываются перед *индексированием*. \n\nКаждое *полнотекстовое поле проходит перед индексированием* через **анализатор** (analyzer), который *конвертирует строку* в *список отдельных термов* (list of individual terms), затем этот *список индексируется*, а *поле* называют **проанализированным** (analyzed). \n\n**Анализирование** (analysis) позволяет *Elasticsearch* *искать отдельные слова* в *каждом полнотекстовом поле*.\n\n*Поля текстового типа* *не используются* для *сортировки*, обычно *не используются* для *агрегаций*.\n\nЗадание типа `text`.\n```http\nPUT <ELASTICSEARCH_URL>/index_name\nContent-Type: application/json\n\n{\n  \"mappings\": {\n    \"_doc\": {\n      \"properties\": {\n        \"description\": {\n          \"type\":  \"text\"\n        }\n      }\n    }\n  }\n}\n```\n\n### Тип данных keyword\n\n*Строковый тип* `keyword` (ключевое слово) используется для *индексирования* таких *значений*, как: `ID`, `email`, `hostname`, `status code`, `tag` и прочих. Эти *значения используются* для *фильтрации*, *сортировки* и *агрегации*. \n\nПоля типа `keyword` *не анализируются*. Они *ищутся только по точному значению, совпадению* (exact value). Например, нельзя найти `tom@gmail.com` по слову `tom` или `gmail`.\n\nЗадание типа `keyword`.\n```http\nPUT <ELASTICSEARCH_URL>/index_name\nContent-Type: application/json\n\n{\n  \"mappings\": {\n    \"_doc\": {\n      \"properties\": {\n        \"email\": {\n          \"type\":  \"keyword\"\n        }\n      }\n    }\n  }\n}\n```\n\n## Составные типы данных\n* **Объект** (Object): `object`. Для JSON-объекта.\n* **Вложенный** (Nested): `nested`. Для массива JSON-объектов.\n\n### Объект\n\n**Объект** (Object) предназначен для хранения JSON-объектов.\n\nJSON-объекты могут содержать в себе другие JSON-объекты, то есть они имеют иерархичную структуру.\n\nНапример, для индексируемого объекта ниже\n```js\n{\n  \"name\": \"Manfredi\",\n  \"age\": 27,\n  \"settings\": {\n    \"theme\": \"dark\"\n  }\n}\n```\nможет быть задан следующий маппинг.\n```http\n{\n  \"mappings\": {\n    \"_doc\": {\n      \"properties\": {\n        \"email\": {\n          \"name\":  { \"type\": \"text\"  },\n          \"age\": { \"type\": \"integer\"  },\n          \"settings\": {\n            \"properties\": {\n              \"theme\": { \"type\": \"keyword\"  }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\nElasticsearch распознаёт, что поле `settings` является объектом, благодаря свойству `properties`. Вложенные свойства `properties` отображают иерархию JSON-объектов.\n\n### Массив\n\n**Массив** (Array) в Elasticsearch не существует как отдельная сущность, поскольку любое поле может иметь одно или несколько значений по умолчанию. Тем не менее, все эти значения должны быть одного типа.\n\n* Массив строк: `[\"1\", \"two\"]`.\n* Массив чисел: `[1, 7]`.\n* Массив объектов `[{ \"name\": \"John\", \"experience\": 5 }, { \"name\": \"Sam\", \"experience\": 3 }]`.\n\nВ массиве объектов нельзя выделить конкретный объект. При такой необходимости нужно использовать тип `nested`.\n\nМассивы смешанных типов не поддерживаются: `[1, \"two\"]`.\n\nПустой массив интерпретируется как отсутствующее значение (поле без значений).\n\nВставка объекта с массивом `tags`.\n```http\nPUT <ELASTICSEARCH_URL>/index_name/_doc/1\nContent-Type: application/json\n\n{\n  \"message\": \"The problem with useEffect\",\n  \"tags\":  [ \"js\", \"react\", \"react-hooks\" ]\n}\n```\n\n### Особенность массива объектов\n\nВ массиве объектов Elasticsearch не рассматривает объекты как независимые сущности, поэтому Elasticsearch просто разбивает их на список полей и значений.\n\nНапример, вставка следующего массива объектов\n```http\nPUT <ELASTICSEARCH_URL>/index_name/_doc/1\nContent-Type: application/json\n\n{\n  \"user\" : [ \n    {\n      \"firstName\" : \"Max\",\n      \"lastName\" :  \"Starling\"\n    },\n    {\n      \"firstName\" : \"Richard\",\n      \"lastName\" :  \"Stone\"\n    }\n  ]\n}\n```\nбудет преобразована в два поля с несколькими значениями.\n```js\n{\n  \"user.firstName\" : [ \"Max\", \"Richard\" ],\n  \"user.lastName\" :  [ \"Starling\", \"Stone\" ]\n}\n```\nСвязь между полями `firstName` и `lastName` теряется и они уже больше не являются одним объектом.\n\nПоэтому при поиске следующий запрос не выдаст совпадений.\n```js\n[\n  { \"match\": { \"user.firstName\": \"Max\" }},\n  { \"match\": { \"user.lastName\": \"Starling\" }}\n]\n```\n\n### Сравнение массива объектов и вложенного типа\n\n**Вложенный тип данных** (Nested datatype) — специальный подтип объекта (`object`), который позволяет индексировать массив JSON-объектов таким образом, чтобы объекты можно было получать отдельно друг от друга.\n\n\n## Специализированные типы данных\n* **IP**: `ip`. Для IPv4 и IPv6 адресов.\n* **Completion** (Completion datatype): `completion`. Для автозаполнения предложений.\n* **Join**: `join`. Для создания отношений между документами одного индекса.\n* **Search-as-you-type**: `search_as_you_type`. Для поиска по мере ввода.\n\n## Мультиполя\n\nElasticserach предоставляет возможность *хранить одно и то же полt несколькими способами* для *разных целей*. Такое *поле* называется **мультиполем** (multi-field).\n\nНапример, *текстовое поле* может быть *одновременно* представлено *типом* `text` для *полтотекстового поиска* и типом `keyword` *для сортировки* и *агрегаций*.\n\nДля определения мультиролей используется свойство `fields`, значением которого выступает объект. Ключом этого объекта является название мультиполя, а в значении описывается тип.\n\nНапример, создадим текстовое поле `position`, которое может быть использовано как ключевое слово.\n```http\nPUT <ELASTICSEARCH_URL>/index_name\nContent-Type: application/json\n\n{\n  \"mappings\": {\n    \"properties\": {\n      \"position\": {\n        \"type\": \"text\",\n        \"fields\": {\n          \"keyword\": { \n            \"type\":  \"keyword\"\n          }\n        }\n      }\n    }\n  }\n}\n```\nТеперь, при необходимости использования `position` как ключевого слова, необходимо писать `position.keyword`, иначе оно будет работать как текстовое значение.\n\n### Получение текущих маппингов\n\nТекущие *маппинги индекса* можно *получить* по *GET-запросу* `_mappings`.\n```http\nGET <ELASTICSEARCH_URL>/index_name/_mappings\n```\n\n# Работа с данными\n\n## Создание индекса\nСоздание индекса с названием `index_name`.\n```http\nPUT <ELASTICSEARCH_URL>/index_name\n```\n\n## Создание типа\n```http\nPUT <ELASTICSEARCH_URL>/index_name/type_name\n```\n\nВ последних версиях Elasticsearch рекомендуется *не создавать тип*, а использовать *тип по умолчанию* `_doc`.\n\n## Удаление индекса\n```http\nDELETE <ELASTICSEARCH_URL>/index_name\n```\n\n## Создание документа\nСоздание документа в типе `type_name` индекса `index_name`.\n```http\nPOST <ELASTICSEARCH_URL>/index_name/type_name\nContent-Type: application/json\n\n{\n  \"name\": \"Alen Stone\",\n  \"job\": \"Full-stack Enginer\"\n}\n```\n\n## Обновление документа по ID\nОбновление документа типа `_doc` в индексе `users` по id.\n```http\nPUT <ELASTICSEARCH_URL>/users/_doc/H3tVi3ABpFL-9AlTbAgj\nContent-Type: application/json\n\n{\n  \"name\": \"Richard Stone\",\n  \"job\": \"Full-stack Enginer\"\n}\n```\n\n## Удаление документа по ID\n\nУдаление документа типа `_doc` по id.\n```http\nDELETE <ELASTICSEARCH_URL>/users/_doc/H3tVi3ABpFL-9AlTbAgj\n```\n\n## Группировка нескольких запросов в один (bulk)\n\nElasticasearch предоставляет возможность группировки нескольких изменяющих данные запросов в один при помощи специального POST-запроса `/_bulk`.\n\nПри помощи этого запроса можно манипулировать данными в нескольких индексах и их типах одновременно.\n\nЗапрос и его тело выглядят примерно следующим образом.\n```http\nPOST <ELASTICSEARCH_URL>/_bulk\n\n{ \"index\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }\n{ \"field1\" : \"value1\" }\n{ \"create\" : { \"_index\" : \"test\", \"_id\" : \"3\" } }\n{ \"field1\" : \"value3\" }\n{ \"delete\" : { \"_index\" : \"test\", \"_id\" : \"3\" } }\n{ \"update\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }\n{ \"doc\" : { \"field2\" : \"value2\" } }\n\n```\nВозможные операции\n* `create` - создание документа. Выдаёт ошибку, если документ с указанным `_id` уже существует.\n* `index` - создание документа (аналогично `create`, но без ошибки, а с замещением существующего).\n* `update` - обновление части документа, указанной в переданном свойстве `doc`.\n* `delete` - удаление документа.\n\nУказание `_index` является обязательным, указание `_id` - нет (это поле может быть сгенерировано автоматически).\n\nМожно также указать `type`, но поскольку он не указан, все документы индексируются в `_doc`.\n\nВ конце тела запроса обязателен переход на новую строку.\n\n### Пример вставки двух пользователей\n```http\nPOST <ELASTICSEARCH_URL>/_bulk\nContent-Type: application/json\n\n{ \"index\": { \"_index\": \"users\", \"_id\" : \"1\" } }\n{ \"name\": \"Harry Smith\", \"job\": \"Dev Ops\" }\n{ \"index\": { \"_index\": \"users\", \"_id\" : \"2\" } }\n{ \"name\":\" Sam Brave\", \"job\": \"QA\" }\n   \n```\n\n# Поиск документов\n\nДля получения документов индекса используется запрос `/_search`.\n\n## Вид запроса, параметры запроса и ответ\n\n### Все документы\n\nПолучить информацию о всех документах можно отправив GET-запрос, ничего не указывая.\n```http\nGET <ELASTICSEARCH_URL>/users/_search\n```\nОтвет выглядит следующим образом. \n```js\n/* response body */\n{\n  \"took\": 80,\n  \"timed_out\": false,\n  \"_shards\": {\n    \"total\": 1,\n    \"successful\": 1,\n    \"skipped\": 0,\n    \"failed\": 0\n  },\n  \"hits\": {\n    \"total\": {\n      \"value\": 2,\n      \"relation\": \"eq\"\n    },\n    \"max_score\": 1.0,\n    \"hits\": [\n        {\n          \"_index\": \"users\",\n          \"_type\": \"user\",\n          \"_id\": \"Jntsi3ABpFL-9AlTdggH\",\n          \"_score\": 1.0,\n          \"_source\": {\n            \"name\": \"Harry Smith\",\n            \"job\": \"Dev Ops\"\n          }\n        },\n        {\n          \"_index\": \"users\",\n          \"_type\": \"user\",\n          \"_id\": \"J3tsi3ABpFL-9AlTdggH\",\n          \"_score\": 1.0,\n          \"_source\": {\n              \"name\": \"Sam Brave\",\n              \"job\": \"QA\"\n          }\n        }\n    ]\n  }\n}\n```\nНаиболее полезная информация лежит в свойстве `hits`: `hits.total.value` - количество всех докуметров, удовлетворяющих поиску, `hits.hits` - массив самих документов (хранятся в `_source`) и дополнительной информации о них. По умолчанию возвращается только 10 документов (см. [Пагинация](#пагинация)). \n\n### По конкретному слову (все поля документа)\n\nПоиск по конкретному слову (слову `ops`) во всех полях (и `name`, и `job`).\n```http\nGET <ELASTICSEARCH_URL>/users/_search?q=ops\n```\n\n## Query DSL\n\nElasticsearch предоставляет **Query DSL** (Domain Specific Language) — предметно-ориентированный язык, позволяющий описывать запрос `query` в формате JSON и отправлять его в теле запроса (request body). Тело можно отравлять даже с GET-запросами.\n\nСоздатели Elasticsearch предлагают рассматривать Query DSL как **абстрактное синтаксическое дерево** (AST, Abstract Syntax Tree) **запросов**, которое имеет два типа **предложений** (clauses):\n* **Листовые, конечные** (leaf query clauses). Предназначены для поиска определённого значения в определённом поле. Пример: `match`, `term`, `range`.\n* **Составные** (compound query clauses). Предназначены для логического объединения листовых и других составных предложений. Пример: `bool`.\n\n## Основные запросы Elasticsearch\n\n### Запрос с match_all\n\nЯвляется самым простым запросом, поскольку возвращает все документы и не принимает какие-либо параметры.\n```json\n{\n  \"match_all\": {}\n}\n```\nСам по себе избыточен, но может использоваться в комбинации с более сложными запросами.\n\n### Запрос с match\n\nВ запрос с `match` передаётся текстовое, числовое, логическое значение или дата. Результатом поиска становятся документы, которые соответствуют переданному значению.\n\n```json\n{\n  \"match\": {\n    \"fieldName\": \"search text\"\n  }\n}\n```\n\nЕсли значением является текст, то он анализируется перед поиском. Поэтому запрос с `match` является стандартным для полнотекстового поиска.\n\nПример поиска пользователя с именем `Sam`. \n```http\n```http\nGET <ELASTICSEARCH_URL>/users/_search\nContent-Type: application/json\n\n{\n  \"query\": {\n    \"match\": {\n      \"name\": \"Sam\"\n    }\n  }\n}\n```\n\nКраткая версия запроса.\n```http\nGET <ELASTICSEARCH_URL>/users/_search?q=name:Sam\n```\n\n### Запрос с term\n\nВ запрос с `term` так же передаётся текстовое, числовое, логическое значение или дата.\n\nВ отличии от запроса с `match`, результатом запроса с `term` являются документы, которые содержат **точный терм** (exact term) в указанном поле.\n\n```json\n{\n  \"term\": {\n    \"fieldName\": \"a\"\n  }\n}\n```\n\nТекстовое значение, переданное в запрос с `term` *не анализируется*. Но данные документов уже проанализированы перед индексированием, поэтому поиск может выдать неверные результаты и лучше не использовать запрос с `term` для полей типа `text`, а использовать с типом `keyword`.\n\n### Запрос с terms\n\nЗапрос с terms аналогичен запросу с term, но позволяет передать несколько допустимых значений вместо одного.\n```json\n{\n  \"terms\": {\n    \"fieldName\": [\"a\", \"b\"]\n  }\n}\n```\n\nВажно отметить, что при работе с массивом значений в документе свойства `term` и `terms` ищут не точное совпадение, а включение (contains). Оба примера выше с `term` и с `terms` включат в выборку документ со значением `[\"a\", \"b\", \"c\"]`.\n\n### Запрос с range\n\nЗапрос с `range` позволяет задать промежуток значений для конкретного поля. Результатом станут документы, удовлетворяющее промежутку.\n\nПредоставляемые свойства для задания промежутка:\n* `gt` - больше (greater than).\n* `lt` - меньше (less than).\n* `gte` - больше или равно (greater than or equal to).\n* `lte` - меньше или равно (less than or equal to).\n\nПример запроса для поиска пользователей от 18 до 25 лет.\n```js\nGET <ELASTICSEARCH_URL>/users/_search\nContent-Type: application/json\n\n{\n  \"query\": {\n    \"range\": {\n      \"age\": {\n        \"gte\": 18,\n        \"lte\": 25\n      }\n    }\n  }\n}\n```\n\n### Запрос с exists и missing\n\nЗапрос с `exists` (с `missing`) позволяет находить документы, у которых значение конкретного поля присутствует (отсутствует).\n```json\n{\n  \"exists\": {\n    \"field\": \"username\"\n  },\n  \"missing\": {\n    \"field\": \"bio\"\n  }\n}\n```\n\n## Поиск по нескольким полям\n\nДля поиска по нескольким полям используется запрос с `multi_match`.\n\n```http\nGET <ELASTICSEARCH_URL>/users/_search\nContent-Type: application/json\n\n{\n \"query\": {\n   \"multi_match\": {\n     \"query\": \"sam\",\n     \"fields\": [\"firstName\", \"lastName\"]\n   }\n}\n```\n\nМожно задавать приоритеты полей при помощи символа `^`. В примере ниже `firstName` в два раза важнее `lastName`.\n```json\n\"fields\": [\"firstName^2\", \"lastName\"]\n```\n\nЕсли свойство `fields` не указано, то по умолчанию Elasticsearch берёт из маппинга все поля индекса, которые удовлетворяют типу искомого значения, и ищет по этим полям.\n\n## Пагинация\n\nЗа пагинацию отвечают параметры `size` и `from` запроса `_search`.\n\nПараметр `size` определяет *количество возвращаемых документов*. Значение *по умолчанию*: *10*.\n\nСвойство `from` отвечает за *смещение документов* (количество документов, которые должны быть пропущены).\n\n### Пример пагинации\n\nСоздадим индекс `films` с объектами, имеющими поля `name` (keyword), `date`, `rating`.\n```HTTP\nPUT <ELASTICSEARCH_URL>/films\nContent-Type: application/json\n\n{\n  \"mappings\": {\n    \"properties\": {\n      \"name\": { \"type\": \"keyword\" },\n      \"date\": { \"type\": \"date\" },\n      \"rating\": { \"type\": \"float\" }\n    }\n  }\n}\n```\nПроиндексируем 3 фильма.\n```HTTP\nPUT <ELASTICSEARCH_URL>/films/_doc/_bulk\nContent-Type: application/json\n\n{ \"index\":{} }\n{ \"name\": \"film 1\", \"date\": \"2020-05-01T12:10:30Z\", \"rating\": 4.5 }\n{ \"index\":{} }\n{ \"name\": \"film 2\", \"date\": \"2020-06-30T16:00:45Z\", \"rating\": 4.5 }\n{ \"index\":{} }\n{ \"name\": \"film 3\", \"date\": \"2020-04-07T23:15:50Z\", \"rating\": 4.7 }\n\n```\n\nСделаем поисковый запрос к индексу и добавим параметр `size`.\n```http\nGET <ELASTICSEARCH_URL>/films/_doc/_search\nContent-Type: application/json\n\n{\n  \"size\": 2\n}\n```\nРезультат\n```json\n[{\n  \"...\": \"...\",\n  \"name\": \"film 1\"\n},\n{\n  \"...\": \"...\",\n  \"name\": \"film 2\"\n}]\n```\n\nДобавим также параметр `from`.\n```http\nGET <ELASTICSEARCH_URL>/films/_doc/_search\nContent-Type: application/json\n\n{\n  \"size\": 2,\n  \"from\": 1\n}\n```\nРезультат\n```json\n[{\n  \"...\": \"...\",\n  \"name\": \"film 2\"\n},\n{\n  \"...\": \"...\",\n  \"name\": \"film 3\"\n}]\n```\n\n## Релевантность, контекст запроса и контекст фильтра\n\nПо умолчанию Elasticsearch ищет по **релевантности**, которая показывает, насколько запрос документ удовлетворяет поисковому запросу. \n\nРелевантность определяется **оценкой релевантности** (relevance score). Эта оценка зависит от самого поискового запроса, а также от контекста.\n\nВ **контексте запроса** (query context) предложения (query clauses) отвечают на вопрос \"Насколько хорошо документ удовлетворяет запросу?\". Помимо выяснения, соответствует ли документ запросу или нет, вычисляется *оценка релевантности* и записывается в *мета-свойство* `_score` в ответе.\n\nКонтекст запроса относится к параметру `query`.\n\nВ **контексте фильтра** (filter context) предложения отвечает на запрос \"Удовлетворяет ли документ запросу?\". Ответом является либо да, либо нет, и документ либо включается в выборку, либо нет в соответствии с ответом. \n\nПримером фильтра являются свойства `filter` и `must_not` для запроса с `bool`, о которых будет рассказано далее.\n\n## Фильтрация\n\n**Фильтр** (в программировании) — функция или программа, которая принимает структуру данных (обычно список) и возвращает новую структуру данных, содержащую только те элементы из исходной структуры, которые удовлетворяют заданному условию. \n\n<!-- На языке математики удовлетворение условия здесь означает, что логическая функция (boolean function), определяющая заданные условия, возвращает истиное значение (true). -->\n\nElasticsearch предоставляет составное предложение `bool`, которое позволяет задавать условия запроса и комбинировать их.\n\nПредложение `bool` принимает объект, свойства которого обозначают логические операции.\n* `must` — аналог логического И (AND, объединение условий).\n* `should` — аналог логического ИЛИ (OR, пересечение условий).\n* `must_not` — аналог логического НЕ (NOT, исключение). Выполняется в контексте фильтра, поэтому не высчитывает оценку релевантности. \n\nТакже `bool` предоставляет свойство `filter`. Оно работает аналогично `must`, но в контексте фильтра, поэтому вычисление оценки релевантности игнорируется.\n\nКаждое из указанных выше свойств может принимать объект, содержащий условие, или массив таких объектов.\n\nУсловия задаются при помощи свойств `match`, `term`, `terms`, `range`.\n\n### Пример поиска специалиста с фильтрами\n\nВ следующем примере производится поиск специалиста, который:\n* Имеет позицию `Software Enginer` И знает технологии `React`, `Vue` (must).\n* Имеет опыт работы более одного года ИЛИ его желаемый уровень заработной платы не привышает 1000$ (should).\n* Его возраст НЕ меньше 25 лет. (must_not).\n```js\n{\n  \"query\" : {\n    \"bool\" : {\n      \"must\": [{\n        \"term\": {\n          \"position\": \"Software Enginer\"\n        }\n      }, {\n        \"terms\": {\n          \"technology\": [\"React\", \"Vue\"]\n        }\n      }],\n      \"should\": [{\n        \"range\": {\n          \"experience\": {\n            \"gte\": \"1 year\"\n          }\n        }\n      }, {\n        \"range\": {\n          \"desiredSalary\": {\n            \"lte\": \"1000 USD\"\n          }\n        }\n      }],\n      \"must_not\": {\n        \"range\": {\n          \"age\": {\n            \"lte\": 25\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Важное замечание про should\n\nНесмотря на то, что `should` работает как логическое ИЛИ, позволяя задавать несколько условий, не все из которых должны выполняться одновременно, по умолчанию при наличии `must` ни одно из условий `should` не должно обязательно выполняться.\n\nВ этом случае `should` просто увеличивает значимость тех документов (увеличивая значение `_score`, которое по умолчанию = 1), которые удовлетворяют заданным условиям, но не исключает из выборки те документы, которые не условиям удовлетворяют.\n\nЧтобы это исправить, нужно использовать свойство `minimum_should_match`. Оно позволяет задать минимальное количество условий `should`, которые должны выполниться, чтобы документ попал в выборку. \n\nПо умолчанию свойство `minimum_should_match` = 1, если отсутствуют `must` или `filter`, 0 — иначе.\n\nВ примере ниже искомый специалист обязательно должен или иметь опыт больше года, или иметь желаемую зарплату меньше 1000$.\n```js\n{\n  \"query\": {\n    \"bool\": {\n      \"must\": [],\n      \"should\": [{\n        \"range\": {\n          \"experience\": {\n            \"gte\": \"1 year\"\n          }\n        }\n      }, {\n        \"range\": {\n          \"desiredSalary\": {\n            \"lte\": \"1000 USD\"\n          }\n        }\n     }],\n     \"minimum_should_match\": 1\n    }\n  }\n}\n```\n\nЕсли количество условий в `should` совпадает с `minimum_should_match`, то `should` вернёт те же документы, что и `must` при тех же условиях.\n\nЕсли количество условий в `should` меньше, чем `minimum_should_match`, то вернётся пустая выборка.\n\n## Сортировка\n\nElasticsearch позволяет при поиске *сортировать* документы *по одному* или *нескольким полям*. \n\nЗа **сортировку** (sort) отвечает параметр `sort`. \n\nСортировка может производиться **по возрастанию** (`asc`, ascending) и **по убыванию** (`desc`, descending).\n```js\n{\n  \"sort\" : [\n    { \"likes\" : { \"order\" : \"desc\" } },\n    { \"date\" : { \"order\" : \"asc\" } }\n  ]\n}\n```\n\nПри сортировке по нескольким полям более приоритетным является то поле, которое указано первым в массиве.\n\n### Сортировка полей-массивов\n\nElasticsearch также поддерживает сортировку полей, значениями которых являются массивы. В этом случае доступны следующие режимы (`mode`)\n* `min` — сортировка по минимальным значениям массивов.\n* `max` — сортировка по максимальным значениям массивов.\n* `avg` — сортировка по средним значениям массивов.\n* `sum` — сортировка по сумме значений массива.\n\n```js\n{\n  \"sort\" : [\n    { \"values\" : { \"order\" : \"desc\", \"mode\": \"avg\" } },\n  ]\n}\n```\n\n### Пример сортировки фильмов по рейтингу и дате\n\nВоспользуемся примером из раздела с пагинацией.\n\nСортировка индекса `films` по убыванию рейтинга и даты.\n```HTTP\nGET <ELASTICSEARCH_URL>/films/_doc/_search\nContent-Type: application/json\n\n{\n  \"sort\" : [\n    { \"rating\" : { \"order\" : \"desc\" } },\n    { \"date\" : { \"order\" : \"desc\" } }\n  ]\n}\n```\n\nРезультат\n```json\n[{\n  \"...\": \"...\",\n  \"name\": \"film 3\"\n},\n{\n  \"...\": \"...\",\n  \"name\": \"film 2\"\n},\n{\n  \"...\": \"...\",\n  \"name\": \"film 1\"\n}]\n```\n\n`film 3` является самым старым, но имеет выше рейтинг, а рейтинг приоритетнее даты, поскольку указан раньше в параметре `sort`.\n`film 2` имеет такой же рейтинг, как и `film 1`, но по дате он новее.\n\n## Обработка больших объёмов данных (scroll)\n\nОдин поисковой запрос в Elasticsearch не может обработать более 10000 элементов. \n\nСвойство `size` не может превышать 10000 элементов. Параметр `from`, отвечающий за пагинацию, не может захватить элементы с индексом, большим 10000. Свойство `total` также не может возвращать более 10000 элементов.\n\nМожно повысить лимит, увеличив значение параметра `index.max_result_window value` в настройках индекса `settings`. Но делать это не желательно без крайней необходимости, поскольку поисковые запросы занимают память кучи (heap memory) и время пропорционально формуле `max(max_result_window, from + size)`. Если убрать лимит, то лимит памяти будет отсутствовать и кластер может упасть от перенагрузки. Лучше получать данные меньшими порциями. \n\nКогда необходимо обработать более 10000 документов одного индекса, следует использовать `Scroll API`.\n\n**Scroll** позволяет возвращать большое количество результатов (или все результаты) аналогично курсору (cursor) в традиционных базах данных.\n\n*Scroll* не используется для пользовательских запросов в режиме реального времени, но используется для получения больших объёмов данных, чтобы как-то обработать их (например, проиндексировать все документы индекса заново с обновлённой конфигурацией индекса или скопировать их для записи куда-либо).\n\n**Контекст поиска** (Search context) — состояние, которые поддерживается в течение всей операции поиска в шарде. Чем больше параллельных (concurrent) поисковых операций выполняется, тем больше объектов поискового контекста существуют одновременно. Когда поисковая операция завершается, контекст поиска удаляется.\n\n### Как работать со Scroll API\n\nК обычному поисковому запросу следует добавить query-параметр `scroll` и в качестве значения передать туда время, которое будет жить поисковой контекст до следующего вызова.\n\nДля оптимальной работы поисковой контекст должен жить как можно меньше, но этого времени должно хватать, чтобы обработать результат предыдущего результат запроса. Поставим 1 минуту в качестве времени жизни поискового контекста.\n```http\nGET <ELASTICSEARCH_URL>/index_name/_search?scroll=1m\n```\n\nОсновные единицы времени\n* `h` — час.\n* `m` — минута.\n* `s` — секунда.\n* `ms` — миллисекунда.\n\nВ ответ на такой GET-запрос приходит ответ, который помимо привычных свойств содержит\n* Достоверный `hits.total` (количество всех документов в индексе, не ограниченное лимитом в 10000).\n* `_scroll_id` — идентификатор контекста поиска, который используется для получения следующей части результатов.\n\nКаждый запрос со `scroll` возвращает в свойстве `_scroll_id` ссылку на текущий контекст поиска, по которой можно сослаться на следующую порцию результатов. Эта ссылка может меняться, а может оставаться прежней (то есть при двух идентичных последовательных запросах можно получить разные данные) — важно использовать её последнюю версию.\n\nДля запроса со `scroll` контекст поиска создаётся при первоначальом (initial) запросе и живёт для выполения последующих запросов.\n\nДля получения следующей порции результатов используется запрос следующего вида (в запросе отсутствует название индекса). Каждый такой запрос устанавливает своё время жизни следующего запроса. Время начинает считаться с момента, когда предыдущий запрос вернул данные.\n```http\nGET <ELASTICSEARCH_URL>/_search/scroll \n\n{\n    \"scroll\" : \"1m\", \n    \"scroll_id\" : \"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==\" \n}\n```\n\nКогда время жизни контекста поиска истекает, получить дальнейшие документы при помощи `scroll` не получится. Выдаётся ошибка о том, что контекст поиска не существует.\n```json\n {\n   \"type\": \"search_context_missing_exception\",\n   \"reason\": \"No search context found for id [xxxx]\"\n}\n```\n\nВажно отметить, что использование `from` запрещено при использовании `scroll`.\n\nПосле завершения работы со `scroll` можно удалить его вручную, не дожидаясь, пока истечёт время его жизни. Это освободит занимаемую память.\n```http\nDELETE <ELASTICSEARCH_URL>/_search/scroll\n\n{\n    \"scroll_id\" : \"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==\"\n}\n```\n\n### Псевдокод для работы со scroll\nПолучать данные при помощи `scroll` можно рекурсивно примерно следующим образом.\n\n```js\nconst getScrollDataRec = (documents, scroll_id) => {\n  const { scroll_id, hits } = POST(`${ELASTICSEARCH_URL}/_search/scroll`, {\n    scroll_id,\n    scroll: '1m',\n  });\n\n  if (hits.hits.length) {\n    documents.push(hits.hits);\n    return getScrollDataRec(documents, scroll_id);\n  }\n  return documents;\n};\n  \nconst getAllDocuments = (indexName) => {\n  const { scroll_id, hits } = GET(`${ELASTICSEARCH_URL}/${indexName}/_search?scroll=1m`);\n  const documents = getScrollDataRec(hits.hits, scroll_id);\n  return documents;\n};\n```\n\n# Анализаторы\n\n**Анализаторы** (Analyzers) определяют способ, которым данные будут анализироваться перед индексацией.\n\n**Анализ текста** (Text analysis) — процесс преобразования обычного текста в структурированный формат, оптимизированный для поиска. Используется, когда установлен тип данных `text`.\n\nПосле анализа текст поля разделяется на **термы** (terms). Таким образом, после анализа поле представлено в виде **списка термов** (list of terms), в котором оно и индексируется.\n\n## Встроенные анализаторы\n\nElasticsearch предоставляет набор **встроенных анализаторов** (build-in analyzers).\n\nДля их будем анализировать фразу `\"- How old are you? - I'm 17.\"`.\n\nЧтобы проверить, как работает конкретный анализатор, можно отправить следующий запрос.\n```http\nGET <ELASTICSEARCH_URL>/_analyze\nContent-Type: application/json\n\n{ \n  \"analyzer\": \"analyzer_name\",\n  \"text\":\"- How old are you? - I'm 17.\"\n}\n```\n\n* **Стандартный**: `standard`. Используется по умолчанию. Разбивает текст на слова, переводит их в нижний регистр (`lowercase`), удаляет знаке препинания, при необходимости удаляет стоп-слова.\n```js\n/* terms */\n[\"how\", \"old\", \"are\", \"you\", \"i'm\", \"17\"]\n```\n* **Простой**: `simple`. Разделяет слова каждый раз, когда встречает не букву. Все термы переводятся в нижний регистр.\n```js\n/* terms */\n[\"how\", \"old\", \"are\", \"you\", \"i\", \"m\"]\n```\n* **Стоп-анализатор**: `stop`. Как `simple`, но с возможностью удалять стоп-слова. По умолчанию используются стоп-слова английского языка (вспомогательные глаголы, предлоги и так далее).\n```js\n/* terms */\n[\"how\", \"old\", \"you\", \"i\", \"m\"]\n```\n* **Пробельный**: `whitespace`. Разделяет текст, когда находит пробельные символы.\n```js\n/* terms */\n[\"-\", \"How\", \"old\", \"are\", \"you?\", \"-\", \"I'm\", \"17.\"]\n```\n* **Анализатор ключевых слов**: `keyword`. Принимает текст и его возвращает как есть.\n```js\n/* terms */\n[\"- How old are you? - I'm 17.\"]\n```\n* **Языковой**: `english`, `french`. Анализирует текст соответственно специфике языка. Удаляет стоп-слова, характерные языку. Переводит в нижний регистр.\n```js\n/* terms */\n[\"how\", \"old\", \"you\", \"i'm\", \"17\"]\n```\n* **Шаблонный**: `pattern`. Для разделения текста на термы использует регулярные выражения. По умолчанию используется регулярное выражение `\\W+` (всё, что не может быть словом). Переводит в нижний регистр.\n```js\n/* terms */\n[\"how\", \"old\", \"are\", \"you\", \"i\", \"m\", \"17\"]\n```\n\n## Пользовательские анализаторы\n\nЧтобы расширить функциональность встроенного анализатора (например, заменить стоп-слова или заменить регулярное выражение), необходимо создать **пользовательский анализатор** (custom analyzer) в настройках индекса (`settings`), что обычно делается при создании индекса.\n\nПользовательские анализаторы существуют в пределах индекса.\n\nНапример, создадим пользовательский анализатор, который игнорирует слово `old`. Для этого добавим его в поле `stopwords`.\n```http\nPUT <ELASTICSEARCH_URL>/index_name\nContent-Type: application/json\n\n{\n  \"settings\": {\n    \"analysis\": {\n      \"analyzer\": {\n        \"custom_stop\": {\n          \"type\": \"stop\",\n          \"stopwords\": [\"old\"]\n        }\n      }\n    }\n  }\n}\n```\nПроверим, как анализируется текст `\"- How old are you? - I'm 17.\"`.\n```http\nGET <ELASTICSEARCH_URL>/index_name/_analyze\nContent-Type: application/json\n\n{ \n  \"analyzer\": \"analyzer_name\",\n  \"text\":\"- How old are you? - I'm 17.\"\n}\n```\n```js\n/* terms */\n[\"how\", \"are\", \"you\", \"i\", \"m\"]\n```\n\nДля более гибкой настройки пользовательских анализаторов необходимо ознакомиться с блоками, из которых состоит каждый анализатор.\n\n## Составляющие анализатора\n\n*Анализатор* является *пакетом*, который *состоит* из *нескольких строительных блоков*: *фильтры символов*, *токенизатор*, *фильтры токенов*.\n\nПри *преобразовании текста* эти *блоки вызываются* в *указанном выше порядке*.\n\n### Фильтр символов\n\n**Фильтр символов** (Character filter) принимает оригинальный текст в качестве потока символов и трансформирует этот поток, добавляя, удаляя и изменяя символы.\n\nНапример, римские цифры (`I`, `II`, `III`) могут переводиться в арабские (1, 2, 3).\n\nУ анализатора может быть несколько фильтров символов или не быть вообще. Они применяются в указанном порядке.\n\n### Токенизатор\n\n**Токенизатор** (Tokenizer) *принимает поток символов* (stream of characters), *разбивает его* на *отдельные токены* (individual tokens) и *возвращает поток токенов*. Чаще всего токенами являются отдельные слова. \n\n*Процесс разбиения потока символов* на *токены* называется **токенизацией** (tokenization).\n\nИменно *благодаря токенизации доступен полтотекстовый поиск*, ведь *каждый токен индексируется отдельно*.\n\nРанее было показано, как *токенизаторы встроенных анализаторов* разделяют текст на токены (термы).\n\n*Токенизатор* также *отвечает за порядок термов* (порядок может меняться).\n\n*Один анализатор имеет ровно один токенизатор*.\n\n### Фильтр токенов\n\n**Фильтр токенов** (Token filter) принимает поток токенов (stream of tokens) и транмформирует его, удаляя, добавляя и изменяя токены.\n\nНапример, фильтр токенов `lowercase` переводит все токены в нижний регистр, фильтр `stop` удаляет стоп-слова, фильтр `synonym` добавляет синонимы в поток токенов.\n\nУ анализатора может быть несколько фильтров токенов или не быть вообще. Они применяются в указанном порядке.\n\n<!-- Можно заменить, что английские стоп-слова перестали игнорироваться, поскольку произошла их перезапись массивом `[\"old\"]`. Чтобы это исправить, можно использовать.\n_english_ -->\n\n## Граф токенов\n\nКогда токенизатор превращает поток символов в поток токенов, он запоминает **позицию** (`position`) каждого токена в потоке и число позиций (`positionLength`), которые охватывает токен.\n\nЭто позволяет построить **ориентированный** (имеющий направление движения) **ациклический** (без циклов) **граф**, который называется **графом токенов** (token graph).\n\nКаждая позиция представляет **вершину графа** (node).\n\nКаждый токен представляет **дугу графа** (edge), указывающую на следующую позицию.\n\n```js\n/* граф токенов для текста \"Hello our users!\" после токенизации */\n\n   hello      our       users    \n0 ------> 1 -------> 2 -------> 3\n```\n\nФильтры токенов могут добавлять новые токены (например, синонимы) в поток токенов, а значит и в граф токенов.\n\nСинонимы записываются на ту же позицию, что и существующие токены.\n```js\n/* граф токенов для текста \"Hello our users!\" после токенизации\nи фильтрации фильтром токенов с синонимами */\n\n   hello      our       users    \n0 ------> 1 -------> 2 -------> 3\n    hi                 clients\n```\n\n### Многопозиционные токены\n\nПо умолчанию токен занимает только одну позицию, то есть его `positionLength` равняется 1.\n\nНо некоторые синонимы могут занимать несколько позиций. Например, расшифровки аббревиатур (CSS, Cascading Style Sheets).\n```js\n  cascading      style       sheets         is          ...\n0 ----------> 1 --------> 2 ---------> 3 ---------> 4 --------> 5\n|                                      |\n----------------------------------------\n                  css\n```\n\nФильтры, которые могут добавлять многопозиционные токен, называются **фильтрами токенов графа** ( graph token filters). Такими являются фильтры `synonym_graph` и `word_delimiter_graph`.\n\n## Нормализация\n\nВ то время, как благодаря токенизации доступен полтотекстовый поиск, каждый отдельный токен при поиске сравнивается посимвольно.\n\n* При поиске `How`, токен `how` не пройдёт проверку на совпадение.  \n* При поиске `user`, токен `users` не пройдёт проверку на совпадение. \n* При поиске `hello`, токен `hi` не пройдёт проверку на совпадение.\n\nЧтобы этого избежать, можно **нормализовать** (normalize) данные, то есть привести их к стандартному формату. Таким образом токены не будут точно совпадать (not exact match), но будут достаточно похожи, чтобы попасть в результат поиска.\n\nК примеру, токен `Hello` может быть переведено в нижний регистру (`be lowercased`), `users` может быть приведён к его корневому слову `user` (stemmed), `hello` и `hi` являются синонимами и могут индексироваться как единственное слово `hello`.\n\n## Анализатор индекса и анализатор поиска\n\n*Анализ текста* осуществляется *дважды*\n* при *индексации документа* (Index time)\n* во *время поиска* (Search time, query time).\n\n**Анализатор индекса** (Index analyzer) анализирует текстовые данные перед индексацией.\n\n**Анализатор поиска** (Search analyzer) анализирует текс поискового запроса (`query`).\n\nВ большитсве случаев эти анализаторы должны иметь одинаковый набор правил токенизации и нормализации.\n\nНапример, при текст `\"Hello our USERS!\"` может быть преобразован анализатором индекса в `[hello, our, user]`, а текст поискового запроса `\"Hi user\"` — анализатором поиска в `[hello, user]`.\n\n| Слово     | Поиск       | Индекс      |\n| --------- | ----------- | ----------- |\n| hello     |     +       |      +      |\n| our       |             |      +      |\n| user      |     +       |      +      |\n\nТогда документ со значением `\"Hello our USERS!\"` в текстовом поле попадёт в результат поиска по запросу `\"Hi user\"`.\n\n### Разные анализаторы поиска и инекса \n\nИногда может появиться необходимость использовать разные анализаторы индекса и поиска.\n\nНапример, когда мы хотим, чтобы убрать из поиска некоторые нежелательные результаты.\n\nВ этом случае можно указать отдельный анализатор для поиска.\n```http\nGET <ELASTICSEARCH_URL>/index_name/_search\nContent-Type: application/json\n\n{\n  \"query\": {\n    \"match\": {\n      \"message\": {\n        \"query\": \"Hi user\",\n        \"analyzer\": \"stop\"\n      }\n    }\n  }\n}\n```\n\nМожно также задать разные анализаторы для конкретного поля при создании индекса в `mappings`.\n```http\nPUT <ELASTICSEARCH_URL>/index_name\nContent-Type: application/json\n\n{\n  \"mappings\": {\n    \"properties\": {\n      \"title\": {\n        \"type\": \"text\",\n        \"analyzer\": \"whitespace\",\n        \"search_analyzer\": \"simple\"\n      }\n    }\n  }\n}\n```\n\n# Токенизаторы\n\nВ главе **[Составляющие анализатора](#составляющие-анализатора)** было рассказано, что такое *токенизатор*.\n\nПомимо перечисленных ранее функций, *токенизаторы* также *задают* **тип токенов** (token type). \n\n*Простые токенизаторы* *разбиват текст* на *слова* и задают *тип* `word`. *Другие токенизаторы* могут задавать *типы* `<ALPHANUM>`, `<HANGUL>`, `<NUM>`.\n\n*Виды токенизаторов*\n* *Ориентированные на слова*.\n* *Токенизаторы частичных слов*.\n* *Токенизаторы структурированного текста*.\n\n## Ориентированные на слова\n\n**Ориентированные на слова токенизаторы** (Word oriented tokenizer) разбивают текст на отдельные токены, которые явяются словами. \n\nЗадаваемый тип токенов: `word`.\n\n## Токенизаторы частичных слов\n\n**Токенизаторы частичных слов** (Partial word tokenizer) разбивают текст или слова на маленькие фрагменты для проверки на частичное совпадение слов.\n\n### N-gram\n\n### Edge n-gram\n\n## Токенизаторы структурированного текста\n\n**Токенизаторы структурированного текста** (Structured text tokenizer) обычно используются не для полнотекстового поиска, а для идентификаторов (`id`, `email`, `phone` и так далее).\n\n\n# Настройки индекса (settings)\n\nУ каждого индекса при создании задаётся набор настроек.\n\n## Получение текущих настроек индекса\nТекущие настройки индекса можно получить по GET-запросу `_settings`.\n```http\nGET <ELASTICSEARCH_URL>/users/_settings\n```\n\n```js\n/* response body */\n{\n  \"users\": {\n    \"settings\": {\n      \"index\": {\n        \"number_of_shards\": \"1\",\n        \"provided_name\": \"users\",\n        \"creation_date\": \"1582885295047\",\n        \"number_of_replicas\": \"1\",\n        \"uuid\": \"cNAP5avkRueTAkUGNxsHew\",\n        \"version\": {\n          \"created\": \"7030299\"\n        }\n      }\n    }\n  }\n}\n```\n\n*Количество шардов* указано в *настройках индекса* в *свойстве* `number_of_shards`.\n\nКоличество *реплик* указано в *настройках индекса* в *свойстве* `number_of_replicas`.\n\n\n# Установка Elasticsearch на ПК\n\n* [Скачать архив](https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html#install-elasticsearch).\n* Разархивировать и запустить `bin\\elasticsearch` или `bin\\elasticsearch.bat` (в зависимости от операционной системы).\n* Поскольку общение с Elasticserach идёт при помощи HTTP-запросов, можно использовать Postman (или что-то похожее) и делать запросы на `http://localhost:9200`.\n"
  },
  {
    "path": "Encoding.md",
    "content": "- [Cистемы счисления](#системы-счисления)\n  - [Представление чисел в различных с/с](#представление-чисел-в-различных-с-с) \n- [Кодировки символов](#кодировки-символов)\n  - [ASCII](#ascii)\n- [Кодирование и декодирование](#кодирование-и-декодирование)\n  - [UTF](#utf)\n- [Медиа тип (`MIME type`)](#медиа-тип-mime-type)\n\n# Системы счисления\n\n**Система счисления** (англ. `numeral system`, `system of numeration`) — представление чисел при помощи символов.\n\n**Двоичная, бинарная** (англ. `binary`): 0, 1.  \n**Десятичная** (англ. `decimal`): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.  \n**Шестнадцатеричная** (англ. `hexadecimal`): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.\n\nЧтобы не путать с 10 с/с, к числу в 16 с/с обычно добавляют `0x` в начало или `h` (`₁₆`) в конец.  \nЧтобы не путать с 10 с/с, к числу в 2 с/с обычно добавляют `b` (`₂`) в конец.  \n10 с/с считается стандартной, поэтому к числу обычно ничего не добавляют (но иногда можно явно указать `₁₀`).\n\nДвоичные цифры 0 и 1 называют **битами** (англ. `bit`), они взаимоисключаемы: 0 - ложь, 1 - истина.\n\n**Байт** (англ. `byte`), **октет** (англ. `octet`) — *последовательность из 8 бит*, её можно компактно представить в виде шестнадцатеричного числа.\n```css\n00000000₂ = 0x0\n10110011₂ = 0xB3\n11111111₂ = 0xFF\n```\n\nАналогично, **ниббл** (англ. `nibble`), **полубайт** (англ. `half-byte`) или **тетрада** (англ. `tetrade`) — 4 бита, **слово** (англ. `word`) — 16 бит, **двойное слово** (англ. `double word`, `dword`) — 32 бита.\n\n*Отсчёт битов* в *двоичном числе* начинается *с нуля* и идёт *справа налево*.  \n**Младший бит** (англ. `least significant`, `low-order`) — крайний справа, **старший бит** (англ. `most significant`, `hight-order`) — крайний слева.\n\n**Порядок байтов от старшего к младшему** (англ. `big-endian`, `BE`) соответствует порядку записи арабских цифр: 123 (сто двадцать три). \nЭтот порядок также называют **сетевым порядком байтов**, поскольку он является стандартным для протоколов TCP/IP. \nИспользуется также в технической и учебной литературе, в PNG и JPEG.\n\n**Порядок байтов от младшего к старшему** (англ. `little-endian`, `LE`) соответствует обратному порядку записи арабских цифр: 321 (сто двадцать три). \nЭтот порядок также называют **интеловским порядком байтов**, так как он используется в памяти ПК с процессорами архитектуры x86.\n\n## Представление чисел в различных с/с\n\n**Числовой разряд** — место, позиция цифры в числе.\n \nРазряды начинают считать справа налево (`BE`), начиная с нулевого.  \n\nПредставление чисел в `N` c/c:\nчисло представляется в виде многочлена степени n - 1 (n - количество цифр),  \nс переменной `N` и цифрами в качестве коэффициентов.\n```css\n/* в 10 с/с */\n/* 4 - нулевой разряд, 3 - первый, 2 - второй, 1 - третий */\n1234₁₀ = 1 * 10³ + 2 * 10² + 3 * 10¹ + 4 * 10⁰\n\n/* в 16 с/с */\n/* C₁₆ - нулевой разряд, B₁₆ - первый, A₁₆ - второй */\nABC₁₆ = A₁₆ * (10₁₆)² + B₁₆ * (10₁₆)¹ + C₁₆ * (10₁₆)⁰\n\n/* в 2 с/с */\n/* 0₂ - нулевой разряд, 1₂ - первый, 1₂ - второй */\n110₂ = 1₂ * (10₂)² + 1₂ * (10₂)¹ + 0₂ * (10₂)⁰\n```\n\nПеревод чисел в 10 с/с из другой c/c.\n```css\n/* из 16 c/c */\n/* A₁₆ = 10, B₁₆ = 11, C₁₆ = 12 */\nABC₁₆ = 10 * 16² + 11 * 16¹ + 12 * 16⁰ = 2748\n\n/* из 2 с/c */\n/* 1₂ = 1, 0₂ = 0 */\n110₂ = 1 * 2² + 1 * 2¹ + 0 * 2⁰ = 6\n```\nПеревод чисел из 10 с/с в другую `N` с/c:  \nпоследовательно делим число в 10 с/с на `N` до тех пор, пока не останется остаток `<= (N - 1)`,\nответ состоит из остатков деления в обратном порядке.\n```css\n/* в 2 с/с */\n22₁₀\n22 / 2 = 11 (ост. 0)\n11 / 2 = 5 (ост. 1)\n5 / 2 = 2 (ост. 1)\n2 / 2 = 1 (ост. 0)\n1 <= 1 (ост. 1)\n10110₂\n\n/* в 16 с/с */\n7327₁₀\n7327 / 16 = 457 (ост. 15)\n457 / 16 = 28 (ост. 9)\n28 / 16 = 1 (ост. 12)\n1 <= 15 (ост. 1)\n/* 12₁₀ → C₁₆, 15₁₀ → F₁₆ */\n1C9F₁₆\n```\n\nДругие переводы (например, 2 c/c -> 16 c/c) можно проводить в два действия: 2 с/c -> 10 c/c -> 16 c/c.\n# Кодировки символов\n\n**Символ** (англ . `character`) — минимальная единица текста, имеющая семантическое значение.\n\nПример символов: `G` — латинская буква G, `(` — открывающая круглая скобка, `<` — знак меньше.\n\n**Множество символов** (англ. `character set`) — набор, совокупность символов, которые могут быть использованы в разных языках.  \n\nНапример, множество символов `{ A, B, C, D }` используются в английском, немецком и других языках.\n\n**Кодировка символов** (англ. `character encoding`, `charset`) — представление символов какой-то системой кодирования.\n\n**Закодированное множество символов** (англ. `coded character set`) — *множество символов*, каждый символ которого соответствует какому-то уникальному номеру.  \n```css\n/* закодированное множество символов { A, B, C, D } (ключ — значение) */\n\n/* ASCII */\n65 — A\n66 — B\n67 — C\n68 — D\n\n/* Unicode */\nU+41 — A\nU+42 — B\nU+43 — C\nU+44 — D\n```\nТо есть существует другое множество — *множество уникальных номеров*, определяемое *кодировкой*, с которым у *множества символов* установлено *взаимно однозначное соответствие*.\n\n**Взаимно однозначное соответствие, биекция** (англ. `bijection`) — такое *соответствие между элементами двух множеств*, при котором *каждому элементу первого* множества *соответствует один определенный элемент второго* множества, а *каждому элементу второго* множества — *один определенный элемент первого* множества.\n![image](https://github.com/Max-Starling/Notes/assets/22237384/85d50dda-e9e9-4c27-ba90-ce8097cf0d56)\n\n\n**Кодовая точка, позиция кода** (англ. `code point`, `code position`) — любой элемент из *множества уникальных номеров* заданной *кодировки*.  \n\nВ примере выше *кодовыми точками* в кодировках *ASCII* и *Unicode* являются `66, 66, 67, 68` и `U+41, U+42, U+43, U+44` соответственно.\n<!-- Кодовые точки представлены числами в шестнадцатеричной системе счисления (hexadecimal). -->\n\nКоличество кодовых точек в различных кодировках:\n* ASCII - 128 кодовых точек (0 - 7F).  \n* Extended ASCII - 256 (0 - FF).\n* Unicode - 1114112 (0 - 10FFFF), поскольку состоит из 17 плоскостей (каждая из них содержит 2¹⁶ кодовых точек).\n\nК *кодовым точкам Unicode* принято добавлять `U+` в начало, чтобы можно было отличить кодировку от других.\n```css\n/* кодовые точки латинской буквы \"M\" */\nUTF-8:  U+4D\nUTF-16: U+004D\nUTF-32: U+0000004D\n\n/* кодовые точки фигурной скобки \"{\" */\nUTF-8:  U+65115\nUTF-16: U+65115\nUTF-32: U+00065115\n```\n\n**Кодовая единица** (англ. `code unit`) — последовательность бит, используемая для кодирования символа из множества символов заданной кодировки.\n\n*Кодовая единица* состоит из *заданного кодировкой количества бит*:\n* ASCII - 7 бит\n* UTF-8 - 8 бит\n* UTF-16 - 16 бит\n* UTF-32 - 32 бит\n\nКаждая *кодовая точка* представляется в виде *последовательности кодовых единиц*.  \n\nЧем *больше бит вмещается* в *кодовой единице*, тем *меньше кодовых единиц требуется* для *представления кодовой точки*.  \n* UTF-8: 1-4 кодовых единиц\n* UTF-16: 1-2 кодовые единицы (две требуеются для символов, идущих после U+10000)\n* UTF-32: 1 кодовая единица\n\n```css\n/* кодовые единицы латинской буквы \"M\" */\nUTF-8:  4D\nUTF-16: 004D\nUTF-32: 0000004D\n\n/* кодовые единицы фигурной скобки \"{\" */\nUTF-8:  EF B9 9B\nUTF-16: FE5B\nUTF-32: 0000FE5B\n```\n\n## ASCII\n\n**ASCII** (American Standard Code for Information Interchange) — американский стандартный код для обмена информацией.\n\nБольшинство кодировок символов берут ASCII за основу (например, Unicode), дополняя её другими символами.\n\nASCII создавалась для кодирования символов, коды которых вмещались в 7 бит (2⁷ = 128 символов), 8-ой бит использовался для ошибок, возникших при передаче данных.  \n\nНа большинстве компьютеров минимально адресуемая единица памяти — байт (8 бит), поэтому там используются 8-битные вместо 7 (добавляется дополнительный старший бит, например, 0).  \n\nЗатем появилась **расширенная ASCII кодировка** (англ. `extended ASCII`), использующая 8 бит (2⁸ = 256 символов).\n\nИзначально ASCII была разработана для телекомуникационного оборудования (печатные машинки для передачи сообщений), поэтому помимо символов, выводимых на печать, содержит управляющие символы, которые не печатаются.\n* `EOT` (англ. `end of transmission`) — конец файла\n* `LF` (line feed) — перевод строки (характерен для Unix)\n* `CR` (carriage return) — возврат каретки (курсор перемещается в начало текущей строки, не переходя на новую)\n* `CR LF` - перевод строки (характерен для Windows)\n* `FF` (form feed) — прогон страницы (продолжить печать со следующего листа)\n* `BS` (backspace) — возврат на один символ\n* `DEL` (delete) — стереть последний символ\n* `TAB` (tab) — горизонтальная табуляция\n* `CAN` (cancel) — отмена\n* и другие.\n\nС помощью символа BS (возврат на один символ) можно печатать один символ поверх другого (например, добавить диакритический знак).\n* `a BS '` → á\n* `c BS ,` → ç\n* `o BS /` → ø\n* `t BS t` → **t**  (дважды тот же символ — выделение жирным)\n\nСуществуют национальные варианты ASCII, где некоторые символы заменяются на свойственные языку.\n\n# Кодирование и декодирование\n\n## UTF\n\n**UTF** (Unicode Transformation Format) — формат преобразования Юникода.\n\n**Маркер последовательности байтов**, **метка порядка байтов** (Byte Order Mark, BOM) — специальный Unicode-символ `U+FEFF`, вставляемый в начало текстового файла или потока, чтобы указать кодировку и порядок байтов, с помощью которых символы Unicode были закодированы. Маркер не обязателен, но очень желателен.\n```css\nU+FEFF /* кодовое слово маркера */\n\n/* кодовые единицы */\nEF BB BF     /* UTF-8 */\nFE FF        /* UTF-16 (BE) */\nFF FE        /* UTF-16 (LE) */\n00 00 FE FF  /* UTF-32 (BE) */\nFF FE 00 00  /* UTF-32 (LE) */\n```\n\n### Алгоритм кодирования UTF-8 \n1) Определить кодовую точку (номер) рассматриваемого символа в Unicode.\n```css\n$       /* символ */\nU+0024  /* кодовая точка */\n```  \n2) Определить по кодовой точке количество октетов (байтов, последовательностей из 8 бит; 1 - 4), требуемых для кодирования символам.\n```css\n0 - 7F          /* 1 октет */\n80 - 7FF        /* 2 октета */\n800 - FFFF      /* 3 октета */\n10000 - 10FFFF  /* 4 октета */\n```\n3) Установить старшие биты первого октета, опираясь на вычисленное количество необходимых октетов (`0` - 1 октет, `110` - 2 октета, `1110` - три октета, `11110` - четыре октета), во 2-4 октетах установить старшие биты равными 10. При декодировании файла это позволяет определить, где начинается новая точка и сколько следующих октетов она занимает.\n```css\n0xxxxxxx                             /* 1 октет */\n110xxxxx 10xxxxxx                    /* 2 октета */\n1110xxxx 10xxxxxx 10xxxxxx           /* 3 октета */\n11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  /* 4 октета */\n```\n4) `x` - значащие биты. Вместо них нужно подставить биты кодовой точки в двоичном представлении. Если их не хватает, чтобы заполнить все значащие биты, заполняем старшие значащие биты нулями. В результате получим последовательность кодовых единиц.\n```css\n€ → 0xE2 0x82 0xAC\n```\n\nПолные примеры кодирования символов:\n```css\n$                      /* символ */\nU+0024                 /* кодовая точка */\n24₁₆ < 7F₁₆            /* 1 октет */\n0xxxxxxx               /* маска */\n24₁₆ = 36₁₀ = 100100₂  /* двоичное представление кодовой точки */\n00100100               /* кодовые единицы в 2 с/с */\n0x24                   /* кодовые единицы в 16 с/с */\n\n€                                  /* символ */\nU+20AC                             /* кодовая точка */\n800 < 20AC < FFFF                  /* 3 октета */\n1110xxxx 10xxxxxx 10xxxxxx         /* маска */\n20AC₁₆ = 8364₁₀ = 10000010101100₂  /* двоичное представление кодовой точки */\n11100010 10000010 10101100         /* кодовые единицы в 2 с/с */ \n0xE2 0x82 0xAC                     /* кодовые единицы в 16 с/с */\n```\n\n### Алгоритм декодирования UTF-8\n\nАналогично, но наоборот\n\nИмеем поток байт.\n\n# Медиа тип (`MIME type`)\n\n**Медиа тип**, **MIME тип** (англ. `Media type`, `MIME type`, `Multipurpose Internet Mail Extensions`) это стандарт, описывающий природу и формат файла или набора байтов.\n\nМедиа типы *определёны* и *стандартизированы* в спецификации [RFC 6838](https://datatracker.ietf.org/doc/html/rfc6838).\n\nОтветственной организацией за все *официальные медиа типы*, то есть *официально стандартизированные*, является **IANA** (`The Internet Assigned Numbers Authority`). Страница про медиа типы на их сайте: https://www.iana.org/assignments/media-types/media-types.xhtml.\n\n## Структура MIME типа\nMIME тип состоит из *типа* (англ. `type`) и **подтипа** (англ. `subtype`). Они разделяются наклонной чертой `/`.\n```\ntype/subtype\n```\n\n**Типом** (англ. `type`) называют *общую категория данных* (например, `text`, `aplication`, `video`, `audio`, `image`).\n\n**Подтипом** (англ. `subtype`) называют *подкатегорию*. (например, простой текст `text/plain`, HTML `text/html`).\n\n## Параметры MIME типа\n\nК MIME типу опционально может быть добавлен параметр после `;`.\n```\ntype/subtype;param=value\n```\n\nНапример, для текста в качестве *параметра* может быть указана *кодировка*. Для задания кодировки (англ. `charset`) UTF-8 используется `text/plain;charset=UTF-8`. Если параметр `charset` не задан, его значение по умолчанию US-ASCII (`text/plain;charset=ASCII`). Другие кодировки можно найти [здесь](https://help.hcltechsw.com/dom_designer/9.0.1/appdev/LSAZ_APPENDIX_E_MIME_CHARSET_NAMES.html).\n\n## Регистр MIME типа\n*Регистр не имеет значения* (англ. `case insensitive`) для всего, *кроме значения параметра* `value`. Как правило, используют *нижний регистр*, *значение параметра* может быть в *любом регистре*.\n\n##\n\n\n"
  },
  {
    "path": "Features.md",
    "content": "# Типы веб-сайтов\n\nОнлайн-магазин, новостной портал, социальная сеть, информационная страница (информационный портал) человека или организации, ресурсы для обучения, для просмотра картинок, видео, фильмов, для прослушивания музыки, страницы для управления бизнесом - админки, конструкторы сайтов.\n\n# Виды технологий\nБиблиотеки - \n\nФреймворки - \n\nСервисы -\n\nПриложения -\n\nСайты - \n\n# Фичи\n\n## Регистрация, авторизация, аутентицикафия\nКлючевые слова: `auth`, `authorization`, `authentication`, `registration`, `login`, `signup`, `signin`, `forget password`\n\nАутентификация чаще всего проводится через сторонние приложения или: например, через Gmail или другую почту, Facebook, иногда через VK, Github, через номер телефона.\n\n## Работа с формами\nКлючевые слова: `Forms`, `working with forms`, `inputs`.\n\n## Работа с таблицами\nКлючевые слова: `tables`, `sheets`,\n\nКлючевые технологии: `CSV` (`comma separated values`), `Excel`, `Google Docs`, таблицы из таких библиотек компонентов, как `Bootstrap`, `Antd`, `MUI` и так далее.\n\n## Работа с файлами\nКлючевые слова: `working with files`, `file upload`, `file download`.\n\nКлючевые расширения файлов: `PDF`, `TXT`, `CSV`, расширения `Excel` и `Word`. \n\n## Пагинация\nКлючевые слова: `pagination`, `infinite scroll`, `loading by parts` (by `chanks`)\n\nОграничения количества отображаемых на странице элементов.\n\n## Фильтрация, фильтр\nКлючевые слова: `filter`, `filtration`\n\nОграничение выборки по какому-то заданному критерию - фильтру.\n\nНапример, фильтруем все модели телефонов по марке `Xiaomi` или `Apple`, сотрудников компании по специальности.\n\n## Поиск\nКлючевые слова: `search`, `match`, `query`, `full text search`\n\n## Сортировка\nКлючевые слова: `Sorting`, `sort`, `sort order`\n\n## Уведомления\nКлючевые слова: `Notifications`, `email notifications`, `push notifications`, `sms notifications`, `broadcast`, `broadcasting`\n\n\n## Подписка на обновления, новостная рассылка\nКлючевые слова `Subscriptions`, ``subscription`, `email notifications`, `newsletter`\n\nЧаще всего присутствует на новостных порталах, на сайтах онлайн-магазинов (условный wildberries), фаст-фуда (Dodo, dominos, McDonnals) \n\n## Управление пользователям\nКлючевые слова: `User Roles`, `User Permissions`, `User Management`, `User Access`, `Users`\n\nОбычно такая фича требуется в приложениях, в которых необходима некоторая иерархия\n\nНапример, есть онлайн магазин (для покупателей). И есть отдельная онлайн страница для управления этим магазином - админка. \n\n## Работа с картой\nКлючевые слова: `Map`, `Map integration`, `Map API`, `Geolocation`, `Location`.\n\nПриложения: `Yandex map`, `Google Maps`. Пять менее известных альтернатив:\n```\nIf you need navigation, choose TomTom\nIf you need accuracy, choose Mapfit\nIf you need affordability, choose OpenLayers\nIf you need map visualizations, choose * HERE\nIf you need custom maps, choose Mapbox\n```\n\n## Статистика, аналитика\n\n## Платёжные системы\nPayments, Payment Systems\n\n## Чат\nКлючевые слова: `Messaging`, `Chat`, `Communication`, `Messager`\n\n## Новостная лента\nКлючевые слова: `Feed`, `news`, `news feed`, `news line`\n\nЧаще всего встречается в соцсетях. У некоторых соц. сетей является главной фичей (например, у Twitter, Instagram)\n\n## Анимации\n\n## Голосовой помошник\n\n\n\n"
  },
  {
    "path": "Flux-Redux-Vuex-Mobx.md",
    "content": "\n* [Flux](#flux)\n  * [Особенности Flux](#особенности-flux)\n  * [Структура Flux](#структура-flux)\n* [Redux](#redux)\n  * [Три основных принципа Redux](#три-основных-принципа-redux)\n  * [Другие особенности Redux](#другие-особенности-redux)\n  * [Структура Redux](#структура-redux)\n* [Vuex](#vuex)\n  * [Особенности Vuex](#особенности-vuex)\n  * [Структура Vuex](#структура-vuex)\n* [MobX](#mobx)\n  * [Основной принцип Mobx](#основной-принцип-mobx)\n  * [Особенности Vuex](#особенности-mobx)\n  * [Структура MobX](#структура-mobx)\n* [Дополнительно](#дополнительно)\n  * [Реализация Flux от Facebook и её использование](#реализация-flux-от-facebook-и-её-использование)\n  * [Redux Thunk vs Redux Saga](#redux-thunk-vs-redux-saga)\n  * [Использование React с Redux](#использование-react-с-redux)\n\n# Flux\n\n**Flux** — *архитетура построения пользовательских интерфейсов*, основанная на *шаблоне \"Наблюдатель\"* (observer pattern, EventEmitter).  \nИзначально разработана компанией *Facebook* для *React* и *React Native* приложений.  \n \n## Особенности Flux\n* *Однонаправленный поток данных*:  \n**Action Creator -> Action -> Dispatcher -> Callbacks -> Stores -> Views**.  \nAction создаётся при взаимодействии пользователя со View, но может создаваться и самим приложением.\n* Может быть *несколько Stores*.\n* *Store* может быть как *изменяемым* (mutable), так и *неизменяемым* (immutable).\n* В приложении может быть только *один Dispatcher*, *региструющий все Callbacks*.\n\n## Структура Flux\n\n**Action** — *объект*, *описывающий происходящее* в приложении *действие*.  \n\n*Action обязательно* должен иметь **тип** (type), может иметь *дополнительную информацию* (если полей несколько, то для удобства можно их объединить в одно поле-объект, например `payload: { /* ... */ }`).\n```js\nconst CHANGE_FETCH_STATUS = 'CHANGE_FETCH_STATUS';\nconst FETCH_ITEMS = 'FETCH_ITEMS';\n```\nПример Action.\n```js\nconst FETCH_ITEMS_ACTION = ({\n  type: FETCH_ITEMS,\n  items: [1, 3],\n});\n```\n\n**Action Creator** — *функция-строитель*, которая создаёт *Action* в зависимости от переданных ей аргументов.\n```js\nconst changeFetchStatus = isFetching => ({\n  type: CHANGE_FETCH_STATUS,\n  isFetching,\n});\n\nconst fetchItems = () => FETCH_ITEMS_ACTION;\n```\n\n**Store** — место, где хранятся данные.  \nВ простейшем случае — объект, в более сложном — класс или модуль.\n```js\nconst store = {\n  items: [],\n  isFetching: false,\n};\n```\nFlux допускает наличие нескольких Stores.  \n\n**Callback** (в контексте Flux) — функция, которая принимает Action и в зависимости от него обновляет или не обновляет часть данных, лежащих в Stores.\n```js\nconst itemCallback = (action) => {\n  if (action.type === CHANGE_FETCH_STATUS) {\n    store.isFetching = action.isFetching;\n  } else if (action.type === FETCH_ITEMS) {\n    store.items = action.items;\n  }\n} \n```\nКаждый Callback должен быть зарегистрирован при помощи Dispatcher.\n\n**Dispatcher** — это модуль или класс, который позволяет регистировать (register) Callbacks и вызывать их всех с параметром Action каждый раз, когда вызывается функция dispatch. \n\n<!-- Под капотом лежит шаблон \"Наблюдатель\" (Observer pattern, EventEmmiter) и происходит подписка на события (subscription). -->\n\n```js\nconst dispatcher = new Dispatcher();\n\ndispatcher.register(itemCallback);\n\n// store: { items: [], isFetching: false }\ndispatcher.dispatch(changeFetchStatus(true)); \n// store: { items: [], isFetching: true }\ndispatcher.dispatch(fetchItems()); \n// store: { items: [1, 3], isFetching: true }\ndispatcher.dispatch(changeFetchStatus(false)); \n// store: { items: [1, 3], isFetching: false }\n\n// если Action не обрабатывается ни в одном Callback, то Store должен остаться без изменений\ndispatcher.dispatch({ type: 'INCORRECT_ACTION' }); \n// store: { items: [1, 3], isFetching: false }\n```\n\nВо *Flux* разрешено, чтобы *Action Creator* *вызывал dispatch* сразу *при создании Action* (так можно упростить код выше):\n```js\nconst fetchItems = () => dispatcher.dispatch({\n  type: FETCH_ITEMS,\n  items: [1, 3],\n});\n```\n\n**View** — UI-компонента (обычно React-компонента).\n```jsx\nconst Button = (<button onClick={fetchItems}>Fetch items</button>);\n```\n\n# Redux\n\n**Redux** — библиотека, основанная на подходе Flux и вносящая в него некоторые правки.\n\n## Три основных принципа Redux\n* Только *один источник правды* (single source of truth) — *Store*.  \n* *Состояние* доступно *только для чтения* (state is read-only). Его можно *изменить* лишь с помощью *Actions*.\n* *Изменение состояния* происходит только с использованием *чистых функций* (pure functions) — *Reducers*.\n\n## Другие особенности Redux\n* *Однонаправленный поток данных*:  \n**Action Creator -> Action -> Reducers -> Store -> Views**.  \n* *Store* только один, но он может разбиваться на части, за каждую из которых отвечает отдельный Reducer.\n* *Store* должен быть *неизменяемым* (immutable). Reducer не изменяет state напрямую, а работает с копией и возвращает её.\n* *Отсутствует Dispatcher*, вместо него используется функция `store.dispatch()`.\n* В схеме Redux изначально нет места асинхронным функциям, но есть возможность это исправить, подключив middleware (thunk, saga).\n\n## Структура Redux\n\nЧасти **Action**, **Action Creator**, **Store** и **View** определяются аналогично подходу Flux за исключением того, что в Redux может быть только один Store, а Action Creator не может вызывать функцию dispatch, при необходимости за него это делает **Bound Action Creator**:\n```js\nconst ADD_ITEM = 'ADD_ITEM';\n\n/* Action Creator */\nconst addItem = item => ({ type: ADD_ITEM, item });\ndispatch(addItem(5)); // dispatch action ADD_ITEM\n\n/* Bound Action Creator */\nconst boundAddItem = item => dispatch(addItem(item));\nboundAddItem(5) // dispatch action ADD_ITEM\n```\n\n**Reducer** — *чистая функция*, определяющая, как изменится состояние приложения (state) в ответ на *Action*.  \nReducer имеет вид: `(previousState, action) => newState`, что напоминает функцию `reduce()`, откуда и название.  \n```js\nconst reduce = (accumulator, currentValue) => accumulator + currentValue;\n```\n\n```js\nconst initialState = {\n  items: [],\n};\n\nconst itemReducer = (state = initialState, action) => {\n  switch (action) {\n    case ADD_ITEM:\n      return ({\n        ...state,\n        items: [\n          ...state.items,\n          action.item,\n        ],\n      });\n\n    default:\n      return state;\n  }\n};\n```\nПочему используется деструктуризация `...` и нельзя просто напрямую изменить значение в state?  \nПотому что объекты и массивы в JavaScript передаются по ссылке.  \nЕсли изменить их значение напрямую, то произойдёт мутация и Redux не заметит изменений, а значит оно не дойдёт до View в нужный момент.\n\nКогда Action не обрабатывается в текущем Reducer, то он попадает в блок default.  \nСостояние должно остаться без изменений, поэтому оно возвращается без деструктуризации.  \n\nВ Redux может быть несколько Reducers, каждый из которых отвечает за какую-то часть State.  \nReducers комбинируются в главный Reducer, который передаётся в Store при создании:\n```js\nimport { createStore, combineReducers } from 'redux';\n\n/* ... */\n\nconst reducer = combineReducers({\n  item: itemReducer,\n  another: anotherReducer,\n});\n\nconst store = createStore(reducer);\n/*\nconst store = {\n  item: { ... },\n  another: { ... },\n};\n*/\n```\n\nКак и в случае Callbacks из Flux, все Reducers получают приходящий Action.  \nТем не менее между Reducer и Callback есть существенное отличие:  \nCallback не принимает в параметрах и не возвращает state (как это делает Reducer), вместо этого он изменяет state напрямую.  \n```js\nconst itemState = {\n  items: [],\n};\n\nconst itemCallback = (action) => {\n  if (action.type === ADD_ITEM) {\n    itemState.items = [\n      ...items,\n      action.item,\n    ];\n  }\n};\n```\n\nВ Redux отсутствует Dispatcher, его работу берёт на Store, предоставляя функцию **dispatch**.\n```js\n// item's state: { items: [] }\nstore.dispatch(addItem(7));\n// item's state: { items: [7] }\nconsole.log(store.getState())\n```\n\n# Vuex\n\n**Vuex** — *шаблон управления состоянием* (state management pattern) приложения, а также *библиотека*, разработанная для Vue.js приложений. \n \n## Особенности Vuex\n* *Однонаправленный поток данных*:  \n**Action -> Mutations -> State -> Views**.  \nAction создаётся при взаимодействии пользователя со View, но может создаваться и самим приложением.\n* Может быть *только один State*, но в нём могут быть выделены отдельные *независимые блоки* — *Modules*.\n* *State* *изменяем* (mutable), *единственный способ* его *изменить* — *совершить* (commit) *Mutation*.\n* *Action* может использовать *асинхронные функции*, *Mutation* — не может.\n\n## Структура Vuex\n \n**State** — *объект*, в котором *хранится состояние приложения*.  \n*Располагается* в *Store* и *инициализируется* вместе с ним.  \n```js\nimport Vue from 'vue';\nimport Vuex from 'vuex';\n\nVue.use(Vuex);\nconst store = new Vuex.Store({\n  state: {\n    items: [],\n  },\n});\n```\nЧтобы *State* был *доступен во всех компонентах*, нужно *встроить Store* в приложение.\n```js\n  const app = new Vue({\n    /* ... */\n    store,\n  });\n```\nИспользование *State внутри компонент*: `this.$store.state`.\n```js\nconst Items = {\n  template: `<ul>\n  <li v-for=\"item in items\">\n    {{ item }}\n  </li>\n</ul>`,\n  computed: {\n    items () {\n      return this.$store.state.items,\n    }\n  }\n}\n```\n\n**Getter** — *вспомогательная функция*, *принимающая State* и *возвращающая* некоторую его *часть* или же *вычисляющая* на его основании *что-то новое*.\n*Параметры Getter*: `(state, getters)`, `state` — *ссылка* на `store.state`, `getters` — другие Getters, которые можно использовать.\n```js\nconst store = new Vuex.Store({\n  /* ... */,\n  getters: {\n    itemsLength: state => state.items.length,\n    numbers: state => state.items.filter(item => typeof item === 'number'),\n    getItemByIndex: state => index => state.items[index],\n  },\n});\n```\n*Использование Getters*.\n```js\nstore.getters.itemsLength;\nstore.getters.getItemByIndex(0);\n\n// в компоненте\nthis.$store.getters.numbers;\n```\n\n**Mutation** — *функция*, в которой происходит *изменение State*.  \n*Единственный способ изменить State* — *совершить* (commit) *Mutation*.  \n*Mutation* — *синхронная транзакция*, для *асинхронности* используется *Action*.  \n*Параметры Mutation*: `(state, payload)`, `state` — *ссылка* на `store.state`, `payload` — *объект с* переданными *параметрами*.\n```js\n// mutation-types.js\nexport const ADD_ITEM = 'ADD_ITEM';\n```\n```js\n// store.js\nimport { ADD_ITEM } from './mutation-types';\n\nconst store = new Vuex.Store({\n  /* ... */,\n  mutations: {\n    [ADD_ITEM] (state, payload) {\n      // мутируем State (не можем испозовать state.items.push(), потому\n      // что должна измениться ссылка на массив, произойти мутация)\n      state.items = [...state.items, item];\n    },\n  },\n});\n```\n*Совершение Mutation*: `commit(type, payload)`.\n```js\n// state: { items: [] }\nstore.commit(ADD_ITEM, { item: 7 });\n// или\nstore.commit({ type: ADD_ITEM, item: 7 });\n// state: { items: [7] }\n\n// в компоненте\nthis.$store.commit(ADD_ITEM, { item: 7 });\n```\n\n**Action** — *функция*, которые *совершает* (commit) в своём теле *Mutations* и может выполнять *асинхронные операции*.  \n*Параметры Action*: `(context, payload)`, `context` — объект, который содержит:\n* `state` — то же, что и `store.state`\n* `commit(type, payload)` — функция для *совершения Mutation*\n* `dispatch(type, payload)` — функция для *отправки Action*\n* `getters` — *Getters*\n\n```js\nimport api from '/* ... */'; // связующее звено между клиентом и сервером\n\nconst store = new Vuex.Store({\n  /* ... */,\n  actions: {\n    /* синхронный Action */\n    addItem (context, payload) {\n      context.commit('addItem', payload.item);\n    },\n    /* асинхронный Action */\n    async fetchItem (context) {\n      const item = await api.get(/*...*/); // получение item откуда-либо, допустим получили 7\n      \n      context.dispatch('addItem', { item });\n      // или\n      context.dispatch({ type: 'addItem', item });\n    },\n  },\n});\n```\n*Отправка* (dispatch) *Action*: `dispatch(type, payload)`.\n```js\n// state: { items: [] }\nstore.dispatch('addItem', 5);\n// state: { items: [5] }\nstore.dispatch('fetchItem');\n// state: { items: [5, 7] }\n\n// в компоненте\nthis.$store.dispatch('fetchItem');\n```\n\n**View** — UI-компонента (Vue.js) \n\n**Modules** позволяют создавать независимые блоки глобального State, в которых есть свои локальные State, Actions, Getters и Mutations. \n```js\nconst itemModule = {\n  state: { /* ... */ },\n  mutations: { /* ... */ },\n  actions: { /* ... */ },\n  getters: { /* ... */ },\n};\n\nconst userModule = {\n  state: { /* ... */ },\n  mutations: { /* ... */ },\n  actions: { /* ... */ },\n  getters: { /* ... */ },\n};\n\nconst store = new Vuex.Store({\n  modules: {\n    item: itemModule,\n    user: userModule,\n  }\n});\n\nstore.state.item // item's state\nstore.state.user // user's state\n```\nСтруктура локальных Mutations, Actions, Getters такая же, только теперь в качестве `state` берётся локальный State.  \nВ локальных Actions и Getters есть возможность обратиться к глобальным State и Getters при помощи `rootState` и `rootGetters`.  \n```js\nconst itemModule = {\n  state: {\n    items: [],\n  },\n  getters: {\n    itemsLength (state, getters, rootState, rootGetters) { /* ... */ },\n  },\n  mutations: {\n    ADD_ITEM (state, payload) {\n      state.items = [...state.items, item];\n    },\n  },\n  actions: {\n    addItem (context, payload) {\n      context.rootGetters;\n    },\n  },\n};\n```\n\nПо умолчанию Actions, Getters, Mutations вызываются так, словно они были определены в глобальном State.  \nЭто удобно, если все их названия уникальны, но может стать проблемой в большом приложении, где названия могут совпадать.  \n```js\nstore.commit('ADD_ITEM');\nstore.dispatch('addItem');\nstore.getters.itemsLength;\n```\nЧтобы это исправить, можно замкнуть функционал в Module, задав ему namespace.\n```js\nconst itemModule = {\n  namespaced: true,\n  /* ... */\n};\n\nconst store = Vuex.Store({\n  modules: {\n    item: itemModule,\n  },\n});\n```\nТогда в обращение добавится префикс с названием модуля, а предыдущее обращение перестанет работать.\n```js\nstore.commit('item/ADD_ITEM');\nstore.dispatch('item/addItem');\nstore.getters['item/itemsLength'];\n```\n<!-- Обращения вроде `store.item.commit()` не работают. -->\n\nModules можно подключать и удалять динамически (многие Vue.js плагины подключают свои Modules так к приложению).\n```js\nconst adminModule = { /* ... */ };\n\n/* подключение модуля */\nstore.registerModule('user', userModule);\n/* удаление модуля */\nstore.unregisterModule('user');\n```\nModule может содержать другие Modules.  \n```js\nconst moderatorModule = { /* ... */ };\n\nconst userModule = {\n  modules: {\n    moderator: moderatorModule,\n  },\n};\n```\n\nВ больших приложениях State может сильно разрастаться (становится неудобно и сложно его поддерживать), модули помогают решить эту проблему и сделать приложение более расширяемым.  \n\n# MobX\n\n**MobX** — библиотека, помогающая просто и гибко управлять состоянием приложения, основанная на подходе прозрачного реактивного функционального программирования (Transparent functional reactive programming, TFRP).  \n\nСам по себе *MobX не является архитектурой или хранилищем состояния*, но обладает функционалом, с помощью которого можно это всё построить, из-за чего его часто считают альтернативой Redux.  \n\n## Основной принцип MobX\n> Всё, что может быть извлечено (be derived) из состояния приложения, должно быть извлечено. Автоматически.  \n\nРечь идёт о *текущем состоянии приложения*: при его обновлении должно автоматически обновиться всё, что его использовало.\n\nСуть того, к чему *стремится реактивное программирование*, можно показать на примере (достигается при помощи Observable).\n```js\na = 5\nb = 7\nc = a + b // с = 12\n\nb = 10 // c = 15\na -=4 // c = 11\n```\n\n## Особенности MobX\n\n* *Однонаправленный поток данных*:  \n**Actions -> State -> Derivations (computed values) -> Reactions**.\n* *State* представлен любым Observable, изменения которого отслеживаются при помощи Reactions; из-за этого State децентрализован, раздроблен.\n* *State* *изменяем* (mutable).\n* Нужные *Derivations* пересчитываются и *Reactions* вызываются автоматически при изменении *State*.\n\n## Структура MobX\n\n### State\n\n**State** — *данные приложения* (объекты, массивы, примитивы), составляющие в совокупности Модель приложения (как в подходах по типу MVC).  \n \nЛюбое значение, которое в приложении имеет тип Observable и его изменения отслеживаются при помощи Reactions, является частью State.\n\n<!--Чтобы была возможность подписаться на обновление значения, нужно задать ему тип Observable. -->\n\nСинтаксис:\n* `observable(value)` (только для массивов и объектов)\n* `observable.box(value)` (для примитивных значений)\n* `@observable property = value` (только в классах)\n\n```js\nimport { observable } from 'mobx';\n\n// массивы и объекты\nconst items = observable([1, 2, 3]);\nconst item = observable({ title: 'Foo', description: 'Bar' });\n\n// примитивы\nconst string = observable.box('string');\nconst number = observable.box(7);\nconst boolean = observable.box(true);\n\n// Observable.box создаёт объект. Для получения примитивного значения нужно использовать метод get()\nconsole.log(number.get()) // 7\n\n// внутри классов можно использовать декораторы (официально пока ещё экспериментальные) или функции\nclass ItemStore {\n  @observable item = { title: 'Foo', description: 'Bar' }\n  @observable string = 'string' // в декораторе примитив без .box\n  /* или */\n  item = observable({ title: 'Foo', description: 'Bar' })\n  string = observable.box('string')\n}\n```\n\n### Action\n\n**Action** — всё, что изменяет State.  \n\nВ отличие от архитектуры Flux и её производных, MobX не ставит ораничений на то, как должны быть обработаны события пользователя.\n\nПростые Actions для массивов и объектов\n```js\nconst item = observable({ title: 'foo', description: 'bar' });\nconst items = observable([1, 2, 3]);\n\n// Actions\nitem.title = 'new';\nitem.title = `${item.title} title`;\nitem.description = 'new description';\nitems.pop()\nitems.push(4)\nitems[1] = 8;\n```\nПростые Actions для примитивов:\n```js\nlet number = observable.box(1);\n\n// Action\nnumber.set(3); // в консоль выведется 'Changed to 3'\n\n// как ниже делать нельзя, поскольку это перезапишет переменную number, сделав её обычным примитивом\nnumber = 2; // в консоль не выведется ничего\nnumber.set(4); // TypeError: number.set is not a function\n```\nMobX сам заботится, чтобы все изменения State, произошедшие при помощи Action, автоматически обработались в Derivations и Reactions.\n\nЕсли строить архитектуру управления состоянием при помощи MobX, то лучше всего использовать встроенный функционал `action`.\n* `action(fn)`\n* `action(name, fn)`\n* `@action classMethod()`\n\nВ этом случае так же следует запретить любые изменения State (то есть изменения любого Observable) вне Actions.\n```js\nimport { configure } from 'mobx';\n\nconfigure({ enforceActions: 'always' });\n```\nС такой конфигурацией простые примеры выше с Actions не будут работать (после появления Reactions), поскольку будет ошибка: `Error: [mobx] Since strict-mode is enabled, changing observed observable values outside actions is not allowed`.  \n\nНужно изменить код следующим образом:\n```js\nimport { observable, action } from 'mobx';\n\nconst item = observable({ title: 'foo', description: 'bar' });\nconst items = observable([1, 2, 3]);\n\nconst changeItem = action(({ title, description } = {}) => {\n  item.title = title;\n  item.description = description;\n  \n  // нельзя переприсваивать, поскольку item перестанет быть Observable\n  item = { title, description };\n});\n\nconst addItem = action(item => items.push(item));\n```\n```js\nlet number = observable.box(1);\n// set - автоматически является action, но если его добавить в функцию, то это уже работать не будет\nnumber.set(3); // может работать\nconst set = value => number.set(value); // не работает\nconst set = action(value => number.set(value)); // работает\n```\n\n\n### Derivation\n\n**Derivation (computed values)** — любое значение, которое может вычисляется автоматически после обновления State.  \nDerivation может быть переменной, UI-компонентой и многим другим.  \nDerivations используются в приложении, как и Observables.  \n\nНа практике Derivation — результат выполнения функции, которая имеет тип Computed.  \nВ этой функции не должно быть сайд-эффектов (side effects).\n\nСинтаксис:\n* `computed(() => derivation)`\n* `@computed get property() { return derivation; }` (только в классе)\n\n```js\nconst statement = observable.box(3 > 1); // true\n\nconst oppositeStatement = computed(() => !statement.get());\n\nconsole.log(oppositeStatement); // false\ntrueStatement.set(false);\nconsole.log(oppositeStatement); // true\n```\n\n### Reaction\n\n**Reaction** — это функция, которая запускается автоматически после обновления State.  \nReaction похож на Derivation, но вместо генерации нового значения в нём обрабатываются сайд-эффекты: вывод в консоль, запросы к серверу и прочее.\n\n```js\n// Автоматически вызывается для всех Observable значений, использующихся в сайд-эффектах\nautorun(() => { /* side effects here */ }));\n\n// подписка на изменения\nautorun(() => console.log(item)); // сработает только при инициализации (ссылка на объект не меняется) и выведет в консоль Observable\nautorun(() => console.log('Title changed', item.title)); // при обновлении поля поля title объекта item\nautorun(() => console.log('Title or description changed', item.title, item.description)); // при обновлении одного из полей title или description объекта item\nautorun(() => console.log('Data changed', { ...item })); // при обновлении любого поля объекта item\n\n// подписка на изменения\n// Observable — объект. Чтобы получить значение переменной number, нужно использовать number.get()\nautorun(() => console.log('Changed to', number.get()); \n\nautorun(() => { console.log('Item changed', item) });\n\nwhen(() => condition, () => { /* side effects here */ });\n\nreaction(() => data, data => { /* side effects here */ });\n\nreaction(\n  () => arr.map(item => [item.a, item.b]),\n  data => console.log(\"CHANGED A,B:\", data)\n);\n```\n\nПри использовании MobX с React наиболее популярный Reaction — `observer`.  \nОн оборачивает метод класса `render()` или функциональный компонент в `autorun`, тогда в случае изменения любого Observable внутри них, компонент автоматически перерендерится.  \n```jsx\nimport { Fragment } from 'react';\nimport { render } from 'react-dom';\nimport { observable } from 'mobx';\nimport { observer } from 'mobx-react';\n\nconst state = observable({\n  number: 0,\n});\n\nconst increment = () => {\n  state.number += 1;\n};\n\n// пример функциональной компоненты\nconst Number = observer(({ state }) => (\n  <button onClick={increment}>Click me to increase: {state.number}</button>\n));\n\n// пример класса\n@observer\nclass NumberClass extends React.Component {\n  render() {\n    return <button onClick={increment}>Click me to increase: {this.props.state.number}</button>;\n  }\n}\n\nconst App = () => (\n  <Fragment>\n    <Number state={state} />\n    <NumberClass state={state} />\n  </Fragment>\n);\n\nrender(<App />, document.getElementById('root'));\n```\n\n# Дополнительно\n\n## Реализация Flux от Facebook и её использование\n*Функционал Dispatcher*:\n- `register(callback: function): string` — регистрирует Callback, возвращает его идентификатор id.\n- `dispatch(action: object): void` — отправляет Action во все зарегистрированные Callbacks.\n- `isDispatching(): boolean` — возвращает true, если происходит отправка (dispatching) в данный момент, false иначе.\n- `waitFor(ids: string[]): void` — ожидает выполения Callbacks, имеющих идентификаторы ids, прежде, чем продолжать выполнять текущий Callback.\n- `unregister(id): void` — разрегистрирует Callback по id.\n\n*Функционал ReduceStore*:\n* `getState(): T` — получение полного состояния текущего Store. Если Store неизменяемый (immutable), то следует переопределить метод и не передавать состояние напрямую.\n* `getInitialState(): T` — задание начального состояния текущего Store. Вызывается только один раз: во время создания.\n* `reduce(state: T, action: object)` — изменяет или не изменяет текущее состояние в зависимости от Action. Метод обязательно должен быть переопределён; должен быть чистым (pure), без сайд-эффектов.\n* `areEqual(one: T, two: T): boolean` — проверяет, совпадают ли две версии состояния. Если Store неизменяемый, то не нужно переопределять этот метод.\n```ts\nimport { Dispatcher } from 'flux';\nimport { ReduceStore } from 'flux/utils';\n\nconst ItemDispatcher = new Dispatcher();\n\n/* Action Creators */\nexport const changeFetchStatus = isFetching => ItemDispatcher.dispatch({\n  type: 'CHANGE_FETCH_STATUS',\n  isFetching,\n});\n\nexport const fetchItems = () => ItemDispatcher.dispatch({\n  type: 'FETCH_ITEMS',\n  items: [1, 3],\n});\n\ninterface IItemStore {\n  items: any[];\n  isFetching: boolean;\n}\n\nclass ItemStore extends ReduceStore<IItemStore> {\n  constructor() {\n    super(ItemDispatcher);\n  }\n\n  getInitialState(): IItemStore {\n    return ({\n      items: [],\n      isFetching: false,\n    });\n  }\n\n  getState(): IItemStore {\n    return ({\n      ...this._state,\n    });\n  }\n\n  reduce(state: IItemStore, action: object): IItemStore {\n    switch (action.type) {\n      case 'CHANGE_FETCH_STATUS':\n        return ({\n          ...state,\n          isFetching: action.isFetching,\n        });\n    \n      case 'FETCH_ITEMS':\n        return ({\n          ...state,\n          items: [...action.items],\n        });\n    \n      default:\n        return state;\n    }\n  }\n}\n\nexport default new ItemStore();\n```\n\n## Redux Thunk vs Redux Saga\n\n### Redux Thunk\n\n**Thunk** — функция, которая оборачивает выражение, чтобы отложить его выполнение.  \n\n```js\n// обычный Action Creator\nconst doSomething = params => ({ type: 'DO_SOMETHING', ...params });\n\n// обычный Bound Action Creator\nconst boundDoSomething = params => dispatch({ type: 'DO_SOMETHING', ...params });\n```\nВ обоих случаях нет места асинхронным функциям.\n\nТакже можно заметить, что функция `dispatch(action)` сама по себе *возвращает Action*, то есть результат вызова функций `doSomething()` и `boundDoSomething()` одинаков: возвращается Action типа `DO_SOMETHNG`.\n\nБиблиотека `redux-thunk` вместо возвращения Action или вызова `dispatch(action)` возвращает функцию, что позволяет отложить вызов функции `dispatch(action)`, а перед вызовом проводить какие-то дополнительные операции, в том числе и асинхронные.\n```js\n// Action Creator при использовании redux-thunk\nconst doSomething = params => dispatch => dispatch({ type: 'DO_SOMETHING', ...params });\n\n// Action Creator c асинхронной операцией при использовании redux-thunk\nconst doSomething = params => async (dispatch) => {\n  await new Promise(resolve => setTimeout(resolve, 1000));\n  return dispatch({ type: 'DO_SOMETHING', ...params });\n};\n```\nПриятным бонусом является то, что в функции мы можем контролировать возвращаемое значения, поскольку не обязательно возвращать результат выполнения `dispatch(action)`).\n```js\nconst doSomething = params => async (dispatch) => {\n  await new Promise(resolve => setTimeout(resolve, 1000));\n  dispatch({ type: 'DO_SOMETHING', ...params });\n  const data = { /* ... */ };\n  return data;\n};\n```\nТаким образом можно вернуть какие-то данные в компоненту (например, пойманные ошибки).\n\n### Redux Saga\n\n**Saga** — архитектурный паттерн их микросервисной архитектуры для реализации транзакции, охватывающей (span) несколько сервисов. \n\nВнутри сервиса доступны ACID-транзакции, но между несколькими сервисами их использовать нельзя, поэтому и используется *Saga*, последовательно запускающая локальные транзакции в каждом сервисе. В случае, если какая-нибудь локальная транзакция из последовательности не проходит, Saga отменяет всю последовательность при помощи компенсирующих транзакций.\n\nRedux Saga реализована как промежуточный слой (middleware), позволяющий проделывать сайд-эффекты (например, запросы к серверу), при помощи ES6-генераторов.\n\nФункция-генератор возрващает объект-итератор. Каждый вызов метода итератора `next()` выполняет код до следующего выражения `yield` и останавливается.\n```js\nfunction* addTwo(x) {\n  yield x;\n  for (let i = 0; i < 2; i++) {\n    yield x += 1;\n  }\n}\n\nlet it = addTwo(0);\nconsole.log(it.next()); // { value: 0, done: false }\nconsole.log(it.next()); // { value: 1, done: false }\nconsole.log(it.next()); // { value: 2, done: false }\nconsole.log(it.next()); // { value: undefined, done: true }\n```\nДля началы работы с сагами необходимо создать сагу-наблюдателя и сагу-рабочего.\n\n**Сага-наблюдатель** (Watcher Saga) следит за Actions и создаёт новую задачу на каждый отправленный Action при помощи вспомогательной функции `takeEvery`. \n```js\nimport { takeEvery } from 'redux-saga/effects'\n\nfunction* watchFetchArticles() {\n  yield takeEvery('FETCH_ARTICLES', fetchArticles);\n}\n```\n\n**Эффект** (Effect) — простой JavaScript-объект (plain JS Object), содержащий необходимые инструкции, которые должен выполнить `redux-saga middleware`. Например: отправить (dispatch) Action, вызвать асинхронную функцию.\n\n**Сага-рабочий** (Worker Saga) использует эффекты, чтобы обработать Action.\n```js\nimport axios from 'axios';\nimport { call, put } from 'redux-saga/effects'\n\nfunction* fetchArticles() {\n  try {\n    const articles = yield call(axios.get, '/articles');\n    yield put({ type: 'FETCH_SUCCESS', payload: articles });\n  } catch (error) {\n    yield put({ type: 'FETCH_ERROR', payload: error });\n  }\n}\n```\n\nВ примере выше используются два основных эффекта.  \nПервым эффектом является `call(fn, ...args)`, описывающий асинхронный HTTP-запрос.\n```js\n{\n  CALL: {\n    fn: axios.get,\n    args: ['/articles']\n  }\n}\n```\nВторым — `put(action)`, заменяющий `dispatch`.\n```js\n{\n  PUT: {\n    action: { type: 'FETCH_SUCCESS', payload: articles }\n  }\n}\n```\n\nТаким образом, вместо вызова сайд-эффектов напрямую\n```js\nfunction* fetchArticles() {\n  const articles = yield axios.get('/articles');\n  dispatch({ type: 'FETCH_SUCCESS', articles })\n}\n```\nиспользуются Эффекты, являющиеся простыми объектами, что вносит большую гибкость и позволяет с лёгкостью их тестировать.\n\nЕсли есть несколько отправленных одновременно Actions, то `takeEvery` вызывает несколько экземпляров саги-рабочего, наделяя саги свойством параллелизма (concurrency).\n\n## Использование React с Redux\n\n### Возможная структура\n\nДля маленьких проектов и быстрых решений:\n```\n  - resources/\n  -- item.js\n  -- store.js\n```\n\nХорошо расширяемая структура (разбиение по смыслу):\n```\n - resources/\n -- item/\n --- item.actions.js\n --- item.reducer.js\n --- item.selectors.js\n --- item.types.js\n -- store.js\n```\n\nТакже расширяемая, но очень удобная (разбиение по типу файлов):\n```\n - resources/\n -- actions/\n --- item.actions.js\n -- reducers/\n --- item.reducer.js\n -- selectors/\n --- item.selectors.js\n -- types/\n --- item.types.js\n -- store.js\n```\n### Настройка\n\n0) Устанавливаем зависимости\n```npm\nnpm i redux react-redux redux-thunk\n```\n\n1) Создаём основные компоненты:\n\nTypes\n```js\nexport const DELETE_ITEM = 'DELETE_ITEM';\n```\n\nActions (Actions, Action Creators, Bound Action Creators)\n```js\n/* item.actions */\nimport { DELETE_ITEM } from './item.types';\n\nconst deleteItem = id => dispatch => dispatch({ type: DELETE_ITEM, id });\n```\n\nReducer\n```js\n/* item.reducer.js */\n\nconst initialState = {\n  items: [],\n  /* ... */\n};\n\nconst itemReducer = (state = initialState, action) => { /* ... */ };\n\nexport default itemReducer;\n```\n\nSelectors (вспомогательные функции для получения некоторой части state)\n```js\n/* item.selectors */\n\n/* state - все данные в Store\n   item - название Reducer, отвечающего за данные для этой компоненты\n   items - массив элементов */\nconst getItemById = (state, id) => state.item.items.find(item => item.id === id);\nconst getItemsLength = state => state.item.items.length;\n```\n\n2) Создаём Store, комбинируя Reducers и добавляя redux-thunk в качестве middleware:\n```js\n/* store.js */\nimport {\n  createStore,\n  combineReducers,\n  applyMiddleware,\n} from 'redux';\nimport thunk from 'redux-thunk';\nimport { itemReducer } from './item/item.reducer';\n\nconst reducer = combineReducers({ /* ... */ });\n\nconst store = createStore(reducer, applyMiddleware(thunk));\n\nexport default store;\n```\n\n3) Оборачиваем главную компоненту `<App>` компонентой `<Provider>`, в которую передаём созданный Store.\n```jsx\nimport React from 'react'\nimport { render } from 'react-dom'\nimport { Provider } from 'react-redux'\nimport App from './App'\nimport store from './store'\n\nconst AppContainer = () => (\n  <Provider store={store}>\n    <App />\n  </Provider>\n);\n\nrender(<AppContainer />, document.getElementById('root'));\n```\n\n4) Оборачиваем компоненту, которую хотим подключить к Redux, компонентой высшего порядка при помощи функции `connect(mapStateToProps, mapDispatchToProps)`.\n* `mapStateToProps(state, props)` отвечает за передачу части Store в props оборачиваемой компоненте (эта часть будет передаваться каждый раз после обновления в Store).\n* `mapDispatchToProps` отвечает за оборачивание переданных ему Action Creators в функцию dispatch (чтобы в оборачиваемой компоненте не нужно было вызывать dispatch).\n\n```jsx\nimport { connect } from 'react-redux';\nimport { deleteItem } from 'resources/item/item.actions';\nimport { getItemsLength, getItemById } from 'resources/item/item.selectors';\n\nconst ItemView = ({\n  item,\n  itemsLength,\n  dispatchDeleteItem\n}) => (\n  <>\n    <div class=\"item\">{item}</div>\n    <div class=\"index\">`Item 1 of ${itemsLength}`</div>\n    <button onClick={dispatchDeleteItem}> \n  </>\n);\n\n// пусть props.id - параметр, который передаётся от родительской компоненты или от роутера\nconst mapStateToProps = (state, props) => ({\n  item: getItemById(state, props.id),\n  itemsLength: getItemsLength(state),\n});\n\nconst mapDispatchToProps = {\n  dispatchDeleteItem: deleteItem,\n};\n\nexport default connect(mapStateToProps, mapDispatchToProps);\n```\n"
  },
  {
    "path": "FunctionalProgramming.md",
    "content": "- [Объекты и функции первого класса](#объекты-и-функции-первого-класса)\n- [Функции высшего порядка](#функции-высшего-порядка)\n- [Композиция функций](#композиция-функций)\n- [Мемоизация](#мемоизация)\n- [Каррирование](#каррирование)\n\n## Объекты и функции первого класса\n\n*Объект* называется **объектом первого класса** (First-class Object/Entity/Citizen), если он \n1. может быть передан в функцию как аргумент,  \n2. может быть возвращен из функции как результат, \n3. может быть присвоен переменной, \n4. может быть сохранён в структуру данных.\n\nОстальные объекты считаются **объектами второго класса** (Second-class Objects).\n\n<!-- 5) может быть создан во время выполнения программы,  -->\n\nФункции, являющиеся объектами первого класса, называют **функциями первого класса** (First-class Function).\n\nВо *многих языках программирования* понятие *функции первого класса ассоциируется* с понятием **анонимной функции** (anonymous functions).\n\nВ *JavaScript все функции* являются *функциями первого класса*.\n```js\n// 1., 2. и 3.\nconst createLogger = log => (...args) => log(...args);\n\n// 3.\nconst log = createLogger(console.log);\nlog('qq'); // \"qq\"\n\n// 4.\nconst speaker = {\n  hello: () => log('hello'),\n}\n```\n\n## Функции высшего порядка\n\n**Функция высшего порядка** (Higher-order Function) —  *функция*, *принимающая другую функцию* в *качестве аргумента* или *возвращающая функцию* в *качестве результата* своего выполнения.\n\n*JavaScript* является *событийно-ориентированным* (Event-driven) языком. Многие *действия выполняются* по *наступлению определённых событий* или выполняются *строго после других действий*. *Функции высшего порядка*, принимающие в *качестве параметра другие функции*, помогают добиться такого поведения. *Функции-параметры* называют **функциями обратного вызова**.\n```js\nconst callback = () => console.log('done');\n\ndocument.addEventListener('onClick', callback);\n\nsetTimeout(callback, 1000);\n\nconst p = new Promise(res => res());\np.then(callback);\n\nconst calculate = (cb) => {\n  const result = 1 + 2;\n  cb();\n  return result;\n};\ncalculate(callback);\n```\n\nПример *функции высшего порядка*, реализующей *замыкание*. Она замыкает переменную `count` и возвращает другую функцию, изменяющую замкнутую переменную.\n```js\n/* счётчик */\nconst createCounter = () => {\n  let count = 0;\n  return () => count += 1;\n}\n\nconst counter = createCounter();\nconsole.log(counter()); // 1\nconsole.log(counter()); // 2\n\nconst anotherCounter = createCounter();\nconsole.log(anotherCounter()); // 1\n```\nПример *функции высшего порядка*, реализующей *[каррирование](#каррирование)*. Она возвращает другую функцию, использующую свой параметр `y` и параметр функции высшего порядка `x`, доступный из замыкания.\n```js\n/* конкатенация двух строк */\nconst concat = x => y => `${x} ${y}`;\n\nconsole.log(concat('Simple')('notes')); // \"Simple notes\"\n```\nПример *функции высшего подярка*, принимающей и возвращающей React-компонент (компоненты в React являются функциями), которая называется **компонентом высшего порядка** (Higher-order Component, HOC).\n```jsx\nconst user = { name: 'Max' };\n\nconst withUser = Component => props => (\n  <Component {...props} user={user} />\n);\n\nconst Article = withUser(props => <div>{props.user.name}</div>);\n```\n```jsx\nconst createConditionalComponent => condition => Component => props => (\n  condition ? <Component {...props} /> : null;\n);\n\nconst Text = () => (<span>Notes</span>);\n\nconst AlwayRenderingText = createConditionalComponent(true)(Text);\nconst NeverRenderingText = createConditionalComponent(false)(Text);\n```\n\n## Композиция функций\n\n**Композиция функций** (Function Composition) — *применение одной функции* к *результату другой*. \n\nФункция `h` — *композиция* функций `g` и `f`, если `h(x) = g(f(x))`. Обозначение: `h(x) = (g ∘ f)(x)`.\n\n```js\nconst increment = val => val + 1;\nconst decrement = val => val - 1;\n\nconst foo = decrement(increment(0)); // композиция\nconsole.log(foo); // 0\nconst bar = increment(increment(0)); // композиция\nconsole.log(bar); // 2\n```\n\nПри помощи композиции можно составлять из простых функций выражения любой сложности.\n```js\nconst divide = (value, divider) => value / divider;\nconst pow = (value, power) => value ** power;\ndivide(pow(3, pow(2, 2)), 3); // 27 = (3 ^ (2 * 2)) / 3\n```\nЕсли в *композиции* участвует слишком *много функций*, то её код становится *трудно читаемым*. Для таких случаев можно использовать *функцию* `compose`, которая принимает несколько функции и составляет из них композицию справа налево (начинает с последней и заканчивает первой).\n```js\nconst compose = (...fns) => initialValue => fns.reduceRight((value, fn) => fn(value), initialValue);\n```\n```js\nconsole.log(compose(\n  increment,\n  increment,\n  decrement,\n  increment,\n  increment,\n)(4)); // 7\n\n/* массив из 10 функций increment */\nconst fns = Array(10).fill(increment); // [ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ]\nconsole.log(compose(...fns)(0)); // 10\n```\nЕсли промежуточная функция в композиции имеет больше одного параметра, её можно вызвать в теле функции-обёртки с нужным количеством параметров.\n```js\ndivide(pow(3, pow(2, 2)), 3); // 27 = (3 ^ (2 * 2)) / 3\n/* преобразуется в */\ncompose(\n  value => divide(value, 3),\n  value => pow(3, value),\n  () => pow(2, 2),\n)();\n```\nПоскольку функция не может вернуть больше одного значения, промежуточные функции композиции могут принять только один аргумент. Симулировать поведение передачи нескольких аргументов можно, если передавать их массивом или объектом.\n```js\ncompose(\n  value => divide(value, 3),\n  value => pow(3, value),\n  ([value, power]) => pow(value, power),\n)([2, 2]);\n```\nСтоит ещё раз отметить, что функция `compose` начинается с последней переданной функции, а не с первой.\n\n### Pipe\nФункция `pipe` работает аналогично функции `compose`, но составляет композицию из своих функций-аргументов слева направо.\n```js\nconst pipe = (...fns) => initialValue => fns.reduce((value, fn) => fn(value), initialValue);\n```\nЕё использование выглядит более естественным.\n```js\npipe(\n  ([value, power]) => pow(value, power),\n  value => pow(3, value),\n  value => divide(value, 3),\n)([2, 2]);\n```\n\n### Несколько аргументов\nМожно переписать `compose` и `pipe` таким образом, чтобы первая функция принимала несколько аргументов.\n```js\nconst compose = (...fns) => fns.reduceRight((f, g) => (...args) => g(f(...args)));\nconst pipe = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));\n/* или */\nconst compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));\nconst pipe = (...fns) => fns.reduceRight((f, g) => (...args) => f(g(...args)));\n```\n```js\npipe(\n  (value, power) => pow(value, power),\n  value => pow(3, value),\n  value => divide(value, 3),\n)(2, 2);\n```\nПромежуточные функции по-прежнему не смогут принять больше одного параметра.\n\nОписание работы функции `compose` по шагам. Метод `reduce` при отсутствии начального значения кладёт первый элемент массива первым параметром, второй - вторым параметром.\n```js\nconst compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));\n/* далее псевдокод */\ncompose(a, b, c, d)(1, 3)\n/* I */\n(a, b) => args => a(b(args)) := k\n/* II */\n(k, c) => args => k(c(args)) := m\n/* III */\n(m, d) => args => m(d(args)) := n\n/* IV */\nn := args => a(b(c(d(args)));\n/* V */\nn(1, 3) := a(b(c(d(1, 3)))\n```\n\n## Мемоизация\n\n**Мемоизация** (memoization, запоминание) — *сохранение результатов выполнения функций* для *предотвращения повторных вычислений*.\n\n*Мемоизация* подразумевает проведение *проверки* перед каждым *вызовом мемоизируемой функции*:\n* если функция вызывалась ранее с такими же параметрами, как сейчас, — вернуть результат из памяти,\n* иначе — вычислить и записать в память.\n\n<!--\nОчевидно, что мемоизация предполагает работу с чистыми функциями, которые всегда при тех же параметрах вернут тот же результат.\n```js\n/* функция ниже не подойдёт для мемоизации */\nconst rand = (min, max) => min + Math.random() * (max - min);\n```\n-->\n\nНапишем простую функцию инкремента.\n```js\nconst inc = val => val + 1;\nconsole.log(inc(5)); // 6\n```\nМемоизируем её.\n```js\nlet memory = {};\n\nconst incMemo = (val) => {\n  if (memory[val] === void 0) {\n    memory[val] = inc(val);\n  } else {\n    console.log('took from memory!');\n  }\n  return memory[val];\n};\n\nconsole.log(incMemo(5)); // 6\nconsole.log(memory); // { 5: 6 }\nconsole.log(incMemo(5)); // 'took from memory!', 6\n```\n\n### Обобщение для любой функции одного аргумента\nОбобщим функцию мемоизации, чтобы её можно было переиспользовать для *любой функции одного аргумента*.\n```js\nlet memory = {};\n\nconst memo = fn => (val) => {\n  if (memory[val] === void 0) {\n    memory[val] = fn(val);\n  } else {\n    console.log('took from memory!');\n  }\n  return memory[val];\n};\n```\n```js\nconst dec = memo(val => val - 1);\n\nconsole.log(dec(6)); // 5\nconsole.log(dec(6)); // 'took from memory!', 5\n```\n\n### Когда использовать мемоизацию?\n\n*Мемоизация экономит время*, но при этом *использует память*. \n\n*Простейшие арифметические операции* `+`, `-`,  `*`, `/` *выполняются слишком быстро*, чтобы сохранять их в памяти. *Поиск по ключу* в хранилище *не выполнится быстрее* (и это не считая затрат памяти). \n\nОбычно *мемоизируют только чистые функции* (вызов с определённым набором параметров всегда вернёт один и тот же результат). Нет смысла мемоизировать функцию, генерирующую рандомные значения: проще положить результат её выполнения в переменную и использовать её. Нет смысла мемоизировать запрос к серверу, так как на сервере данные могут изменяться: намного важнее показывать пользователю последнюю версию.\n\n*Использовать мемоизацию* стоит, когда имеются *трудоёмкие алгоритмы* и *сложные преобразования*, *результат* которых зависит *только от входных параметров* и *не меняется* при *повторяющемся наборе параметров*.\n\n*Использовать мемоизацию* стоит, если только *сэкономленное на вычислениях время стоит затреченной памяти* и при этом *время для приложения* является *более ценным ресурсом*, чем *память*. \n\nЭто обычно касается *рендеринга*. Например, работа с `DOM` считается *достаточно трудоёмкой*, при этом *пользователь должен увидеть интерфейс как можно скорее* — на помощь приходят мемоимзация и некоторые другие техники.\n\n### Несколько аргументов\nПерепишем функцию `memo` таким образом, чтобы она могла принимать несколько аргументов.\n```js\nlet memory = {};\n\nconst memo = fn => (...args) => {\n  const key = args.toString();\n  if (memory[key] === void 0) {\n    memory[key] = fn(...args);\n  } else {\n    console.log('took from memory!');\n  }\n  return memory[key];\n};\n```\n```js\nconst mulFn = (a, b) => a * b;\nconst mul = memo(mulFn);\n\nconsole.log(mul(3, 7)); // 21\nconsole.log(memory); // { \"3, 7\": 21 }\nconsole.log(mul(3, 7)); // 'took from memory!', 21\n```\n### Отдельное хранилище для каждой функции\nВ примерах выше используется общее хранилище для всех мемоизируемых функций. Перепишем функцию `memo` таким образом, чтобы у каждой функции `fn` было своё хранилище.\n\nДля этого используем метод `toString()`, возвращающий строковое представление функции.\n```js\nconsole.log(mulFn); // \"(a, b) => a * b\"\n```\nБудем использовать это представление в качестве ключа в хранилище `memory`. Этому ключу будет соответствовать хранилище для конкретной функции `fn`.\n```js\nlet memory = {};\n\nconst memo = fn => (...args) => {\n  const fnKey = fn.toString();\n  memory[fnKey] = memory[fnKey] || {};\n  const argsKey = args.toString();\n  if (memory[fnKey][argsKey] === void 0) {\n    memory[fnKey][argsKey] = fn(...args);\n  } else {\n    console.log('took from memory!');\n  }\n  return memory[fnKey][argsKey];\n};\n```\n```js\nconst divideFn = (a, b) => a / b;\nconst divide = memo(divideFn);\n\nconsole.log(divide(21, 7)); // 3\nconsole.log(memory); // { \"(a, b) => a * b\": { \"3,7\": 21 } }\nconsole.log(memory[divideFn.toString()]); // { \"3,7\": 21 }\nconsole.log(divide(21, 7)); // 'took from memory!', 3\n```\n### Очистка хранилищ\nСейчас хранилище `memory` очищается только вручную: результаты вычислений остаются даже тогда, когда функция больше не используется.\n```js\nlet sumFn = (...args) => args.reduce((acc, val) => acc + val, 0);\nconst sumKey = sumFn.toString();\n\nconst sum = memo(sum);\nconsole.log(sum(1, 2, 3)); // 6\nconsole.log(sum(1, 2, 3)); // 'took from memory!', 6\n\nsumFn = null; // удаляем функцию\nconsole.log(memory[sumKey]); // { \"1,2,3\": 6 } (данные о sum остались)\n```\nЕсли бы мы использовали в качестве `fnKey` не строковое представление функции, а саму функцию, то результат был бы тем же: любое значение (в том числе непримитивное), используемое как ключ, приводится к стровокому значению. Будем использовать `WeakMap`, принимающий объект в качестве ключа и очищающий ячейку хранилища, когда этот объект удаляется (как вручную, так и сборщиком мусора).\n```js\nlet memory = new WeakMap();\n\nconst memo = fn => (...args) => {\n  if (!memory.has(fn)) {\n    memory.set(fn, {});\n  }\n  const argsKey = args.toString();\n  if (memory.get(fn)[argsKey] === void 0) {\n     memory.get(fn)[argsKey] = fn(...args);\n  } else {\n    console.log('took from memory!');\n  }\n  return memory.get(fn)[argsKey];\n};\n```\n```js\nlet sumFn = (...args) => args.reduce((acc, val) => acc + val, 0);\n\nconst sum = memo(sumFn);\n\nconsole.log(sum(1, 2, 3)); // 6\nconsole.log(sum(1, 2, 3)); // 'took from memory!', 6\nconsole.log(memory.get(sumFn)); // { \"1,2,3\": 6 }\nsumFn = null;\nconsole.log(memory.get(sumFn)); // undefined\n```\n### Гибкая генерация ключей\nЧто, если *аргументами функции* `fn` будут являться *объекты*, *массивы*, другие *функции*?\n```js\n/* объекты */\nlet args = [{ foo: 1 }, { bar: 2 }];\nconsole.log(args.toString()); // \"[object Object],[object Object]\"\n/* массивы */\nargs = [[1], [2, 3]];\nconsole.log(args.toString()); // \"1,2,3\"\nargs = [1, 2, 3];\nconsole.log(args.toString()); // \"1,2,3\"\n/* другие функции */\nargs = [() => 1, () => 2];\nconsole.log(args.toString()); // \"() => 1,() => 2\"\n```\nЧтобы сделать функцию `memo` более расширяемой, следует добавить возможность *генерации ключей*.\n```js\nlet memory = new WeakMap();\n\nconst defaultKeyGenerator = args => args.toString();\n\nconst memo = (fn, keyGerenator) => (...args) => {\n  if (!memory.has(fn)) {\n    memory.set(fn, {});\n  }\n  const argsKey = keyGerenator ? keyGerenator(args) : defaultKeyGenerator(args);\n  if (memory.get(fn)[argsKey] === void 0) {\n     memory.get(fn)[argsKey] = fn(...args);\n  } else {\n    console.log('took from memory!');\n  }\n  return memory.get(fn)[argsKey];\n};\n```\nДля *объектов* можно использовать их *JSON-представления*.\n```js\nimport equal from 'deep-equal';\n\nconst keyGenerator = args => args.map(item => JSON.stringify(item)).join('__');\n\nconst deepEqual = memo(equal, arrayKeyGenerator);\n\nconst foo = { a: { b: 1 } };\nconst bar = { a: { b: 1 } };\n\nconsole.log(deepEqual(foo, bar)); // true\nconsole.log(deepEqual(foo, bar)); // took from memory!, true\nconsole.log(memory.get(equal)); // { '{\"a\":{\"b\":1}}__{\"a\":{\"b\":1}}': true }\n```\nДля массивов можно использовать `join(separator)` вместо стандартного `toString()`.\n<!--\n\n```js\nconst sumFn = (...args) => args.reduce((acc, val) => acc + val, 0);\nconst sum = memo(sumFn);\n\nconst sumArraysFn = (...args) => args.reduce((acc, val) => acc + (Array.isArray(val) ? sum(...val) : val), 0);\nconst sumArrays = memo(sumArraysFn, args => args.join('__'));\n\nconsole.log(sumArrays([1,2,3], [2,3], [1,2,3])) // took from memory!, 17 (подсчёт [1, 2, 3] берётся из памяти)\nconsole.log(memory.get(sumFn)); // { \"1,2,3\": 6, \"2,3\": 5 }\nconsole.log(sumArrays([1,2,3], [2,3], [1,2,3])); // took from memory!, 17\nconsole.log(memory.get(sumArraysFn)); // { \"1,2,3__2,3__1,2,3\": 17 }\n```\n\n-->\n\n## Каррирование\n**Каррирование**, **карринг** (currying) — *преобразование функции несокольких аргументов* в *цепочку функций одного аргумента*.\n\n```js\nconst sum = (x, y) => x + y;\nsum(1, 3); // 4\n\nconst curriedSum = x => y => x + y;\ncurriedSum(1)(3); // 4\n```\nКаждый *вызов преобразованной функции возвращает новую функцию*, в которой *один параметр фиксируется*. Такая функция называется **частичной**, **частично применённой**.\n```js\nconst tmp = curriedSum(1); //  y => 1 + y\ntmp(3); // 4\n```\n*Функцию преобразования* можно написать следующим образом.\n```js\nconst curry = (fn) => { \n const curried = (...args) => { \n   if (args.length === fn.length) { \n     return fn(...args); \n   } else { \n     return nextArg => curried(...args, nextArg); \n   } \n }; \n return curried; \n}\n```\nИли ещё короче.\n```js\nconst curry = fn => curried = (...args) => \n  args.length === fn.length\n   ? fn(...args)\n   : nextArg => curried(...args, nextArg);\n```\n\n### Когда использовать каррирование?\n* Когда можно удачно *переиспользовать частично применённые функции*.\n```js\nconst error => type => message = { /* ... */ };\n\nconst authError = error(\"Auth\"); // частично применённая функция (фиксируем аргумент)\n\n/* несколько вариантов развития событий */\nauthError(\"Incorrect password\");\nauthError(\"Banned user\");\n```\n* Когда при решении *поставленной задачи* можно использовать *только функции одного аргумента* (например, при работе с lambda-функциями).\n\n## Thunk\n\nhttps://en.wikipedia.org/wiki/Thunk\n"
  },
  {
    "path": "Git.md",
    "content": "- [Основные понятия Git](#основные-понятия-git)\n\t- [Как работает Git](#как-работает-git)\n\t- [Состояния файлов](#состояния-файлов)\n\t- [Три области Git](#три-области-git)\n\t- [Коммит и индекс](#коммит-и-индекс)\n\t- [Ветвление](#ветвление)\n\t- [HEAD и верхушка ветки](#head-и-верхушка-ветки)\n- [Основные команды Git](#основные-команды-git)\n\t- [merge](#merge)\n\t\t- [Как работает merge](#как-работает-merge)\n\t\t- [Fast-forward merge](#fast-forward-merge)\n\t\t- [True merge](#true-merge)\n\t\t- [Конфликты при true merge и их разрешение](#конфликты-при-true-merge-и-их-разрешение)\n\t- [rebase](#rebase)\n\t\t- [Как работает rebase](#как-работает-rebase)\n\t\t- [rebase vs merge](#rebase-vs-merge)\n\t\t- [Другие возможности rebase и интерактивный режим](#другие-возможности-rebase-и-интерактивный-режим)\n\t- [pull и fetch](#pull-и-fetch)\n\t- [Откат изменений с reset, checkout, revert, restore](#откат-изменений-с-reset-checkout-revert-restore)\n \t\t- [`reset`](#reset)\n     \t\t- [`checkout`](#checkout)\n       \t\t- [`revert`](#revert)\n          \t- [`restore`](#restore)\n- [Версионирование и тэги](#версионирование-и-тэги)\n- [Полезные возможности Git](#полезные-возможности-git)\n\t- [Отмена последнего коммита](#отмена-последнего-коммита)\n\t- [Перенос коммитов из одной локальной ветки в другую](#перенос-коммитов-из-одной-локальной-ветки-в-другую)\n\t- [Смена CRLF на LF одновременно для всех файлов в проекте](#смена-crlf-на-lf-одновременно-для-всех-файлов-в-проекте)\n- [Git flow](#git-flow)\n- [SSH Github & Gitlab](#ssh-github--gitlab)\n- [Git Config](#git-config)\n- [Git Bash](#git-bash)\n\n# Основные понятия Git\n\n## Как работает Git\n\n**Git** — *система контроля версий*, то есть система, которая *следит* за *изменениями файлов*, *позволяет фиксировать определённые состояния изменений* и *возвращаться* к *любому* из этих *состояний* при необходимости.\n\nВ сравнении со всеми другими системами контроля версий, *Git* имеет *уникальный подход* к *работе* со своими *данными*. Все *остальные системы хранят набор файлов* и *списки изменений* (дельты) этих *файлов* с *течением времени*.\n\n| Файл          | Версия I           | Версия II | Версия III |\n| :-------------: |:------------------:| :-----: | :-----: |\n| A.txt     | delta I    | delta II | — |\n| B.txt     | — | delta I | delta II |\n\nВ таблице выше `delta` — список изменений файла, прочерк — отсутствие данных об изменениях.\n\n*Git рассматривает данные* не как таблицы изменений конкретных файлов, а как **поток снимков** (stream of snapshots).\n\nКаждый **снимок** (shapshot) означает *сохранение определённого состояния проекта*. *Система запоминает*, *как выглядит каждый файл* проекта на *момент сохранения* (делает снимок) и *сохраняет ссылку* на этот *снимок*. Если *файл не изменился*, то *Git не запоминает* его *вновь*, а *создаёт ссылку* на *идентичную версию файла* из *предшествующего снимка*. \n\n| Файл          | Версия I           | Версия II | Версия III |\n| :-------------: |:------------------:| :-----: | :-----: |\n| A.txt     | A1    | A2 | *A2* |\n| B.txt     | B1 | *B1* | B2 |\n\nВ таблице выше *выделенные курсивом версии файлов не менялись* в *новой версии*, поэтому были использованы ссылки на версии из предыдущих снимков.\n\nБлагодаря такому подходу *Git* чем-то *похож на файловую систему*. <!--, что даёт определённые преимущества при работе с разными ветками. -->\n\n## Состояния файлов\n\nGit делит все файлы на *отслеживаемые* и *неотслеживаемые*. \n\n**Отслеживаемые** (tracked) файлы — файлы, которые были в *последнем снимке состояния проекта*, **неотслеживаемые** (untracked) файлы — *все остальные*.\n\n<!--Это зависит от того, отслеживает ли Git изменения конкретного файла или нет. -->\n\nТакже имеются категория **игнорируемых** (ignoring) файлов. Изменения этих файлов Git игнорирует.\n\nЧтобы *сделать файл* (папку) *игнорируемым*, необходимо его *добавить* в файл `.gitignore`, который должен лежать в проекте (обычно в корневой папке).\n\nПример содержимого `.gitignore`.\n```js\nnode_modules/\nlogs/\ndist/\n.env\n```\n\n### Состояния отслеживаемых файлов\n\n* **Неизменённый** (Unmodified). Файл не изменён.\n* **Изменённый** (Modified). Файл изменён локально.\n* **Подготовленный** (Staged). Файл изменён локально и помечен для включения в следующий коммит.\n* **Зафиксированный** (Committed). Версия файла сохранена в истории Git.\n\n## Три области Git\n* **Рабочая директория** (Working Directory). Файлы распаковываются из сжатой базы данных репозитория и располагаются на локальном диске для чтения и записи. Здесь происходит работа с ними до тех пор, а затем они переходят\n* **Область подготовленных файлов** (Staging Area). Здесь содержатся сведения о подготовленных файлах и их изменениях, которые должны попасть в следующий коммит. Эта область является частью папки .git.\n* **Папка .git** (.git directory, Repository). Здесь хранятся метаданные (данные о данных) и Git-объекты текущего проекта, в том числе история коммитов.\n\n## Коммит и индекс\n\n**Коммит** (англ. `Commit`) — отметка (точка, этап) в истории Git. К этой точке *можно вернуться*. Вся история проекта состоит из цепочки связанных друг с другом коммитов. Каждый коммит имеет *родительский коммит* и *зависит* от него. \n\nБлизкими по смыслу коммиту понятиями являются **версия** (англ. `version`) и **снимок** (англ. `snapshot`).\n\n*Уникальным идентификатором коммита* является его *хеш*.\n```git\ncommit 54e2f882f077cb0f4a1ca0600eada25cc96c7e5a\ncommit ec1b065a072c545ca2849e0c05b60c520bdc39e4\n```\n\n**Индекс** (англ. `Index`) — *снимок следующего намеченного коммита*.\n\nЧтобы добавить файл из рабочей директории в индекс (область подготовленных файлов), используется команда `git add`.\n```js\n/* добавить изменённый файл */\ngit add <filename>\n\n/* добавить папку с изменёнными файлами */\ngit add <directory>\n\n/* добавить все изменённые файлы */\ngit add .\n```\n\nЧтобы добавить файлы из области подготовленных файлов в коммит, используется команда `git commit`.\n```js\ngit commit\n```\nКоманда выше открывает текстовый редактор по умолчанию для ввода сообщения коммита. Чтобы этого избежать, можно использовать флаг `-m` и передать в него сообщение коммита.\n```js\ngit commit -m \"Commit message here\"\n```\n\n## Ветвление\n\nВсе системы контроля версий поддерживают возможность ветвления.\n\n**Ветка** (англ. `Branch`) — независимая линия разработки проекта. \n\nВетка состоит из последовательности коммитов.\n\n**Базовый коммит ветки** (англ. `Base commit`) — коммит, с которого начиналась (была создана) ветка, её *корень*.\n\nБудем обозначать латинискими буквами хеши коммитов: `A`, `B`, `C` и т.д. \n```js\n/* Ветвление */\nfeature              |                  F —— G\n                     |                /\ndevelop              |          C —— D —— E\n\t             |         /\nmaster (основная)    |   A —— B\n```\n\nНа изображении выше базовым коммитом для ветки `master` является коммит `A`,  для ветки `B` — `C`, для `feature` — `D`.\n\nИзначально выбирается **основная ветка**, которая будет хранить в себе основную историю развития приложения (обычно `master`). Это не обязательно должна быть ветка, в которой создавался первый коммит, но чаще всего это так. \n\nОт основной ветки создаются новые ветки, от них тоже при необходимости создаются ветки. \n\nБазовым коммитом новой ветки становится последний на момент создания коммит текущей ветки.\n\nСуть ветвления заключается в том, что ветки (содержащие новую функциональность приложения) могут развиваться независимо от основной и других веток, а затем их изменения попадают (или не попадают) в основную или другую ветку путём слияния. После слияния второстепенные ветки обычно удаляются.\n\nДля управления ветками используется команда `branch`.\n```js\n/* создание новой ветки */\ngit branch <branch_name>\n\n/* список всех веток */\ngit branch -a\n\n/* переименование ветки */\ngit branch -m <old_name> <new_name>\n\n/* удаление ветки */\ngit branch -D <branch_name>\n```\n\nДля переключения между ветками используется команда `checkout`.\n```js\n/* переключение на ветку branch_name */\ngit checkout <branch_name>\n```\n\nДля слияния веток используется команда `merge`, о которой будет рассказано [позже](#merge).\n\n\n## HEAD и верхушка ветки\n\nПоскольку последний коммит знает своего родителя, а тот — своего, по последнему коммиту можно восстановить всю цепочку коммитов любой ветки. Это значит, что ветка может быть представлена указателем на последний коммит, сделанный в этой ветке.\n\nЭто позволяет Git хранить ветку не как последовательность коммитов, а просто как ссылку на последний её коммит.\n\n**Верхушка ветки** (Branch tip, Branch head) — последний коммит ветки. У каждой ветки есть одна вершутка.\n\n**HEAD** — указатель на текущую ветку (указатель на последний коммит текущей ветки). `HEAD` может быть только один в текущий момент времени для всего проекта. \n\nКогда мы создаём новую ветку, её базовым коммитом становится тот, на который ссылается `HEAD`.\n\nЕсли открыть файл `HEAD` в папке `.git`, то можно увидеть, что он содержит ссылку на текущую ветку.\n```js\ncat .git/HEAD\n/* ref: refs/heads/master */\n\ncheckout develop\ncat .git/HEAD\n/* ref: refs/heads/develop */\n```\n\nЕсли открыть папку `refs/heads`, то там можно видеть, что Git хранит для каждой ветки отдельный файл, в котором хранится хеш последнего коммита ветки.\n```js\ncat ./git/refs/heads/master\n/* 1ed2bf4eebbcd0515a638b48550a7eb81c7c01e5 */\n```\n\n# Основные команды Git\n\n## merge\n\n**Команда merge** позволяет *слить* (смержить) *истории двух веток в одну*, то есть из *двух последовательностей коммитов создать одну*.\n\n**Целевой ветка** (target branch) *сливается* в **текущую ветку** (current branch), которую также называют *рабочей*.\n\n```js\ngit merge <target_branch>\n```\n\nПри слиянии изменения затрагивают только текущую ветку: целевая ветка остаётся без изменения.\n\n### Как работает merge\n\nGit просматривает цепочку коммитов обеих веток, пытаясь найти их общий коммит. Обычно такой коммит имеется (как минимум, начальный коммит).\n\nДовольно редко бывают случаи, когда между двумя ветками нет ни одного общего коммита. По умолчанию Git отказывается сливать такие ветки в одну, но можно использовать флаг `--allow-unrelated-histories`, который позволит это сделать.\n\nЕсли *общий коммит* между двумя ветками *найден*, то возможны *два случая*\n* *Только в одной ветке* были *новые коммиты* с *момента общего коммита*. В таком случае *одна ветка* является *продолжением другой*. Используется `fast-forward merge`.\n* *Обе ветки* имеют *новые коммиты* с *момента общего коммита*. Это означает, что они развивались параллельно, независимо от друга. Используется `true merge`.\n\n### Fast-forward merge\n\n**Fast-forward merge** (*перемотка вперёд*) используется, если *одна ветка* является *продолжением другой*.\n\nВозможны *два случая*: *целевая ветка длиннее* или *короче текущей*.\n\nРассмотрим случай, когда *целевая ветка впереди* (длиннее) *текущей*. Тогда *при слиянии веток указатель на последний коммит текущей ветки* (верхушка ветки) *сдвигается на последний коммит целевой ветки*. После такого слияния *истории двух веток становятся идентичными*.\n```js\n/* до слияния веток develop и master целевая ветка develop была\nвпереди на 2 коммита C и D */\ndevelop          |          C —— D\n\t         |         /\nmaster (current) |   A —— B\n```\n```js\ngit checkout master\ngit merge develop\n```\n```js\n/* после слияния верхушки веток указывают на один и тот же коммит D */\ndevelop          |          C —— D\n\t         |         /\nmaster (current) |   A —— B —— C —— D\n```\nЕсли отменить последний коммит в ветке `master`, то в ней отменится только коммит `D`.\n```js\n/* отмена последнего коммита при помощи git reset HEAD~ */\ndevelop          |          C —— D\n\t         |         /\nmaster (current) |   A —— B —— C\n```\nТаким образом, чтобы *отменить изменения*, появившиеся в *текущей ветке* в результате *слияния*, необходимо *удалить* ровно *столько коммитов*, сколько было *новых коммитов* в *целевой ветке*.\n\nВ случае, когда *целевая ветка позади* (короче) *текущей*, *ничего не произойдёт*, поскольку *текущая ветка* уже *содержит все актуальные изменения* (содержит все коммиты целевой ветки).\n```js\ngit checkout develop\ngit merge master\n```\n\n`Fast-forward merge` *не будет применён* при использовании *флага* `--no-ff`, применится `true merge`.\n```js\ngit merge --no-ff branch_name\n```\n\n### True merge\n\nЕсли текущая и целевая ветки развивались независимо друг от друга в течение какого-то времени, тогда каждая из них имеет новые коммиты с момента общего коммита веток и использовать `fast-merge` не получится. В этом случае применяется **true merge** (3-way merge), которые использует **3-х сторонний алгоритм** (3-way algorithm). Алгоритм так называется, поскольку учитываются 3 стороны (состояние до изменений, изменения целевой ветки, изменения текущей ветки).\n\nПри применении `true merge` создаётся особый тип коммита, имеющий сразу два родительских коммита, — **merge commit**. В нём содержатся новые изменения, которые были в целевой ветки, но не были в текущей.\n\n```js\n/* до слияния веток с момента общего коммита B целевая ветка develop \nимеет 2 коммита C и D, текущая ветка master имеет один коммит E */\ndevelop          |          C —— D\n\t         |         /\nmaster (current) |   A —— B —— E\n```\n```js\ngit checkout master\ngit merge develop\n```\n```js\n/* после слияния в текущей ветке master появился merge commit H,\nсодержащий в себе все изменения коммитов C, D, E */\ndevelop          |           C —— D\n\t         |         /       \\\nmaster (current) |   A —— B —— E —— H\n```\nКак и в случае `fast-forward merge`, изменения `true merge` затрагивают только текущую ветку, поэтому `merge commit` создаётся только в текущей ветке (коммит `H` появился только в `master`).\n\nАналогичный результат бы получился, если бы ветка `develop` была текущей, а `master` — целевой. Разница лишь в том, что в этом случае коммит `H` хранился бы только в `develop`.\n\nЕсли из `master` удалить последний коммит, то состояние текущей ветки станет таким, каким оно было до слияния. Таким образом, чтобы отменить все последствия слияния двух веток стратегией `true merge`, достаточно удалить только `merge commit`.\n```js\n/* отмена последнего коммита при помощи git reset HEAD~ */\ndevelop          |          C —— D\n\t         |         /\nmaster (current) |   A —— B —— E\n```\n\n### Конфликты при true merge и их разрешение\n\nЕсли новые коммиты сливающихся веток затрагивают одни и те же файлы и по-разному изменяют их, возникают **конфликты слияния** (merge conflicts). \nGit не может автоматически разрешить их, поскольку изменения производились параллельно. Разработчик должен сам решить, какие изменения стоит оставить.\n\nДля *отображения* *конфликтов* по умолчанию использует *2 версии файла* и следующий *синтаксис*.\n* `<<<<<<<` — изменения коммита текущей ветки.\n* `>>>>>>>` — изменения коммита целевой ветки.\n* `=======` — разделяющая полоса.\n\n```js\n<<<<<<<\n/* изменения в текущей ветке */\n=======\n/* изменения в целевой ветке */\n>>>>>>>\n```\n\nМожно настроить команду `merge` таким образом, чтобы также показывалось и *состояние до изменений*. За это отвечает свойство `merge.conflictStyle` со значением `diff3`. Синтаксис: `|||||||`.\n```js\n<<<<<<<\n/* изменения в текущей ветке */\n|||||||\n/* до изменений */\n=======\n/* изменения в целевой ветке */\n>>>>>>>\n```\n\nЧтобы *разрешить конфликт*, следует *выбрать необходимые изменения* и *убрать всё лишнее*, затем эти изменения добавляются в `merge commit`.\n\nПример конфликта в одном из файлов (в коммите текущей ветки используется `const`, в коммите целевой ветки — `let`).\n```js\n<<<<<<< HEAD (Current Change)\nlet name = 'Notes';\n=======\nconst name = 'Notes';\n>>>>>>> develop (Incoming Change)\n```\nВ этом примере можно оставить следующую строку и добавить её в `merge commit`.\n```js\nlet name = 'Notes';\n```\n\n## rebase\n\n### Как работает rebase\n\n**Базовый коммит ветки** (Base commit) — *коммит*, с которого *начинается* (создана) *ветка*.\n\n**Перебазирование** (Rebasing) — *перемещение последовательности коммитов* к *новому базовому коммиту*. \n```js\ngit rebase <base>\n```\n\n*Перебазирование переписывает историю текущей ветки*.\n\nКогда мы указываем ветку в качестве `<base>` для команды `rebase`, берётся последний коммит этой ветки.\n\n```js\n/* до перебазирования целевая ветка develop по сравнению с master\nимеет новый коммиты C и D, базовым для неё является коммит B */\ndevelop (current) |          C —— D\n\t          |         /\nmaster            |   A —— B —— E\n```\n```js\ngit checkout develop\ngit rebase master\n```\n```js\n/* после перебазирования базовым коммитом для develop стал коммит E\n(последний в master), C* и D* — копии коммитов C и D с новыми хешами */\ndevelop (current) |                C* —— D*\n\t          |               /\nmaster            |   A —— B —— E\n```\n\nЕсли бы перебазировался не `develop`, а `master`, то изменились бы все коммиты ветки `master` с момента их общего коммита с `develop` (в данном случае это только коммит `E`).\n```js\ngit checkout master\ngit rebase develop\n```\n```js\n/* после перебазирования базовым коммитом для master остался коммит A\n(поскольку начало историй обеих веток совпадают), E* — копия\nкоммита E с новым хешем */\ndevelop          |          C —— D\n\t         |         /\nmaster (current) |   A —— B —— C —— D —— E*\n```\n\nПо примерам выше видно, что все новые коммиты перебазируемой ветки пересоздаются. Отменить последствия `rebase` невозможно (хеш старых коммитов утерян).\n\nПри *перебазировании возможны* такие же *конфликты*, как и при `true merge`. *Решаются* они *аналогично*.\n\n### rebase vs merge\n\nСамое *основное отличие* заключается в том, что `rebase` *перезаписывает историю*, а `merge` только *дополняет* её.\n\n`Fast-forward` просто копирует коммиты из одной ветки в конец ветки.\n\n`True merge` создаёт новый `merge commit`, содержащий все необходимые текущей ветке изменения целевой ветки (с необходимыми правками, если были конфликты).\n\nНедостаток `fast-forward merge`: не может работать с ветками, которые развивались параллельно.\n\nНедостаток `true merge`: `merge commits` загрязняют историю приложения. \n\n`Rebase` позволяет *заменять базовый коммит текущей ветки* на *другой коммит*. Это позволяет достигнуть идеальной линейной истории путём её постоянного изменения, поскольку можно всегда размещать новые ветки в конце базовой. Тем не менее, эта история будет характерна только текущей ветке.\n\nЕсли `rebase` затронет коммиты, которые не были созданы в текущей ветке, то при слиянии с другой веткой Git увидит две разные истории и создаст большой merge commit, который использует обе версии. Появится дублирование одних и тех же изменений.\n\nТаким образом, `rebase` лучше использовать только для новых коммитов, если есть необходимость как-то подправить их. Если затрагиваются коммиты, которые имеются в других ветках и в будущем есть вероятность того, что эти ветки сольются вместе, `rebase` лучше не использовать.\n\n### Другие возможности rebase и интерактивный режим\n\nПомимо перебазирования команда `rebase` имеет интерактивный режим, который позволяет полностью переписать историю определённого числа коммитов. Для перехода в интерактивный режим используется флаг `-i`. Для выбора `N` последнимх коммитов используется указатель `HEAD~N`.\n```js\ngit checkout develop\ngit rebase -i HEAD~2 /* изменение истории двух последних коммитов текущей ветки */\n```\nВ этом случае будет открыт текстовый редактор со следующим содержимым.\n```dockerfile\npick C Message for the commit C\npick D Message fof the commit D\n\n# Rebase B..D onto B\n#\n# Commands:\n#  p, pick = use commit\n#  r, reword = use commit, but edit the commit message\n#  e, edit = use commit, but stop for amending\n#  s, squash = use commit, but meld into previous commit\n#  f, fixup = like \"squash\", but discard this commit's log message\n#  d, drop <commit> = remove commit\n#\n# These lines can be re-ordered; they are executed from top to bottom.\n#\n# If you remove a line here THAT COMMIT WILL BE LOST.\n#\n# However, if you remove everything, the rebase will be aborted.\n#\n# Note that empty commits are commented out\n```\nДо символов `#` показывается список всех выбранных для редактирования коммитов. По умолчанию к каждому из них принимается команда `pick` (оставить как есть), но её можно заменить текстом на одну из следующих:\n* `reword` - изменение сообщения коммита (окно для изменения сообщения появится после).\n* `squash` - совместить коммит с предыдущим (далее надо будет набрать общий текст для нового скомбинированного коммита).\n* `fixup` - совместить коммит с предыдущим (с отменой сообщения коммита).\n* `drop` - удалить коммит и его изменения из истории (это можно также сделать, удалив строку с коммитом из списка).\n* Также можно менять строки в списке местами, тогда соответствующие им коммиты также поменяются местами в истории.\n\nТаким образом, при помощи интерактивного `rebase` можно делать с историей практически всё, что угодно, но делать это нужно осторожно. Желательно, не затрагивая те части истории проекта, которые имеются в других ветках.\n\nЕсли нужно перезаписать сообщение последнего коммита, то можно вместо `rebase` использовать команду `commit` с флагом `--ammend`:\n```js\ngit commit --amend -m \"Updated last commit message\"\n```\n\n## pull и fetch\n\n**Команда git fetch** используется для того, чтобы получить информацию о последних изменениях на удалённой ветке (`origin/`). Таким образом можно узнать, были ли изменения вообще.\n```js\ngit fetch\n/*\nFrom github.com:YourName/RepositoryName\n   2eefe71..ac391ab  develop   -> origin/develop\n   fca7c62..c31477c  feature/1 -> origin/feature/1\n * [new branch]      feature/2 -> origin/feature/2\n * [new branch]      feature/3 -> origin/feature/3\n*/\n````\nВыше можно видеть, что ветки `develop` и `feature/1` отличаются от своих одноимённых удалённых веток хешем последних коммитов, а ветки `feature/2` и `feature/3` новые и их нет локально.\n\nКоманда `git fetch` помимо информации об изменениях скачивает и сами изменения, но их не слияние с локальной веткой не происходит. Это можно проверить командой `git diff`.\n```js\ngit diff origin/develop\n/*\ndiff --git a/Git.md b/Git.md\nindex abe21d5..e207862 100644\n+++ b/client/Dockerfile\n- Hello\n+ Hello, Notes!\n*/\n```\n\nПовторный вызов команды `git fetch` ничего не выведет, поскольку изменения уже были подгружены.\n\n**Команда git pull** также, как и `git fetch` получает изменения и информацию о них, но дополнительно сливает изменения в локальную ветку.\n\nПо сути, `git pull` объединяет в себе две команды: `git fetch` и `git merge`.\n\n## Откат изменений с `reset`, `checkout`, `revert`, `restore`\n\nОбычно `HEAD` указывает на верхушку ветки (англ. `branch tip`).\n\n**Detached HEAD** — специальный тип HEAD, который может быть установлен пользователем, чтобы посмотреть, как выглядит изменения на определённом коммите в ветке.\n\nКаждая из команд `reset`, `checkout`, `revert`, `restore` позволяют откатывать (англ. `undo`) изменения, но делают это по-своему.\n\n### `checkout`\n\nБазовое использование **команда checkout** просто перемещает указатель `HEAD`, что позволяет переключиться в любую точку истории проекта и начать работать оттуда.\n\n```js\n/* до checkout HEAD ветки совпадал с верхушкой (tip) \nветки master и указывал на коммит E */\n                     (HEAD,\n\t             master)        \nA ——— B ——— С ——— D ——— E\n```\n```js\ngit checkout B\n```\n```js\n/* после checkout */\n  (detached\n     HEAD)           (master)\nA ——— B ——— С ——— D ——— E\n```\nЕсли ввести `git branch`, то он выдаст ветку `(HEAD detached at B)`.\n\nЕсли ввести `git log`, то коммит B в текущей ветке будет последним.\n```js\n/* git log */\n    (HEAD)\nA ——— B\n```\nТаким образом, при помощи команды `checkout` Git позволяет переключиться на определённый снимок проекта в прошлом, не изменяя при этом реальную ветку. Можно сделать какие-то тестовые изменения, посмотреть или скопировать какие-то файлы, а затем вернуться на полную версию ветки.\n\n### Откат определённых файлов при помощи `checkout`\n\nGit позволяет откатывать отдельные файлы до определённого коммита при помощи продвинутого использования команды `checkout`.\n\nОткат конкретного файла до определённого коммита по хешу коммита `<commit_hash>`. \n```\ngit checkout <commit_hash> -- <path_to_file>\ngit checkout 1e4d903 -- client/package-lock.json\ngit checkout 37fc11b -- server/package-lock.json\n```\nОткат файла до предыдущего коммита (хеш опускается).\n```\ngit checkout -- <path_to_file>\ngit checkout -- client/package-lock.json\n```\nОткат файла к родительскому коммиту конкретного коммита `<commit_hash>` при помощи `~1`:\n```\ngit checkout <commit_hash>~1 -- <path_to_file>\n```\n\n### `revert`\n\n**Команда revert** выбирает коммит и создаёт новый коммит, который откатывате изменения выбранного коммита.\n```js\n/* до revert */\n\t        (HEAD,\n\t        master)        \nA ——— B ——— С ——— D\n```\n```js\ngit revert C\n```\n```js\n/* после revert создаётся коммит E, отменяющий все изменения коммита D,\nи таким образом возвращающий состояние проекта на момент коммита B */\n\t              (HEAD,\n\t              master)        \nA ——— B ——— С ——— D ——— E\n```\nЕсли при помощи `revert` откатывается не последний коммит (например, C), то могут появиться конфликты (поскольку коммит D зависеть от некоторых изменений коммита C).\n\nКоманда `revert` является безопасным вариантом для отката изменений в публичном репозитории, поскольку коммиты не удаляются, а создаются — история не перезаписывается.\n\n### `reset`\n\n**Команда `git reset`** сбрасывает все изменения и историю до определённого коммита.\n```js\n/* до reset */\n\t        (HEAD,\n\t        master)        \nA ——— B ——— С ——— D\n```\n```js\ngit reset B\n```\n```js\n/* после reset */\n   (HEAD,\n   master)        \nA ——— B\n```\n\nПримеры\n```\ngit reset HEAD~1 /* переставляет указатель HEAD на один коммит */\ngit reset HEAD~2 /* переставляет указатель HEAD на два коммита */\n```\n\nКоманда `git reset` имеет несколько режимов:\n* `git reset --soft` - перемещает `HEAD` к определённому коммиту, сохраняет все локальные `staged` изменения с последних коммитов\n* `git reset --mixed` (используется по умолчанию) - перемещает `HEAD` к определённому коммиту, сохраняет локальные `staged` изменения и переводит их в `unstaged`\n* `git reset --hard` - перемещает `HEAD` к определённому коммиту и стирает все изменения локальные `staged` и `unstaged` изменения\n\nТаким образом команда `git reset --hard` полностью, безвозвратно удаляет как все закоммиченные, так и незакоммиченные изменения (получается чистое удаление).\nВ то же время команды `git reset --soft` и `git reset --mixed` сохраняют локальные (незакоммиченные) изменения, но `--soft` оставляет их в `staging`, а `--mixed` - не оставляет.\n\nНапример, \n```\n\t\t(HEAD,\n\t        master)        \nA ——— B ——— С ——— D\n```\nВсе три команды `git reset --soft B`, `git reset --mixed B` (`git reset B`), `git reset --hard B` локально изменят историю ветки `master` на\n```\n   (HEAD,\n   master)        \nA ——— B\n```\nНо при этом:\n* После `git reset --soft B` изменения коммитов `C` и `D` появятся во вкладке `staged changes` (`staging area`).\n* После `git reset --mixed B` изменения коммитов `C` и `D` появятся во вкладке `unstaged changes` (`working area`).\n* После `git reset --mixed B` изменения коммитов `C` и `D` исчезнут, `working tree is clean`.\n\nОтмена команды `git reset --<mode>`:\n* Для `--soft`: `git commit`.\n* Для `--mixed`: `git add`, затем `git commit`.\n* Для `--hard`: изменения не хранятся ни в одной ветке, так что вернуть их не так просто. Закоммиченные ранее и откаченные теперь изменения можно найти при помощи команды `git reflog`, незакоммиченные изменения пропали навсегда и их уже не вернуть. \n\n###  `restore`\n\n**Команда `git restore` восстановит (вернёт) состояние файла `<file_name>` к предыдущему (последнему) коммиту, эффективно откатывая любые изменения с момента последнего комита.\n```\ngit restore <file_name>\n```\nОткатить все файлы в проекте:\n```\ngit restore .\n```\n\n### `reflog`\n\n\n## Версионирование и тэги\n- [О версионировании и тэгах](#о-версионировании-и-тэгах)\n- [Семантическое версионирование, формат версий](#семантическое-версионирование-формат-версий)\n- [Создание тэга](#создание-тэга)\n- [Отображение информации о версии](#отображение-информации-о-версии)\n- [Переключение между версиями](#переключение-между-версиями)\n- [Удаление тэга](#удаление-тэга)\n- [Полный флоу релиза и создания версии](#полный-флоу-релиза-и-создания-версии)\n\n### О версионировании и тэгах\n\n**Версионирование** (англ. `version control`, `source control`) - это *процесс отслежвания изменений* в *коде программного обеспечения* (англ. `software code`) и *управления ими*.\n\nКогда *продукт готов к использованию*, происходит его *выпуск* - **релиз** (англ. `release`), то есть *продукт становится доступен пользователям* (например, *в виде приложения* в App Store или *вебсайта*). Со временем *продукт улушается*, а значит происходят *новые релизы* - и *пользователи* получают *доступ к новым функциям приложения*. Таким образом, при *каждом релизе* появляется **новая версия приложения** (англ. `version`). \n\nЧаще всего, *версионирование подразумевает*, что вы можете использовать *любую* из *доступных* для *скачивания версий приложения*. То есть *один пользователь может использовать одну версию*, *другой* - *другую*, и *у обоих будет всё работать одинаково хорошо*.\n\n*Git* сам по себе является *системой контроля версий*: он *сохраняет, фиксирует состояние приложения* (*делает контрольные точки приложения*) с *каждым коммитом* и *отслеживает разницу между двумя состояниями приложения*, то есть *дельту изменений между двумя коммитами*. Это *позволяет разработчикам работать независимо друг от друга*, а *процесс разработки приложения напоминает дерево*.\n\n*Каждый коммит* в *некотором понимании* является *\"новой версией\" приложения*, но зачастую эта *версия ещё не готова к релизу*, а значит её *не увидят пользователи* и её *нельзя называть *новой версией* приложения *как таковой*.\n\nПо этой причине, в Git *помимо коммита существует альтернативный способ обозначить версию приложения*, *способ сопоставить коммиты и релизы приложения один к одному*. Этим способом является использование **тэгов** (англ. `tag`).\n\n*Тэг* является ничем иным, как *обычной пометкой*, *приклеивающейся к коммиту*. *Единственным необходимым* для *тэга* является *хэш коммита* (*уникальный идентификатор коммита*) и *название тэга*.\n\nЧаще всего *название тэга* имеет *формат* `vX.X.X`.\n\n### Семантическое версионирование, формат версий\n\n*Семантический формат номерации версии*: `<X>.<Y>.<Z>`, где `<X>`, `<Y>`, `<Z>` - *целые неотрицательные числа*.\n\nПри *переходе на новую версию* в приложении *увеличивается одно из чисел* `<X>`, `<Y>`, `<Z>` (один из *счётчиков*).\n\nНиже будут *представлены правила, по которым выбирается счётчик для версии*.\n\n#### Мажорная версия\n\nСчётчик `<X>` называют **мажорной версией** (англ. `major version`, `MAJOR`) *приложения*. *Приложение переходит* на *новую мажорную версию* (то есть счётчик `<X>` увеличивается на `1`, `Y` и `Z` сбрасываются до `0`), если его *изменения обратно не совместимы с предыдущиями версиями*. \n\nНапример, вы используете в своём приложении функцию `f` из пакета `help` версии `1.2.0`. Обновляя пакет `help` до версии `2.0.0` вы обнаруживаете, что функция `f` ломает приложение, поскольку она была удалена из пакета `help`, была переименована в функцию `g` или просто *поменяла тип возвращаемого значения*. Такие изменения называют **ломающими изменениями** (англ. `breaking changes`). Таким образом, *обновляя пакет до новой мажорной версии*, вы ставите под угрозу работоспособность своего приложения. *Код вашего приложения придётся изменять*, если в *мажорной версии были затронуты использующиеся* в вашем приложении *фукнции*.\n\n#### Минорная версия\n\nСчётчик `<Y>` называют **минорной версией** (англ. `minor version`, `MINOR`). *Приложение переходит* на *новую минорную версию* (то есть счётчик `<Y>` увеличивается на `1`, `X` остаётся прежним, `Z` сбрасывается), если была *добавлена новая функциональность* в приложение, а *старая функциональность не была затронута*. В этом случае *обратная совместимость сохраняется*. \n\nНапример, вы используете в своём приложении функцию `f` из пакета `help` версии `1.2.1`. Обновляя пакет `help` до версии `1.3.0` вы обнаруживаете, что функция `f` не ломает приложение, поскольку единственным изменением в пакете было добавление новой функции `g`. Таким образом, *обновляя пакет* до *новой минорной версии*, вы *не должны менять код своего приложения*.\n\n#### Патч-версия\n\nСчётчик `<Z>` называют **патч-версией** (англ. `patch version`, `PATCH`). *Приложение переходит* на *новую патч-версию* (то есть только счётчик `<Z>` увеличивается на `1`, `X` и `Y` остаются прежними), если были в прилажение были внесены *сделаны обратно совместимые исправления*, то есть *новых функций не было добавлено*, а *старые функции* хоть и *были затронуты* в *коде*, но *возвращают тот же результат*. Таким образом, *обратная совместимость сохраняется*. \n\nНапример, вы используете в своём приложении функцию `f` из пакета `help` версии `1.2.0`. Обновляя пакет `help` до версии `1.2.1` вы *обнаруживаете*, что *всё работает как прежде*. Какие исправление могут войти в патч? Оптимизация скорости работы функции, удаление `console`-логов, исправление типографических ошибок, ошибок линтера, переименование внутренней переменной, написание тестов, документации и так далее - всё то, что *не влияет на параметры экспортируемых* из пакета *функций*, их *названия* и *их возвращаемые значения*.\n\n\n### Создание тэга\n\n*Обязательным* при *создании* тэга является лишь указание названия для тэга.\n```yml\ngit tag -a <version_name>\n```\n\n*Создающийся тэг прикрепляется к коммиту*. *Коммит можно выбрать вручную или* он будет *выбран автоматически*.\n\nДля того, чтобы *указать коммит при создании тэга*, необходимо передать хэш коммита:\n```yml\ngit tag -a <version_name> <commit_hash>\n```\nНапример,\n```yml\ngit tag -a v1.1.0 d51bbf173a527bef67a61fa57824763ffa22e302\n```\n\nЕсли *коммит не указан*, то *тэг автоматически навешивается* на *самый свежий коммит в ветке*, поэтому в таком случае *создавать тэг нужно в ветке* `production` уже *после мержа (релиза)* в неё.\n\nДля *создания тэга с описаниеем* используется команда:\n```yml\n$ git tag -a <version_name> -m <version_description>\n```\n```yml\n$ git tag -a v1.1.0 -m \"My version 1.1.0 (Feb, 11 2022)\"\n```\n\n*После создания тэга* необходимо *передать его* в *удалённый резпозиторий* (*автоматически он не передаётся так же*, как и *ветка*).\n```yml\n$ git push origin v1.1.0\n```\n\n### Отображение информации о версии\n\nДля *просмотра списка существующих тэгов (версий*) используется команда:\n```yml\ngit tag\n```\n```js\n/*\nv1.0.0\nv1.0.1\nv1.1.0\n*/\n```\n\nДля *отображения информации* об определённой *версии* используется команда `git show`:\n```yml\n$ git show <tagname>\n```\n```yml\n$ git show v1.1.0\n```\n```js\n/* \ntag v1.1.0\nTagger: Max Starling <17.max.starling@gmail.com>\nDate:   Fri Feb 22 17:33:49 2022 +0300\n\nMy version 1.1.0 (Feb, 11 2022)\n\ncommit d51bbf173a527bef67a61fa57824763ffa22e302\nAuthor: Max Starling <17.max.starling@gmail.com>\nDate:   Fri Feb 22 17:28:32 2022 +0300\n\n    Merge branch 'development'\n*/\n```\n\n### Переключение между версиями\nGit позволяет *переключаться между версиями так же*, как и *между ветками*:\n```yml\n$ git checkout <tagname>\n```\n```yml\n$ git checkout v1.2.0\n```\n\n### Удаление тэга\nДля *удаления тэга* используется команда\n```yml\n$ git tag -d <tagname>\n```\n*Удаляется только тэг, коммит остаётся*.\n\n### Полный флоу релиза и создания версии\n1) Переключаемся на ветку `production`\n```yml\n$ git checkout production\n```\n2) Делаем мерж из `development` ветки в `production`. Возможно, будет создан `merge commit` в ветке `production`:\n```yml\n$ git merge development\n```\n3) Смотрим список существующих версий\n```yml\n$ git tag\nv1.0.0\nv1.0.1\n```\n4) Создаём новый тэг соответствующий номеру версии в ветке `production`.\n```yml\n$ git tag -a v1.1.0 -m \"My version 1.1.0 (Feb, 11 2022)\"\n```\n5) Проверяем, что тэг повесился на самый последний коммит в ветке `production`\n```yml\n$ git log --oneline\n```\n```js\n/*\nd51bbf17 (HEAD -> master, tag: v1.1.0, origin/master) Merge branch 'development'\nccd4b31f Add dark mode\n...\n*/\n```\n6) Пушим тэг в *удалённый репозиторий* (`remote origin`)\n```yml\n$ git push origin v1.1.0\n```\n7) Заходим на `GitHub`, *находим тэг во вкладке* `Releases/Tags`.\n\nДополнительно можно сделать `Release Notes` в `Github`:\n1) Заходим в `GitHub` во вкладку `Releases`.\n2) Нажимаем `Draft a new release`.\n3) *Выбираем тэг из списка*, пишем *название релиза* и *описание* (а лучше нажать на `Auto-generate release notes`)\n![image](https://user-images.githubusercontent.com/22237384/153617979-f1635e5b-1aa7-4353-9fc6-5ff35e490eb1.png)\n4) Нажимаем `Publish release`\n5) Смотрим результат\n![image](https://user-images.githubusercontent.com/22237384/153618606-e47e5e68-b6fc-4c4e-8081-a6b3bb6590e4.png)\n\n\n## Полезные возможности Git\n\n<!--\n### Команда reset\n### Команда stash\n-->\n\n### Отмена последнего коммита\n```sh\ngit reset HEAD~\n```\n\n### Перенос коммитов из одной локальной ветки в другую\n\nНапример, перенос N коммитов из локальной ветки A и локальную ветку B.\n```sh\ngit checkout B\ngit merge A\ngit checkout A\ngit reset --hard HEAD~N # удаление N коммитов из ветки A\n```\n\n### Смена CRLF на LF одновременно для всех файлов в проекте\nЕсли вы работаете на Windows, то скорее всего после скачивания Git-репозитория у вас будет проблема с тем, что при открытии каждого файла в проекте стоит CRLF. При этом чаще всего удалённый сервер использует LF (характерный для Linux и Mac систем). Это может приводить к ошибкам линтера, а значит и к ошибкам в CI & CD пайплайнах. Более того, если линтер стоит локально, то и у вас проект, вероятно, проект не будет запускаться, пока вы не измените каждый открытый файл на LF.\n\nСледующий набор команд позволит решить проблему. Но сперва убедитесь, что нет незакоммиченных файлов - проще говоря, убедитель, что дерево изменений на данный момент пусто, иначе все изменения пропадут после команды `git reset --hard`.\n```sh\ngit config core.autocrlf false \ngit rm --cached -r . \ngit reset --hard\n```\n\n\n## Git flow\n\n**Фича** (Feature) — новая функциональность. \n\n### Разработка новой фичи\n* Разработка новых фич начинается с ветки `develop`, от которой создаётся новая ветка `feature/name`, где `name` - название фичи.\n* Разработчик переключается на новую ветку и начинает работать с ней.\n* После завершения фичи создаётся Pull Request.\n* Ветка `feature/name` сливается с (merge into) веткой `develop`. \n* Ветка `feature/name` удаляется.\n* Разработчик переключается обратно на ветку `develop`.\n\n### Релиз в production\n* От ветки `develop` создаётся ветка `release/vX.X.X`.\n* Ветка `release/vX.X.X` при необходимости помечается тэгом `vX.X.X`.\n* Разрешены мелкие исправления (minor bug fixes) в ветке `release/vX.X.X`.\n* Ветка `release/vX.X.X` сливается с веткой `master`.\n* Ветка `release/vX.X.X` сливается обратно (back-merge) с веткой `develop`.\n* Ветка `release/vX.X.X` удаляется.\n* Разработчик переключается обратно на ветку `develop`.\n\n## Hotfix в production\n* Если в production найден серьёзный баг, который нужно быстро исправить, от ветки `master` создаётся ветка `hotfix/name`, в которой делаются необходимые исправления.\n* Ветка `hotfix/name` сливается с веткой `master`.\n* Ветка `hotfix/name` сливается с веткой `develop`.\n* Ветка `hotfix/name` удаляется.\n\n\n## SSH Github & Gitlab\n\n* Открыть Bash.\n\n### Генерация ключей\n* Сгенерировать ключ для Github при помощи `ssh-keygen -t rsa -C \"email\" -f ~/.ssh/id_rsa_github`, где нужно заменить `email` на свой. `id_rsa_github` - название файла, где будет лежать приватный ключ. Команда также автоматически генерирует публичный ключ `id_rsa_github.pub` в той же папке.\n* Ввести ключевую фразу и повторить её (или просто два раза нажать `Enter`).\n\n* Сгенерировать ключ для Gitlab при помощи `ssh-keygen -t rsa -C \"email\" -f ~/.ssh/id_rsa_gitlab`, где нужно заменить `email` на свой.\n* Ввести ключевую фразу и повторить её (или просто два раза нажать `Enter`).\n\n### Cоздание ключа на Github\n* Скопировать публичный ключ `~/.ssh/id_rsa_github.pub` для Github. Например, вывести его на экран при помощи `cat ~/.ssh/id_rsa_github.pub` и скопировать через `Ctrl + C`.\n* Открыть на Github [Settings > SSH keys](https://github.com/settings/keys) и нажать на добавление нового ключа.\n* Вставить скопированный ключ, придумать название для него и сохранить.\n\n### Cоздание ключа на Gitlab\n* Скопировать публичный ключ `~/.ssh/id_rsa_gitlab` для Gitlab. Например, вывести его на экран при помощи `cat ~/.ssh/id_rsa_gitlab.pub` и скопировать через `Ctrl + C`.\n* Открыть на Gitlab `Settings > SSH keys`.\n* Вставить скопированный ключ, придумать название для него и сохранить.\n\n### Добавление SSH-ключа в SSH-agent\n* Добавить SSH-ключ для GitHub в SSH-agent `ssh-add ~/.ssh/id_rsa_github`. Если агент не запущен, то нужно сперва его запустить `eval $(ssh-agent -s)` или `ssh-agent bash`.\n* Добавить SSH-ключ для GitLab в SSH-agent `ssh-add ~/.ssh/id_rsa_gitlab`.\n* Проверить, что ключи добавлены через `ssh-add -L`.\n\nЕсли для каждой новой консоли запускать агент и добавлять ключ приходиться заново, то можно настроить *псевдоним*.\n\n**Псевдоним** (Alias) — аббревиатура, позволяющая избежать написания длинной последовательности команд.\n* Создадим файл `.bashrc` в корневой папке (в Windows: `C:/Users/<USERNAME>/) и поместим там следующее.\n```bash\nalias sa=\"eval `ssh-agent -s` ssh-add ~/.ssh/id_rsa_gitlab\"\n```\nТеперь каждый запуск команды `sa` в любой консоли Bash будет выполнять запуск агента и добавление ключа.\n\n### Конфигурация SSH\n\n* Создать файл `touch ~/.ssh/config`.\n* Вставить туда следующее\n```\n# config for github\nHost github.com\n   HostName github.com\n   User git\n   IdentityFile ~/.ssh/id_rsa_github\n# config for gitlab\nHost gitlab.com\n   HostName gitlab.com\n   User git\n   IdentityFile ~/.ssh/id_rsa_gitlab\n```\nНапример, командой `cat`\n```linux\ncat <<EOF > ~/.ssh/config\nHost github.com\n   HostName github.com\n   User git\n   IdentityFile ~/.ssh/id_rsa_github\n# config for gitlab\nHost gitlab.com\n   HostName gitlab.com\n   User git\n   IdentityFile ~/.ssh/id_rsa_gitlab\" >> ~/.ssh/config\nEOF\n```\n\n### Проверка работоспособности SSH\n* Попробовать сделать `git pull` через SSH.\n* Дополнительная проверка для Github: `ssh -T git@github.com` (режим отладки: `ssh -T git@github.com`).\n* Дополнительная проверка для Gitlab: `ssh -T git@gitlab.com` (режим отладки: `ssh -T git@gitlab.com`).\n\n### Ошибка `\"Host key verification failed\"`\n\nЕсли при *выполнении* команды `ssh -T git@github.com` *возникает ошибка* `\"Host key verification failed\"` как на скриншоте ниже\n![image](https://user-images.githubusercontent.com/22237384/167812973-c6076638-5320-411c-a72d-582061f31fc7.png)\nТогда следует *добавить хост* `github.com` в *список известных хостов* `known_hosts` следующей командой: \n```\nssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts\n```\nИ *проблема* должна *решиться*.  \n![image](https://user-images.githubusercontent.com/22237384/167813322-d3914c71-f020-44b5-b229-a0b17a1af693.png)\n\n\n\n## Git Config\n\n### Вывод конфига\n```\ngit config -l\n```\n\n### Имя пользователя и почта\n```js\n/* имя пользователя */\ngit config --global user.name \"Your Username\"\n/* электронная почта */\ngit config --global user.email \"your@email\"\n```\n\n## Git Bash\n**Командная оболочка** (Shell) — терминальное приложение (terminal app), используемое для взаимодействия с ОС посредством письменных команд.\n\n**Bash** (Bourne again shell, \"Born again shell\", \"возрождённый\" shell) — *усовершенствованная версия* ранней *командной оболочки Bourne shell*, исполняющей файлы формата `.sh` в `UNIX`. *Bash* является командной оболочкой *по умолчанию* для *Linux* и *macOS*.\n\n**Git Bash** — пакет, *устанавливающий Bash*, некоторые *базовые утилиты* и *Git* на *Windows*.\n\n### Убить запущенный процесс\nЧтобы на *определённом порте* *убить запущенный процесс*, нужно узнать его **PID** (Process Identifier).\n```css\nnetstat -ano | findstr :PORT /* например, :3000 */\n/* Скопировать PID из последнего стобца результата и вставить в команду ниже */\ntskill PID\n```\n"
  },
  {
    "path": "GraphQL-REST.md",
    "content": "- [REST](#rest)\n- [GraphQL](#graphql)\n  - [Преимущества GraphQL](преимущества-graphql)\n- [REST на практике](#rest-на-практике)\n  - [Данные в QUERY](#данные-в-query)\n  - [Данные в BODY](#данные-в-body)\n  - [Данные в PARAMS](#данные-в-params)\n\n# REST\n\nTODO\n\n# GraphQL\n\n**GraphQL** — *язык запросов* (query language) для API. \n\nКлиент посылает **запрос** (query) к сервису GraphQL и получает ответ в виде JSON-объекта по указанной в запросе схеме.\n\nНапример, клиент может послать запрос.\n```Graphql\nquery {\n  currentUser {\n    id\n    username\n    role\n  }\n}\n```\nЕсли сервер позволяет получить данные по заданной выше схеме, по клиенту придёт ответ, соответствующий этой схеме.\n```json\n{\n  \"data\": {\n    \"currentUser\": {\n      \"id\": \"1\",\n      \"username\": \"Notes\",\n      \"role\": \"Admin\"\n    }\n  }\n}\n```\n\n## Запросы query и mutation\n\nСуществуют два типа запросов (requests): *query* и *mutation*. \n\n**Запрос query** отвечает за получение данных, он не может их изменять (аналог GET-запроса в REST).\n```Graphql\nquery {\n  users {\n    id\n    username\n  }\n}\n```\n\n**Запрос mutation** отвечает за изменение данных (объединяет в себе возможности POST, PUT, DELETE и других в REST).\n```Graphql\nmutation {\n  logOut\n}\n```\n## Схемы и типы\n\nВсе поля, которые может получить клиент, должны быть описаны в **типе** (type).\n\nТипы `Query` и `Mutation` являются типами по умолчанию. В них должны быть описаны все возможные запросы.\n\n```Graphql\ntype Query {\n  currentUser: User\n}\n\ntype Mutation {\n  logOut\n}\n```\n```Graphql\nschema {\n  query: Query\n  mutation: Mutation\n}\n```\nМогут создаваться пользовательские типы.\n```Graphql\ntype User {\n  id: ID\n  username: String\n  role: String\n  image: String\n}\n```\n\nЕсли схема в запросе с клиента не удовлетворяет схеме на сервере, то возникнет валидационная ошибка. Например, поле `age` отсутствует в типе `User`.\n```json\n{\n  \"data\": null,\n  \"errors\": [\n    {\n      \"...\": \"...\",\n      \"message\": \"Validation error of type FieldUndefined: Field 'age' in type 'User' is undefined\n    }\n  ]\n}\n```\n\n### Динамические объекты в GraphQL\n\nGraphQL не позволяет создавать динамические объекты в качестве `type`.\n\nПримеры динамических объектов.\n```js\nconst commentsMap = {\n  123: {\n    id: \"123\",\n    message: \"Hello\"\n  },\n  456: {\n    id: \"456\",\n    message: \"Hi\"\n  }\n};\n```\n```js\nconst reportsInfoMap = {\n  spam: [\"id1\", \"id2\"],\n  flood: [],\n  bullying: [\"id3\"]\n}\n```\n```js\nconst flags = {\n  isReviewed: true,\n  isVisited: true,\n  /* ... */\n}\n```\n\nЕсть два решения, как это можно обойти\n* Передавать объекты текстовый как JSON и указывать встроенный тип `String` (не лучшая валидация) или подключить библиотеку, которая имеет скалярный тип JSON (например, [эту](https://github.com/taion/graphql-type-json)). Например, *AWS AppSync* имеет встроенный тип `AWSJSON`.\n```GraphQL\ntype Res {\n  commentsMap: String\n  reportsMap: JSON\n  flags: AWSJSON\n}\n```\n* Представить объекты в виде массивов (предпочтительный вариант).\n```GraphQL\n\ntype Comment {\n  id: ID\n  message: String\n}\n\ntype ReportsInfo {\n  name: String\n  values: [ID]\n}\n\ntype Flag {\n  name: String\n  value Boolean\n}\n\ntype Res {\n  commentsMap: [Comment]\n  reportsMap: [ReportsInfo]\n  flags: [Flag]\n}\n```\n\n\n## Взаимодействие с сервером\n\n### Отправка query\n```GraphQL\nquery Users {\n  users {\n    id\n    username\n    role\n  }\n}\n```\n\n### Query с переменными\n```GraphQL\nquery User($userId: ID!) {\n  user(userId: $userId) {\n    id\n    username\n    role\n  }\n}\n```\n`Variables`\n```json\n{\n  \"userId\": \"auth0|72d45e398924235638341891\"\n}\n```\n\n### Query с input\n```graphql\nquery QueryLeaders($credentialsInput: Credentials) {\n  login(credentials: $credentialsInput) {\n    auth_token\n  }\n}\n```\n\n`Variables`\n```json\n{\n  \"credentialsInput\": {\n    \"username\": \"admin\",\n    \"password\": \"admin\"\n  }\n}\n```\n\n\n\n## Преимущества GraphQL\n\n* *Строгая типизация*. Конкретная схема, полностью описывающая, как можно работать с данными.\n```gql\ntype User {\n  id: ID!\n  username: String!\n}\n\ntype Article {\n  id: ID!\n  title: String!\n  description: String\n  author: User!\n}\n\ntype Query {\n  articles: [Article]\n  article(id: ID!): Article\n}\n```\n* Клиент всегда запрашивает только то, что ему нужно. Он не может получить те поля, которые не запрашивал, ровно как и те, которые не предусмотрены описанной схемой.\n\nСледующий запрос вернёт `articles`, содержащие только поля `id` и `title`. Все эти поля должны присутствовать в схеме запроса на бэкенде (описана выше), иначе будет ошибка.\n```gql\nquery {\n  articles {\n    id\n    title\n  }\n}\n```\n* Один запрос может объединять в себе несколько различных ресурсов.\n\nСледующий запрос объединяет в себе информацию, собранную из `Articles` и `Users`.\n```gql\nquery {\n  articles {\n    id\n    title\n    description\n    author {\n      id\n      username\n    }\n  }\n}\n```\n\n# REST на практике\n\n## Данные в QUERY\n* Форма запроса на клиенте.\n```http\nGET /route?field=value&anotherField=anotherValue\n```\n```js\n/* axios */\naxios.get('/route', {\n  params: {\n    field: 'value',\n    anotherField: 'anotherValue',\n  },\n});\n```\n* Форма запроса на сервере.\n```http\nGET /route\n```\n* Получение данных из запроса на сервере.\n```js\n/* Express */\napp.get('/route', (request, response) => {\n  const { field, anotherField } = request.query;\n});\n```\n\nПередача массива `arr [1, 3, 7]` с клиента.\n```http\nGET /route?arr[]=1&arr[]=3&arr[]=7\n```\nПередача объекта `obj { foo: '1', bar: '7' }` с клиента.\n```http\nGET /route?obj[foo]=1&obj[bar]=7\n```\n\n## Данные в BODY\n* Форма запроса на клиенте.\n```http\nPOST /route\nContent-Type: application/json\n\n{ \"field\": \"value\", \"anotherField\": \"anotherValue\" }\n```\n```js\n/* axios */\naxios.post('/route', {\n  field: 'value',\n  anotherField: 'anotherValue',\n});\n```\n* Форма запроса на сервере.\n```http\nPOST /route\n```\n* Получение данных из запроса на сервере.\n```js\n/* Express */\napp.post('/route', (request, response) => {\n  const { field, anotherField } = request.body;\n});\n```\n\n## Данные в PARAMS\n* Форма запроса на клиенте.\n```http\nGET /route/paramValue\n```\n```js\n/* axios */\naxios.get(`/route/${paramValue}`);\n```\n* Форма запроса на сервере.\n```http\nGET /route/:param\n```\n\n* Получение данных из запроса на сервере.\n```js\n/* Express */\napp.post('/route/:param', (request, response) => {\n  const { param } = request.params;\n});\n```\n\nПример отправки нескольких параметров.\n```http\nPATCH /users/17/name\nPATCH /users/:id/:field\n```\n"
  },
  {
    "path": "HTML.md",
    "content": "- [Основы HTML](#основы-html)\n  - [Теги](#теги)\n  - [Типы HTML-тегов](#типы-html-тегов)\n  - [Фреймы и встроенные фреймы](#фреймы-и-встроенные-фреймы)\n  - [Doctype](#doctype)\n  - [Режимы браузеров](#режимы-браузеров)\n- [Шаблонизаторы](#шаблонизаторы)\n\n# Основы HTML\n\n## Теги\n\n**Тег**, **тэг** (tag) — именованная метка, дескриптор.\n\nHTML-страница состоит из элементов. Теги используются для указания типа этих элементов.\n\nБольшинство тегов — *парные*. Один — **открывающий** `<tagName>`, другой — **закрывающий** `</tagName>`. Между тегами распологается контент, который необходимо отобразить.\n```html\n<div></div>\n<p>Hello</p>\n```\n\nНекоторые элементы не могут содержать контент, поэтому их теги являются самозакрывающимися. `<tagName />`\n```html\n<img />\n```\n\nЭлементы могут содержать характерные им атрибуты в теге `<tagName attribute=\"value\">`.\n```html\n<div class=\"article\"></div>\n```\n\nОдни элементы могут являться частью других элементов, поэтому определённые тэги могут быть вложенными.\n```html\n<div class=\"note\">\n  <h1>Основы HTML</h1>\n  <h2>Теги</h2>\n  <p><b>Тег</b>, <b>тэг</b> (tag) — именованная метка, дескриптор.\n</div>\n```\n\nКомментарий имеет следующую структуру.\n```html\n<!-- comment -->\n```\n\n## Типы HTML-тегов\n\n### Парные и одиночные теги\n\n**Парные теги** имеют открывающий и закрывающий тег, перед именем закрывающиего ставится слэш `/`:  \n```html\n<div></div>\n<p>text</p>\n<span>inline</span>\n```\n\nОдиночные теги не имеют закрывающего тега:\n```html\n<img> <!-- картинка -->\n<input> <!-- поле ввода -->\n<br> <!-- перенос строки -->\n```\n\n### Блочные и строчные теги\n\nСоотвуствуют блочным и строчным элементами.  \nБлочные элементы могут хранить другие блочные элементы (`<p>` не может хранить блочные), строчные элементы и текст. (`<div>`, `<p>`)\n```html\n<div>\n  <div></div>\n</div>\n<p>\n  <span>text</span>\n</p>\n```\nСтрочные элементы обычно хранят только текст (текст, изображение), не могут содержать строчные и блочные элементы. (`<span>`, `<img>`)\n```html\n<span>text</span>\n<img src=\"/*...*/\">\n```\nБлочные теги создают разрыв строки, строчные — не создают.\n\n### Теги верхнего уровня\n\nТеги верхнего уровня отвечают за создание HTML-документа.\n```html\n<!-- начало документа -->\n<html>\n  <!-- голова, заголовок документа -->\n  <head></head>\n  <!-- тело документа -->\n  <body></body>\n<!-- конец документа -->\n</html>\n```\n\n**Теги заголовка документа** отвечают за настройки страницы (описание, подключение скриптов и стилей) и не видны пользователю (кроме `title`).  \n```html\n<link> <!-- подключение внешнего файла -->\n<title></title> <!-- название страницы -->\n<meta> <!-- метатеги, содежащие метаданные (данные о других данных) -->\n<style></style> <!-- включение CSS-кода в документ -->\n<script></script> <!-- JS-код или загруженный внешний JS-файл -->\n```\n\n**Тело документа** отвечает за содержательную часть документа, отображаемую в браузере.\n\nТеги `<html>` и `<body>` считаются необязательными в коде, но их желательно писать всегда.\n\n### Семантические теги\n\nПользователь видит глазами блоки страницы, но браузеры слепы.\n\n**Семантические теги** добавляют семантику (смысловое значение) содержимому страницы.  \nС помощью этих тегов барузерам проще индексировать страницу, отделить важный контент от дополнительной информации.  \n\n```html\n<nav></nav> <!-- навигация -->\n<section></section> <!-- группирует тематическое содержимое и обычно содержит заголовок -->\n<header></header> <!-- заголовок секции или страницы -->\n<main></main> <!-- основное содержимое страницы, которое должно быть уникальным для сайта -->\n<aside></aside> <!-- не является основной частью контента (удаление не влияет на понимание содержимого); обычно это боковая панель -->\n<footer></footer> <!-- нижний колонтитул секции или страницы -->\n<article></article> <!-- запись, публикация, статья -->\n<address></address> <!-- контактная информация автора документа или статьи -->\n<figure></figure> <!-- автономный контент (приложение), содержащий обычно краткие характеристики к фрагментам кода, картинкам, ... -->\n<blockquote></blockquote> <!-- цитата -->\n<mark></mark> <!-- важное содержимое (заметка), выделенное жёлтым цветом по умолчанию -->\n<time datetime=\"YYYY-MM-DDTHH:MM\"></time> <!-- время и дата по григорианскому календарю, T (time) в формате - разделяющий префикс -->\n```\n\nВсе семантические теги можно использовать неоднократно.  \n\n### Устаревшие тэги\n\n**Устаревшие тэги** (obsolete) были ранее запрещены и удалены или могут быть удалены из браузеров в любой моменты (хотя некоторые браузеры до сих пор могут их поддерживать).\n```html\n<marquee>text</marquee> <!-- бегущая строка -->\n<frame></frame> <!-- фрейм -->\n```\n\n## Фреймы и встроенные фреймы\n\n**Фреймы** разделяли окно браузера на отдельные, расположенные рядом области, в каждую из которых загружалась самостоятельная веб-страница. Они устарели и не поддерживаются в HTML5.\n\n**Встроенные (плавающие) фреймы** встраивают новый независимый HTML-документ в текущий.  \nШироко используются для вставок медиа (google-карты, аудио и видеоплееры, примеры кода), для аутентификации и платёжных транзакций, в большинстве остальных случаев лучше их не использовать.\n\nОсновным отличием фреймов и встроенных фреймов является то, что встроенные фреймы вставляются в страницу как есть, а в случае фреймов документ разбивает окно браузера на несколько маленьких панелей, в каждой из которых хранится отдельный документ.\n\n### Синтаксис фреймов (уже не поддерживается)\nУстаревший синтаксис фреймов:\n```html\n <frameset rows=\"25%,75%\">\n   <frame src=\"header.html\" name=\"HEADER\" scrolling=\"no\" noresize>\n   <frameset cols=\"100,*\">\n     <frame src=\"menu.html\" name=\"MENU\">\n     <frame src=\"content.html\" name=\"CONTENT\">\n   </frameset>\n </frameset>\n```\n\n### Синтаксис встроенных фреймов\nВставки видео из Youtube:\n```html\n<iframe\n  width=\"400px\"\n  height=\"400px\"\n  src=\"https://www.youtube.com/embed/M2kSJbLbIgQ\"\n  frameborder=\"0\"\n  allowfullscreen\n  style=\"border: none;\"\n></iframe> \n```\n\nВстроенный фрейм можно использовать как место, где должна открыться гиперссылка, по которой переходит пользователь:\n```html\n<iframe src=\"page.html\" name=\"page-frame\"></iframe>\n<a href=\"https://www.tutorialrepublic.com\" target=\"page-frame\">Click here</a>\n```\n\n### Преимущества и недостатки фреймов\n\n*Преимущества фреймов*:\n* Встраиваемая веб-страница не зависит от родительсокго веб-документа, что наделяет `<iframe>` некоторой безопастностью.\n* Быстро и просто изменяются размеры размещённых веб-страниц (как размеры обычного блока).\n* Удобно встраивать контент с других сайтов.  \n\n*Недостатки фреймов*:\n* Фреймы скрывают адрес страницы и показывать только адрес сайта (пользователю не понятно, где он находится; нельзя добавить страницу в закладки или поделиться ссылкой).\n\n## Doctype\n\nЭлемент `<!DOCTYPE>` предназначен для указания типа документа, он не является HTML-тегом и должен находиться в начале файла. \n\nСуществует несколько версий HTML и XHTML. Чтобы браузер понимал, как правильно интерпретировать документ, необходимо указать его тип.\n\n### DTD\n\n**Document Type Definition** (DTD, определение типа документа) — язык, использующийся для записи синтаксических правил SGML-подобных языков разметки (HTML, XML, SGML, GML). \n\nDTD определяет *структуру документа*, *список валидных элементов* и их *атрибутов*.\n\nОтрывок из DTD-файла для HTML 4.01\n```DTD\n<!--=================== Generic Attributes ===============================-->\n\n<!ENTITY % coreattrs\n \"id          ID             #IMPLIED  -- document-wide unique id --\n  class       CDATA          #IMPLIED  -- space-separated list of classes --\n  style       %StyleSheet;   #IMPLIED  -- associated style info --\n  title       %Text;         #IMPLIED  -- advisory title --\"\n  >\n\n<!ENTITY % i18n\n \"lang        %LanguageCode; #IMPLIED  -- language code --\n  dir         (ltr|rtl)      #IMPLIED  -- direction for weak/neutral text --\"\n  >\n\n<!ENTITY % events\n \"onclick     %Script;       #IMPLIED  -- a pointer button was clicked --\n  ondblclick  %Script;       #IMPLIED  -- a pointer button was double clicked--\n  onmousedown %Script;       #IMPLIED  -- a pointer button was pressed down --\n  onmouseup   %Script;       #IMPLIED  -- a pointer button was released --\n  onmouseover %Script;       #IMPLIED  -- a pointer was moved onto --\n  onmousemove %Script;       #IMPLIED  -- a pointer was moved within --\n  onmouseout  %Script;       #IMPLIED  -- a pointer was moved away --\n  onkeypress  %Script;       #IMPLIED  -- a key was pressed and released --\n  onkeydown   %Script;       #IMPLIED  -- a key was pressed down --\n  onkeyup     %Script;       #IMPLIED  -- a key was released --\"\n  >\n\n<!ENTITY % attrs \"%coreattrs; %i18n; %events;\">\n\n<!--================ Forms ===============================================-->\n<!ELEMENT FORM - - (%block;|SCRIPT)+ -(FORM) -- interactive form -->\n<!ATTLIST FORM\n  %attrs;                              -- %coreattrs, %i18n, %events --\n  action      %URI;          #REQUIRED -- server-side form handler --\n  method      (GET|POST)     GET       -- HTTP method used to submit the form--\n  enctype     %ContentType;  \"application/x-www-form-urlencoded\"\n  accept      %ContentTypes; #IMPLIED  -- list of MIME types for file upload --\n  name        CDATA          #IMPLIED  -- name of form for scripting --\n  onsubmit    %Script;       #IMPLIED  -- the form was submitted --\n  onreset     %Script;       #IMPLIED  -- the form was reset --\n  accept-charset %Charsets;  #IMPLIED  -- list of supported charsets --\n  >\n```\n\nHTML 4.01 базировался на SGML, поэтому его Doctype ссылался на DTD.\n\nРазработчики HTML5 отказались от использования DTD, поскольку посчитали его слишком ограниченным инструментом как для валидации, так и для описания всех возможностей HTML5. Например, в DTD невозможно правильно описать пользовательские атрибуты (`data-*`), поскольку в DTD каждый атрибут должен быть описан отдельно. Бесконечное количество вариантов — бесконечное описание. В итоге разработчики решили использовать свои валидаторы.\n\n### Схема Doctype\n```html\n<!DOCTYPE [Элемент верхнего уровня] [Публичность] \"[Регистрация]//[Организация]//[Тип] [Имя]//[Язык]\" \"[URL]\"\n```\n* *Элемент верхнего уровня*: в HTML — `<html>`, в SVG — `<svg>`.\n* *Публичность*: публичный ресурс — `PUBLIC`, системный локальный — `SYSTEM`. `HTML`, `XHTML` — всегда `PUBLIC`.\n* *Регистрация*: зарегистрирован ли разработчик текущего DTD в международной организации по стандартизации (ISO). `+` — да, `-` — нет. W3C не зарегистрирован.\n* *Уникальное название организации, разработавшей используемый DTD*.\n* *Тип описываемого документа*. `DTD`.\n* *Уникальное имя описания документа в DTD*.\n* *Язык описания*.\n* *URL документа формата DTD*.\n### Примеры Doctype\n* HTML 5. Воспринимает все типы документов, DTD не требуется. Рекомендуемый вариант.\n```html\n<!DOCTYPE html>\n```\n* HTML 4.01\n```html\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n```\n* XHTML 1.1\n```html\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n```\n* SVG\n```HTML\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n```\n* MathML\n```HTML\n<!DOCTYPE math PUBLIC \"-//W3C//DTD MathML 2.0//EN\" \"http://www.w3.org/Math/DTD/mathml2/mathml2.dtd\">\n```\n\n## Режимы браузеров\n\nНа данный момент существует *три режима браузеров* \n* *Режим совместимости* (Quirks mode).\n* *Режим стандартов* (Standards mode).\n* *Режим почти стандартов* (Almost standards mode).\n\n*Режим* может *оказывать влияние* на *разметку* (layout), *парсинг* и *валидацию* *HTML* и *CSS*, *DOM API*.\n\nВведение режимов позволило решить следующие проблемы:\n* устаревшие сайты могут ещё достаточно долго поддерживаться, не переписывая кодовую базу под новые стандарты\n* разработчики, знающие свои целевые стандарты, могут сами выбрать режим, изменяя Doctype.\n\nДля проверки текущего режима в браузерах имеется свойство `document.compatMode`, которое выдаёт `CSS1Compat`, если установлен режим стандартов или режим почти стандартов, и `BackCompat`, если установлен режим совместимости.\n\n### Режим совместимости\n\nВ случае отсутствия Doctype или невалидного Docktype все браузеры переходят в **режим совместимости**, **режим \"причуд\"** (Quirks Mode). Режим совместимости предназначен для отображения веб-страницы подобно старым браузерам (для которых html-страницы до введения Doctype). В этом случае каждый браузер заменяет современную функциональность своими заглушками и происходят различные причуды (quirks), варьирующиеся от \"ничего не происходит\" до \"всё падает с ошибками\".\n\nПример невалидного Doctype, переводящего браузер в *режим совместимости*.\n```html\n<!DOCTYPE html PUBLIC>\n```\nПример валидного Doctype, переводящего браузер в *режим совместимости* (слишком старая версия HTML).\n```html\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n```\n\n[*Пример* того, что может *происходить* в *режиме совместимости*](http://jkorpela.fi/quirks-mode.html).\n\n### Режим стандартов\n\nНаличие валидного Doctype последних версий выступает гарантией того, что современные браузеры обработают документ одинаково: в **режиме стандартов** (Standards mode). \n\nИспользование режима стандартов важно для поддержки современных возможностей браузеров по типу `<video>` и `<canvas>`.\n\nПример Doctype, переводящего браузер в *режим стандартов* с использованием нового браузерного валидатора.\n```html\n<!DOCTYPE html>\n```\n\nПример Doctype, переводящего браузер в *режим стандартов* с использованием старого валидатора, опирающегося на DTD-файл.\n```html\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n```\n\n### Режим почти стандартов\n\n**Режим почти стандартов** (Almost standards mode) очень похож на **режим стандартов**, но имеет один *quirk*, касающийся случаев, когда картинка является единственным контентом какого-то блока (например, клетки таблицы (table cells)). Такие картинки в режиме стандартов являются встроенными элементами (inline), что создаёт дополнительный странный отступ снизу, зарезервированный для специальных символов, которых нет у картинок. Этот отстут можно было убрать только сделав картинку блочным элементов. Это не понравилось некоторым создателям браузеров и они решили ввести *режим почти стандартов*, исправляющий этот баг.\n\nПример Doctype, переводящего браузер в *режим почти стандартов*\n```html\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n```\n\n# Шаблонизаторы\n\nHTML сам по себе не является полноценным языком программирования, поскольку в нём нет условных операторов, переменных циклов и других возможностей, характерных другим языкам. HTML-разметка является не более, чем простым текстом (строкой).\n\nВ наши дни веб-страницы являются достаточно динамичными, что достигается посредством изменения HTML на клиенте. Этой задачей занимается JavaScript, который работает с DOM (древовидным представлением HTML). Каждому HTML-элементу ставится в соответствие DOM-элемент. В DOM имеется множество методов, позволяющих искать, добавлять и удалять DOM-элементы, получать и изменять их атрибуты, стили, подписываться на события и делать многие другие вещи.\n\nЕсли на сервере появляется необходимость работать с HTML, то чаще всего подразумевается генерация HTML (строки) для передачи на клиент, который в дальнейшем будет манипулировать ей при помощи JavaScript. В таком случае нет необходимости в методах, которые предлагает DOM API. \n\nГенерируемый на сервере HTML обычно зависит от некоторых переменным. \n\nВ простейшем случае можно воспользоваться шаблонными строками, которые позволяют использовать переменные.\n```jsx\nconst generateHelloHtml = (name, styles = '') => `\n  <!DOCTYPE html>\n  <html>\n    <head>\n      <title>Hello</title>\n      <style>${styles}</style>\n    </head>\n    <body>\n      <p>Hello, ${name}</p>\n    </body>\n  </html>\n`;\n```\nТем не менее, такие \"строки\" могут сильно разрастаться: много переменных, условных операторов и строк кода в целом. Работать с ними становится затруднительно.\n\nНа помощь приходят **шаблонизаторы** (Template Engines). Они позволяют эффективно работать с HTML как со строкой, предоставляя возможности использования переменных, условий, циклов и многого другого. Достигается это посредством использования специального синтаксиса внутри строки, который распознаётся шаблонизатором и заменяется на обычные символы.\n\nСамыми популярными на данный момент шаблонизаторами являются [Handlebars](https://github.com/wycats/handlebars.js) и [Mystache](https://github.com/janl/mustache.js/)\n\nПример шаблона, написанного при помощи Hanblebars.\n```handlebars\n<!-- ./templates/hello.hbs -->\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Hello</title>\n    <style>{{styles}}</style>\n  </head>\n  <body>\n    {{!-- комментарий, который не попадёт в сгенерированный HTML --}}\n    <p>Hello, {{name}}</p>\n  </body>\n</html>\n```\nИспользование\n```js\n/* Node.js */\nconst fs = require('fs');\nconst handlebars = require('handlebars');\n\n/* считываем данные из файла, в котором хранится шаблон */\nconst source = fs.readFileSync('./templates/hello.hbs', 'utf-8');\n\n/* создаём шаблон */\nconst htmlTemplate = Handlebars.compile(source);\n\n/* заполняем шаблон данными */\nconst styles = 'p { color: \"red\" }';\n\nconst html = helloHtmlTemplate({\n  name: 'Notes',\n  styles,\n});\nconsole.log(html); // \"<!DOCTYPE html>...\"\n```\n"
  },
  {
    "path": "InterviewQuestions.md",
    "content": "# Бэкграунд, проверка общих знаний, фундамента\n\n## Математика\n- Что такое множество?\n- Что такое кортеж?\n- Что такое декартово произведение?\n\n## Алгоритмы и структуры данных\n- Какие структуры данных вы знаете? Что такое очередь, стэк, FIFO, LIFO?\n- Что такое хэш? Хэш-таблица?\n- Что такое граф?\n- Как вы понимаете понятие трудоёмкость или временная сложность алгоритма? Как она вычисляется? Какова трудоёмкость поиска элемента в массиве? В хэш-таблице?\n\n<!-- ## Комбинаторика\n\n## Криптография, шифрование, безопасность -->\n\n## Компьютерные сети\n- Что такое протокол? Какие протоколы вы знаете?\n- Расскажите про HTTP протокол. Что такое клиент, сервер, как они взаимодействуют?\n- Из чего состоит HTTP-запрос? \n- Какие методы HTTP вы знаете? Какие из них имеют BODY? Какие нет?\n- Чем отличаются GET и POST?\n- Чем отличаются PUT и PATCH?\n- Что такое идемпотентность? Какие методы считаются идемпотентными?\n- Что такое API?\n- Что такое REST API и зачем он нужен?\n- Какие группы статус-кодов вы знаете? Расскажите в двух словах про каждую из групп 1xx, 2xx, 3xx, 4xx, 5xx.\n- Чем отличаются REST и GraphQL?\n- Слышали ли вы что-нибудь про GRPC? Чем он отличается от REST и GraphQL?\n- Какие библиотеки и встроенные методы используются для обработки HTTP-запросов на стороне клиента? Какие на стороне сервера?\n- Что вы знаете об HTTPS? Об TCP/IP?\n- Чем отличаются протоколы TCP и UDP?\n- Что вам известно о доменной системе имён DNS? Как она работает на примере сайта `development.starling.com`?\n\n## Операционные системы\n- Чем отличается RAM и ROM память?\n- Что такое поток?\n- Что такое однопоточность и многопоточность? Какие у них плюсы и минусы?\n- Какие проблемы встречаются при многопоточность? Что вы знаете о состоянии гонки? Что происходит, когда два потока обращаются к одному ресурсу одновременно? Какие есть способы решения проблемы?\n\n## Разработка\n- Из каких этапов состоит разработка?\n- Какие специальности в IT-сфере вы знаете, за что они отвечают?\n\n## Программирование\n- Что такое польская нотация? Обратная польская нотация?\n- Что такое конкатенация строк?\n- Зачем нужны регулярные выражения? Как с ними работать?\n- Что такое указатель? Какой размер он имеет? Что можно делать с указателями?\n- Что такое синтаксис? Что такое семантика? Чем они отличаются?\n\n# Узконаправленные темы\n\n## Git\n- Что такое система контроля версий и зачем она нужна на проекте? Почему будет трудно писать код без неё?\n- Что такое коммит? Как сделать коммит?\n- Какие команды Git ты помнишь? Как переключиться на другую ветку?\n- Зачем нужен команда git stash?\n- Как работает Git? Что такое дельта изменений? Чем отличается Git от своих альтернатив?\n- Чем отличается git pull и git fetch?\n- Чем отличается git merge и git rebase?\n- Какие существуют стратегии merge в Git?\n- Что такое Git flow и как он работает? Что такое environment? Какие environments ты знаешь?\n\n## HTML\n- Какая сейчас версия HTML используется? Чем отличается от предыдущих версий?\n- С чего начинается HTML-документ? Что такое Doctype и зачем он нужен? Что содержит `dtd` файл?\n- Зачем нужны семантические теги? Какие семантические теги вы знаете? Почему нельзя просто всегда использовать `<div>`?\n- Из каких блоков состоит HTML?\n- Что размещается в блоке head?\n- Где стоит хранить css: в head или body? Аналогичный вопрос касается скриптов.\n- Как сделать так, чтобы скрипты загружались параллельно? Можно ли сделать так, чтобы один скрипт дожидался выполнения другого? Если да, то каким образом?\n- Какие типы HTML-элементов ты знаешь? Чем отличаются блочные и строчные элементы?\n\n## CSS\n- Как переводится CSS? Что такое каскад? Как работает алгоритм каскада, из каких этапов состоит?\n- Что такое специфичность? Как её вычислить? Покажите на примере.\n- Что такое наследование в CSS? Какие свойства наследуются, а какие нет? Имеет ли наследование специфичность?\n- Что такое CSS Box model.\n- Что такое z-index и как с ним работать? На каких элементах он работает?\n- Как зафиксировать элемент на экране?\n- Какими способами можно позиционировать элемент по центру?\n- Какая самая большая проблема в CSS? Есть ли область видимости в CSS? Что вы знаете о CSS Modules?\n- Что такое BEM? Зачем нужны методологии в CSS? Какие из них вы знаете?\n- Что такое SCSS/SASS/LESS? Что такое постпроцессоры и препроцессоры?\n- Какие типы вёрстки вы знаете? Чем отличается респонсив и адаптивная вёрстка? Посредством чего в CSS они реализуются?\n- Что такое viewport и viewport values\n- Как высчитывается em и rem? Чем они отличаются?\n\n\n## JavaScript\n- Как вы охарактеризуете язык JavaScript?\n- Охарактеризуйте типизацию языка JavaScript.\n- Расскажите про события в JavaScript? Как работает Event Loop? Что такое таски и микротаски? Чем они отличаются и какие вы из них знаете?\n- Что вам известно про область видимости (Scope), замыкание (Closure), Lexical environment?\n- Какие примитивные типы данных есть в JavaScript?\n- Как вы понимаете фразу: функция - это объект в JavaScript. Виды функций и их различия.\n- Какие парадигмы есть в JavaScript? Как использовать ООП, классы в JavaScript?\n- Что вы знаете о прототимном наследовании в JavaScript? Что такое прототип, скрытое свойство `__proto__`?\n- Что такое ключевое слово `this`? Где оно встречается в JavaScript?\n- Как можно явно привязать контекст? Чем отличаются методы `apply`, `call` и `bind`\n- Зачем нужен тип данных `Symbol`?\n- Какие методы массивов вы знаете? Какие из них изменяют исходный массив, а какие возвращают копию?\n- Что такое асинхронность в JavaScript?\n- Что такое функция обратного вызова (`callback`)? Какую роль эта функция выполняет?\n- За что отвечает встроенный объект `Promise`? Какие состояния есть у промисса? Как навешивать обработчики на `Promise`? Что такое `Promise Chaining`?\n- Что такое `async/await` и чем он принципиально отличается от `Promise`? Что возвращает функция с пометкой async?\n- Зачем нужен `eval` в JavaScript? Стоит ли его использовать?\n- Что такое строгий режим (`\"use strict\"`) в JavaScript? Зачем он нужен?\n\n## JavaScript DOM\n- Что такое DOM? В виде какой структуры данных он представляется?\n- Что такое Selector API в JavaScropt? Какие методы этого API вы знаете?\n- Производительно ли работать с DOM?\n- Зачем нужен jQuery? Что можно с ним делать?\n- Как подписаться на событие пользователя? Как работать с addEventListener? Как отписаться от события?\n- Что такое всплытие (hoisting) и погружение (capturing)? Как с ними работать? Зачем нужен третий параметр addEventListener?\n- Зачем нужны методы event.preventDefault и event.stopPropagation? \n\n## Browser\n- Что происходит, когда вы что-то вводите в поисковое окно\n- Что такое Critical Rendering Path и из чего он состоит\n- Что такое совместимость?\n- Что такое полифилл?\n- Какие инструменты есть в Chrome Dev Tools?\n- Чем отличаются LocalStorage от IndexedDB, от Session Storage?\n![image](https://user-images.githubusercontent.com/22237384/165200922-6b595629-0f35-4d06-9b0b-7a8fc5fe81e8.png)\n- Может ли браузер понимать SCSS, TypeScript? Что такое трайспайлер? Babel? \n\n## React\n- Что такое React? Для чего используется? За что отвечает?\n- Чем отличаются фреймворк и библиотека? React - фреймворк или библиотека?\n- Как вы понимаете component-based подход?\n- Какие принципы React вы можете выделить?\n- Что такое JSX? Можно ли написать React-приложение без JSX?\n- Может ли браузер понимать JSX? Если нет, то что нужно для того, чтобы превратить JSX в JS? Что вы знаете о транспайлерах, о Babel?\n- Что такое состояние? Какими способами можно управлять состоянием в React?\n- Чем отличаются функциональный и классовый компонент?\n- Что такое жизненный цикл и хуки жизненного цикла? Жизненный цикл классового компонента. Как вы понимаете слово mounting?\n- React Hooks. Что это такое, зачем нужны, как работают под капотом. Какие хуки вы знаете? Какие доводилось использовать и для чего?\n- Что такое мемоизация? Чем отличаются memo, useMemo, useCallback?\n- Что такое High Order Component (HOC)? С какими HOC-ами вам доводилось иметь дело?\n- Что такое композиция? Пример композиции в React. Сравнение композиции и наследования.\n- Чем отличается useState и this.setState?\n- Зачем нужно передавать callback в this.setState? Почему state в React обновляется асинхронно?\n- Что вы знаете о Context API?\n- Зачем нужны порталы в React?\n- Что вы знаете о Virtual DOM? Зачем он нужен? Как работает?\n- Что такое PropTypes и зачем они нужны?\n- Что и когда лучше: React Hooks + Context или Redux? Когда можно избежать использования Redux?\n- Можно ли использовать TypeScript и React вместе? Доводилось ли вам это делать?\n\n## Redux\n- Что такое состояние в приложении? Зачем нужны инструменты для управления состоянием?\n- Назовите три основных принципа Redux.\n- Жизненный цикл Redux.\n- Как подключить Redux Dev Tools?\n- Как использовать асинхронные функции в Redux? Что вы знаете о redux-thunk и redux-saga.\n- Что такое middleware?\n- Всегда ли нужно использовать Redux? Когда стоит его использовать и когда не стоит?\n\n## NodeJS\n- Что такое NodeJS? Охарактеризуйте его.\n- Как работает Event Loop в рамках NodeJS.\n\n## Базы данных\n- Чем отличается SQL и NoSQL?\n- Что такое отношение в SQL? Что такое реляционная база данных?\n- Какие типы связей между отношениями вы знаете в SQL?\n- Что такое нормализация базы данных? Чем оно отличается от денормализации? Какие преимущества и недостатки у нормализации и денормализации бд? Какие нормальные формы вы знаете ?\n- Что такое транзакция? Какие принципы транзакций вы знаете? Что вы знаете об принципах ACID? Как работать с транзакциями в микросервисной архитектуре? Что вы знаете о паттерне Saga?\n- Какие типы операций в SQL вы знаете?\n- Напишите SQL-запрос, который выбирает работников с зарплатой > 1000$, группирует их по заданному критерию, а затем сортирует их. Зарплата и работники хранятся в разных таблицах, которые нужно предварительно соединить.\n- Что вы знаете об агрегационных функциях SQL? Какие из них знаете? Когда их можно применять?\n- Какие блоки вы знаете у метода aggregate в MongoDB? Зачем нужна проекция?\n- Что такое популяция?\n- Что такое ORM? С какими ORM доводилось работать? Зачем использовать ORM?\n- Какие движки баз данных вы знаете?\n- Что такое индексы? Зачем их используют? В каком виде хранятся индексы?\n\n## TypeScript\n- Что такое статическая типизация? Чем она отличается от динамической?\n- Что такое компилятор? Зачем он нужен TypeScript?\n- Какие типы данных есть в TypeScript? Обязательно ли их указывать?\n- Чем отличается класс и интерфейс? Возможно ли множественное наследование в JavaScript/TypeScript?\n\n## Парадигмы программирования\n- Какие парадигмы программирования вы знаете?\n- Почему JavaScript можно назвать мультипарадигменным языком программирования?\n- Что такое ООП? Что такое объект и класс? Приведите примеры из реальной жизни.\n- Какие принципы ООП знаете? Что такое наследование?\n- Какие паттерны ООП знаете?\n- Что такое композиция в ООП? Чем отличаются композиция и наследование? Что лучше использовать и когда?\n- Что такое SOLID? Раскройте смысл каждой буквы, приведите примеры.\n- Что вы знаете о функциональном программировании?\n- Что такое каррирование?\n- Что такое мемоизация?\n- Чем отличаются ФП и ООП? Когда и что лучше использовать?\n\n## Тестирование\n- Что вы знаете о пирамиде тестирования? Из каких слоёв она состоит? Сколько и каких должно быть тестов на проекте? Приведите пример Unit-теста, интеграционного теста(API-теста), end-to-end теста.\n- Что такое подход TDD? Чем он отличается от BDD? С какими фреймворками для тестирования вам приходилось работать?\n- Какие паттерны тестирования вы знаете? Что такое загрушка?\n- Что такое регрессионное тестирование? Стоит ли его проводить и почему?\n- Как тестировать \"чёрный ящик\"?\n\n## Паттерны, архитектуры и принципы программирования\n\n"
  },
  {
    "path": "JavaScript.md",
    "content": "- [Основы JavaScript](#основы-javascript)\n  - [Типизация](#типизация)\n  - [Типы данных и переменные](#типы-данных-и-переменные)\n  - [Преобразование типов](#преобразование-типов)\n  - [Замыкание и область видимости](#замыкание-и-область-видимости)\n  - [Всплытие (hoising)](#всплытие-hoisting)\n- [Операторы](#операторы)\n  - [Оператор `typeof`](#оператор-typeof)\n  - [Оператор `void`](#оператор-void)\n  - [Оператор `запятая`](#оператор-запятая)\n  - [Оператор `delete`](#оператор-delete)\n  - [Оператор нулевого слияния `??`](#оператор-нулевого-слияния)\n  - [Оператор нулевого присваивания `??=`](#оператор-нулевого-присваивания)\n- [Объекты](#объекты)\n  - [Перечисление свойств объекта](#перечисление-свойств-объекта)\n  - [Является ли объектом](#является-ли-объектом)\n  - [Клонирование объектов](#клонирование-объектов)\n  - [Сравнение объектов](#сравнение-объектов)\n  - [Мутабельность](#мутабельность)\n  - [Отслеживание мутаций](#отслеживание-мутаций)\n  - [Иммутабельность](#иммутабельность)\n  - [Итерируемые объекты](#итерируемые-объекты)\n- [Массивы](#массивы)\n  - [Создание массива](#создание-массива)\n  - [Обращение к элементам массива](#обращение-к-элементам-массива)\n  - [Добавление и удаление элементов](#добавление-и-удаление-элементов)\n  - [Сортировка](#сортировка)\n  - [Псевдомассивы](#псевдомассивы) \n- [Функции](#функции)\n  - [Параметры и аргументы функций](#параметры-и-аргументы-функции)\n- [Встроенные объекты](#встроенные-объекты)\n  - [Error и его наследники](#error-и-его-наследники)\n  - [Promise](#promise)\n- [Спецификация](#спецификация)\n  - [Функции под капотом JavaScript](#функции-под-капотом-javascript)\n\n# Основы JavaScript\n- [Типизация](#типизация)\n  - [Статическая и динамическая](#статическая-и-динамическая)\n  - [Слабая и сильная](#слабая-и-сильная)\n  - [Явная и неявная](#явная-и-неявная)\n- [Типы данных и переменные](#типы-данных-и-переменные)\n- [Преобразование типов](#преобразование-типов)\n- [Замыкание и область видимости](#замыкание-и-область-видимости)\n- [Всплытие](#всплытие)\n\n## Типизация\n- [Статическая и динамическая](#статическая-и-динамическая)\n- [Слабая и сильная](#слабая-и-сильная)\n- [Явная и неявная](#явная-и-неявная)\n\n*JavaScript* является **типизированным** языком и имеет *динамическую*, *слабую*, *неявную типизацию*.\n\n### Статическая и динамическая\n\nПри **стратической типизации** *типы устанавливаются* на *этапе компиляции*. К *моменту выполнения программы* они уже *установлены* и компилятор знает, где какой тип находится.\n\nПример языков со *статической типизацией*: *Java*, *C#*.\n```java\n/* Java */\npublic class Notes {\n  public static void main(String []args){\n    int number = 1; // числовой тип\n    number = true; // error: incompatible types: boolean cannot be converted to int\n  }\n}\n```\n\nПри **динамической типизации** *типы определяются во время работы программы*.\n\nПример языков с *динамической типизацией*: *Python*, *JavaScript*.\n```js\n/* JavaScript */\nlet a; // тип неизвестен\na = 1; // числовой тип\na = true; // логический тип\n```\n\n### Слабая и сильная\n\nПри **слабой** (нестрогой) **типизации** *автоматически* выполняется множество *неявных преобразований типов* даже при условии *неоднозначности преобразования* или возможности *потери точности данных*.\n\nПример языка со *слабой типизацией*: *JavaScript*.\n```js\n/* JavaScript */\nconsole.log(1 + [] + {} + 'notes'); // \"1[object Object]notes\"\nconsole.log(1 - []); // 1\n```\n\nПри **сильной** (строгой) **типизации** в выражениях *не разрешено смешивать различные типы*. *Автоматическое неявное преобразование не производится*. \n\nПример языков с *сильной типизацией*: *Java*, *Python*.\n\nНапример, *нельзя сложить число* и *массив*.\n```java\n/* Java */\npublic class Notes {\n  public static void main(String []args){\n    int number = 17;\n    int array[] = new int[3];\n    System.out.println(number + array); // error: bad operand types for binary operator '+'\n  }\n}\n```\n\n### Явная и неявная\n\nПри **явной типизации** *тип* новых *переменных*, *функции*, их *аргументов* и *возвращаемых* ими *значений* нужно задавать *явно*. \n\nПример языков с *явной типизацией*: *C++*, *C#*.\n\n```cpp\n/* C++ */\nint sum(int a, int b) {\n    return a + b;\n}\n```\n\nПри **неявной типизации** *задание типов* производится *автоматически компиляторами* и *интерпретаторами*.\n\nПример языка с *неявной типизацией*: *JavaScript*.\n\n```js\nlet a; // неизвестно, какого типа будет значение переменной\nfunction fn (arg) { /* .. */ } // неизвестно, какого типа параметр функции и что она возвращает\n```\n\n## Типы данных и переменные\n\n**Переменная** состоит из *имени* и *выделенной* под это имя *области памяти*.\n\n**Имя переменной** может содержать *буквы, цифры, $, _*.  \n*Регистр* важен (`ALL` и `all` - разные переменные).  \n\n*Константы* принято называть в *UPPERCASE*: `ANY_NAME`.  \n\n### 6 примитивных типов и объект\n* **number** `1`, `2.17`, `NaN`, `Infinity`\n* **string** `'str'`, `\"str\"`\n* **boolean** `true`, `false`\n* **null** `null`\n* **undefined** `undefined`\n* **symbol** `Symbol(str)`\n* **object** `{}`\n\nЗначение **null** *не является* «*ссылкой на нулевой адрес/объект*» или чем-то подобным.  \nЗначение **null** *специальное* и имеет смысл «*ничего*» или «*значение неизвестно*».  \nЗначение **undefined** означает «*переменная не присвоена*».  \n\n**Символ** (Symbol) — *уникальный* и *неизменяемый* тип данных, используемый в качестве *идентификатора* для *свойства объекта*.\n```js\nSymbol('notes') === Symbol('notes'); // false\n```\n*Символы* являются *неперечисляемыми* (not enumerable), что делает их *недоступными* при *переборе свойств*.\n```js\nconst symbol = Symbol('notes');\n\nconst foo = { [symbol]: 'notes' };\nconsole.log(Object.keys(foo)); // []\nconsole.log(Object.getOwnPropertyNames(foo)); // []\n\nconsole.log(foo.notes); // undefined\nconsole.log(foo[Symbol('notes')]); // undefined (символы уникальны)\n// но\nconsole.log(foo[symbol]); // notes\n```\n\n### Способы объявить переменную\n\n**Блок** (Block Statement) — всё, что лежит внутри фигурных скобок `{}`.  \n\nНапример, *конструкции* `if-else`, `while`, `switch`, `try-catch`, *циклы* `for`, *функции содержат блоки*. Тем не менее *можно использовать блоки* и *без этих конструкций*.\n\n**Переменная let** имеет *блочную область видимости*, то есть её *нельзя использовать за пределами блока*, в котором она *объявлена*.\n```js\n{\n  let foo = 1;\n  foo = 7;\n  console.log(foo); // 7;\n}\nconsole.log(foo); // ReferenceError: foo is not defined\n```\n```js\nfor (let i = 0; i < 10; i++) {\n  /* ... */\n}\nconsole.log(i); // ReferenceError: i is not defined\n```\n*Переменную let* *нельзя использовать* до её *инициализации*.\n```js\nconsole.log(foo); // ReferenceError: Cannot access 'foo' before initialization\nlet foo = 'notes';\n```\n*Переменную let* *нельзя объявить дважды* с одним и *тем же именем*.\n```js\nlet foo = 1;\nlet foo = 7; // SyntaxError: Identifier 'foo' has already been declared\n```\n\n**Переменная const** имеет *те же свойства*, что и *переменная let*, но вдобавок *её значение нельзя переопределить*.\n```js\nconst foo = 1;\nfoo = 7; // TypeError: Assignment to constant variable.\n```\n\n**Переменная var** является *предком переменных let* и *const* и *не обладает их ограничениями*.\n\n*Переменная var* *не имеет блочной области видимости*.\n```js\nif (true) {\n  var foo = 1;\n}\nconsole.log(foo);\n```\n```js\nfor (var i = 0; i < 10; i++) {\n  /* ... */\n}\nconsole.log(i); // 10\n```\n*Переменную var* *можно использовать до* её *инициализации значением*. Такое поведение называется *всплытием*. *Всплывает только объявление* переменной, а её *временным значением* (до инициализации) становится `undefined`.\n```js\nconsole.log(foo); // undefined\nvar foo = 'notes';\nconsole.log(foo); // notes\n```\n*Переменную var* *можно объявить дважды* (redeclaration) с *тем же именем*.\n```js\nvar foo = 1;\nconsole.log(foo); // 1\nvar foo = 7;\nconsole.log(foo); // 7\n```\n*Переменная var*, находящаяяся *вне* каких-либо *функций*, размещается в *глобальном объекте*. Например, это может быть `window`.\n```js\nvar foo = 'notes';\nconsole.log(this.foo); // 'notes'\nconsole.log(window.foo); // 'notes' (если window является this)\nthis.foo = 'note';\nconsole.log(foo); // 'note'\n```\n\n**Глобальная переменная** *не является переменной* как таковой, а является *свойством глобального объекта* (в *JavaScript* им является `window`, в *NodeJS* — `global`). Поэтому, *в отличие* от *остальных переменных*, её *можно удалить оператором* `delete`.\n```js\nfoo = 1;\nwindow.foo = 7;\nconsole.log(foo); // 7\ndelete foo;\nconsole.log(foo); // ReferenceError: foo is not defined\n```\n\n## Преобразование типов\n\n*Бинарный оператор* `+` приводит значения либо к числам и совершает сложение, либо к строкам и совершает конкатенацию.\n\n*Виды преобразований*:\n* Строковое.\n* Числовое.\n* Логическое.\n\n### Явное и неявное преобразование\n\nЕсли *преобразование типов* происходит *автоматически*, то оно называется **неявным**. Этот тип преобразований *характерен JavaScript*. Обычно *такие преобразования* происходят при *выполнении операций* между *операндами разных типов*. \n```js\nconsole.log(1 + [] + {} + 'notes'); // \"1[object Object]notes\"\nconsole.log(1 - []); // 1\n```\n\nЕсли *преобразование типов* задаётся разработчиком *вручную* (явно), то оно называется **явным**. \n\nВ *JavaScript* есть множество *способов явно преобразовать тип*.\n```js\n/* приведение к числу */\nconsole.log(+'017.6'); // 17.6 (при помощи унарного оператора \"+\")\nconsole.log(Number('')); // 0 (при помощи Number())\nconsole.log(parseInt('11.1abc', 10)); // 11.1 (при помощи parseInt)\nconsole.log(parseFloat('11.1abc', 10)); // 11.1 (при помощи parseFloat)\n\n/* приведение к логическому значению при помощи Boolean() */\nconsole.log(Boolean('notes')); // true\nconsole.log(Boolean('')); // false\nconsole.log(Boolean(-1)); // true\n\n/* приведение к логическому значению при помощи String() */\nconsole.log(String(null)); // 'null'\nconsole.log(String({})); // '[object Object]'\n```\n\nПример *явного преобразования* в *Java*.\n```js\n/* Java */\nclass Notes {\n  public static void main(String[] args) {\n    double foo = 11.1;\n    int bar = (int)a; // приведение double к int\n    System.out.println(bar); // 11\n  }\n}\n```\n\n### Преобразование объекта к примитивному значению\n\nЕсли *объект* участвует в *операции*, подразумевающей использование *примитивного значения*, он *должен быть* *приведён*.\n\n*Каждый объект* имеет *метод* `valueOf()`, который *возвращает примитивное значение объекта*. *По умолчанию* метод *возвращает сам объект* (*непримитивное* значение).\n```js\nconst a = {};\nconsole.log(a.valueOf()); // {}\nconsole.log(a.valueOf() === a); // true\n\nconst b = [];\nconsole.log(b.valueOf()); // []\nconsole.log(b.valueOf() === b); // true\n\nfunction c() {}\nconsole.log(c.valueOf()); // ƒ c() {}\nconsole.log(c.valueOf() === c); // true\n```\nМетод `valueOf()` можно *переопределить*. Например, он переопределён у `Date`.\n```js\nconst a = { valueOf: () => 7 };\nconsole.log(a); // { valueOf: ƒ }\nconsole.log(a.valueOf()); // 7\n\nconst date = new Date();\nconsole.log(date.valueOf()); // 1573833066283\n```\n\nПомимо `valueOf()`, каждый объект имеет метод `toString()`, *возвращающий строковое представление объекта*.\n```js\nconst a = {};\nconsole.log(a.toString()); // \"[object Object]\"\n\nconsole.log([].toString()); // \"\"\nconsole.log([1, 2, 3].toString()); // \"1,2,3\"\n\nconst date = new Date();\nconsole.log(date.toString()); // \"Sun Nov 17 2139 19:13:50 GMT+0300 (Moscow Standard Time)\"\n\nfunction fn() { /* ... */ }\nconsole.log(fn.toString()); // \"function fn() { /* ... */ }\"\n```\nМетод `toString()` также можно *переопределить*.\n```js\nconst a = {};\na.toString = () => 'a{}';\nconsole.log(a.toString()); // 'a{}'\n```\n\n*Объект* приводится к *примитивному значению* при помощи функции `toPrimitive(argument, preferredType)`, работающей по следующему *алгоритму*.\n* Если `value` — *примитивное* значение (`number`, `string`, `boolean`, `null`, `undefined`), то *вернуть* его.\n* Иначе *вызвать* `value.valueOf()`. Если *результат* — *примитивное* значение, то *вернуть* его.\n* Иначе *вызвать* `value.toString()`. Если *результат* — *примитивное* значение, то *вернуть* его.\n* Выбросить *исключение* `TypeError('Cannot convert object to primitive value')`.\n\nПо умолчанию параметр `preferredType` имеет занчение `'number'`. Если передать `'string'`, то шаги алгоритма с `valueOf()` и `toString()` *меняются местами*.\n\n```js\nconst isPrimitive = argument => !['object', 'function'].includes(typeof argument) || argument === null;\n\nconst toPrimitive = (argument, preferredType = 'number') => {\n  if (isPrimitive(argument)) {\n    return argument;\n  }\n  \n  if (preferredType === 'number') {\n    /* сперва valueOf(), затем toString() */\n    if (argument.valueOf && isPrimitive(argument.valueOf())) {\n      return argument.valueOf();\n    }\n\n    if (argument.toString && isPrimitive(argument.toString())) {\n      return argument.toString();\n    }\n  } else if (preferredType === 'string') {\n    /* сперва toString(), затем valueOf() */\n    if (argument.toString && isPrimitive(argument.toString())) {\n      return argument.toString();\n    }\n    \n    if (argument.valueOf && isPrimitive(argument.valueOf())) {\n      return argument.valueOf();\n    }\n  }\n  \n  throw new TypeError('Cannot convert object to primitive value');\n};\n```\n### Преобразование к строке\nПреобразование значения к типу `string` производится функцией `ToString(argument)` по следующим правилам.\n* Если значение `argument` имеет тип `string`, то вернуть значение.\n* Иначе, если `argument` имеет тип `Symbol`, выбросить `TypeError`.\n* Иначе, если `argument` имеет примитивный тип `number`, `boolean`, `undefined`, `null`, обернуть его в строку и вернуть: `\"null\"`, `\"undefined\"`, `\"1.2\"`, `\"NaN\"`, `\"true\"`, `\"false\"`.\n* Иначе, если `argument` имеет тип `Object`, вернуть результат `ToString(ToPrimitive(argument))`.\n```js\nconst ToString = (argument) => {\n  if (typeof argument === 'string') {\n    return argument;\n  }\n\n  if (typeof argument === 'symbol') {\n    throw new TypeError('Cannot convert a Symbol value to a string');\n  }\n  \n  const allowedPrimitives = ['number', 'boolean', 'undefined'];\n  if (allowedPrimitives.includes(typeof argument) || argument === null) {\n    return `${argument}`;\n  }\n\n  if (!isPrimitive(argument)) {\n    return ToNumber(ToPrimitive(argument));\n  }\n\n  throw new TypeError('Cannot convert argument to string');\n}\n```\n\n### Преобразование к логическому значению\nПреобразование значения к типу `boolean` производится функцией `ToBoolean(argument)` по следующим правилам.\n* Если `argument` имеет тип `boolean`, то *вернуть значение*.\n* Иначе, если `argument` равно `undefined`, `null`, `0`, `NaN`, `\"\"` (пустая строка), то *вернуть* `false`.\n* В *остальных* случаях (`Object`, `Symbol`, *числа кроме* `0` и *непустые строки*) *вернуть* `true`.\n```js\nconst ToBoolean = (argument) => {\n  if (typeof argument === 'boolean') {\n    return argument;\n  }\n\n  if ([undefined, null, 0, NaN, ''].includes(argument)) {\n    return false;\n  }\n  \n  return true;\n}\n```\n\n### Преобразование к числу\nПреобразование значения к типу `number` производится функцией `ToNumber(argument)` по следующим правилам.\n* Если значение `argument` имеет тип `number`, то вернуть значение.\n* Иначе, если `argument` имеет тип `boolean`, *вернуть* `1` (`true`) или `0` (`false`).\n* Иначе, если `argument` имеет тип `string`, попытаться *преобразовать строку* к числу или вернуть `NaN` в случае неудачи. *Пустая строка* приводится к *нулю*.\n* Иначе, если `argument` имеет тип `Symbol`, выбросить `TypeError`.\n* Иначе, если `argument` равно `undefined`, *вернуть* `NaN`.\n* Иначе, если `argument` равно `null`, *вернуть* `0`.\n* Иначе, если `argument` имеет тип `Object`, вернуть результат `ToNumber(ToPrimitive(argument))`.\n```js\nconst ToNumber = (argument) => {\n  if (typeof argument === 'number') {\n    return argument;\n  }\n\n  if (typeof argument === 'boolean') {\n    return argument ? 1 : 0;\n  }\n  \n  if (typeof argument === 'string') {\n    return argument === '' ? 0 : parseFloat(argument, 10);\n  }\n  \n  if (typeof argument === 'symbol') {\n    throw new TypeError('Cannot convert a Symbol value to a number');\n  }\n  \n  if (argument === undefined) {\n    return NaN;\n  }\n  \n  if (argument === null) {\n    return 0;\n  }\n\n  if (!isPrimitive(argument)) {\n    return ToNumber(ToPrimitive(argument));\n  }\n  \n  throw new TypeError('Cannot convert argument to number');\n}\n```\n\nОператор *нестрогого равенства* `==` производит *неявное преобразование типа* к *числу* (если оба операнда не являются строками).\n\nИнтересный пример.\n```js\n[] == ![] // true\n// оператор ! имеет больший приоритет, чем ==, поэтому он вызовется раньше\n// ![] --> !toBoolean([]) --> !true --> false --> 0\n[] == 0\n// toPrimitive([]) --> [].valueOf() ~ [] (не подходит) --> [].toString() ~ '' --> '' --> 0\n0 == 0 // true\n```\n\n\n## Область видимости, лексическое окружение, замыкание\n\n<!-- Постараюсь широко расписать эту важную тему, поэтому начнём с истоков.\n### Програмный код\n**Программный код** (англ. `code`) - это обычный текст, написанной с использованием некоторой кодировки (например, в Unicode). Кодировка представляет собой таблицу (алфавит) символов, каждому из которых ставится в соответствие некоторое десятичное число, а десятичное чисто всегда можно представить в виде последовательности нулей и единичек (или последовательности битов). И если каждый символ в коде - это последовательность нулей и единичек, а значит весь код - это тоже последовательность нулей и единиц.\n\nТаким образом человек, знающий язык программирования (можно провести аналогию с разговорным языком), видит код как что-то вполне осмысленное, а компьютер - не видит: для него это всего навсего некоторый клочок неведомой ему информации, хранящейся на жёстом диске (то есть в физической памяти компьютера).\n\n### Движок, интерпретатор, компилятор\nЧтобы код мог как-то повлиять на компьютер, его должна запустить специальная программа, которая понимает, что в этом коде написано и может перевести это на понятный компьютеру яык. Такой программой выступает интерпретатор или компилятор, являющийся частью движка языка. У них немного разный принцип работы, но суть та же:\n1) Проверить, правильно ли написан код (для этого проводится синтаксический разбор).\n2) По ходу синтаксического разбора код разбивается на команды (получается что-то вроде рецепта или алгоритма - чёткой последовательности операций, которые должны быть совершены компьютером: например, создай такую-то область памяти такого-то размера, размести в этой области данные, затем выведи на экран, поменяй на другие данные, запиши в файл и так далее).\n3) Понятно, что если код написан неверно (с ошибками), то компьютер не может правильно его интерпретировать - ведь он не может думать, а лишь действует по алгоритму, заложенному в реализации движка, - поэтому компьютер выдаёт синтаксическую ошибку.\n\n**Язык программирования** (англ. `programming language`) - набор *синтаксических правил*, который позволяет *интерпретатору распознавать код*. Языки программирования эволюционируют, поэтому здесь очень важно версионирование: в каждую новую версию языка заложены новые синтаксические правила, новый алгоритм интерпретации кода.\n\nЧто такое переменная? Переменная - это именнованная область памяти для хранения данных (информации). \nПеременная состоит из имени и данных, которые в ней хранятся (если таковые имеются). Имя служит чем-то вроде псевдонима, который связывается отношением один к одному с некоторой ссылкой, представляющей собой физический адрес\n\nИнтерпретатор проходится построчно по коду, создаёт переменные (выделяется некоторая память для хранения данных, создаётся ссылка, ссылка связывается с данными и именем переменной, данные могут меняться по ходу выполнения программы). -->\n\n<!-- . Сам по себе он мало что значит. Важно то, что делает движок. Интерпретатор проходится построчно по коду, интерпретирует текст и превращает его в команды для компьютера (например, поместить в память компьютера данную информацию, вывести её на экран, записать её в файл и так далее). -->\n\n<!-- Будем считать, что переменная недоступна, если недоступно её значение (то есть её значением является `undefined`, либо ошибка -->\n\nВ *любой момент выполнения кода* некоторая *переменная либо доступна*, *либо не доступна*.\n\n<!-- Простой пример в переменной `let`: нельзя использовать переменную `let` до её объявления в коде, то есть переменная недоступна в любой момент до объявления кода.\n```js\nconsole.log(foo); // Reference Error\nlet foo = 17;\n``` -->\n\n\n*Видимость* (`visibility`), *доступность* (`accessibility`) *переменных* отражает понятие **область видимости**, **скоуп** (англ. `scope`). \n\n<!-- Каждый скрипт, каждый модуль имеет некоторую свою область видимости. -->\n\nСуществует *два типа области видимости*: *глобальная*  и *локальная*.\n\n**Глобальная область видимости** (англ. `Global Scope`) - это область видимости всей программы (всего скрипта). В браузере глобальная область видимости представлена объектом `window`. На NodeJS-сервере глобальная область видимости представлена объектом `global`. \n\n**Локальная область видимости** (англ. `Local Scope`) - это область видимости любой функции, объявленной в скрипте. Каждая функция при своём вызове создаёт локальную область видимости. *Переменные*, *определённые внутри функции*, *недоступны извне*.\n\nСтоит избегать явного использования глобальной области видимости, если это возможно, и стараться использовать только локальную область видимости. В идеале следует писать код так, чтобы внешняя область видимости не содержала тех переменных, которые характерны какой-то определённой внутренней области видимости. С одной стороны, это отвечает принципу инкапсуляции: скрываются детали реализации (выставляется наружу только то, что необходимо). С другой стороны, это отвечает принципу модульности: в коде появляются самодостаточные блоки (модули, фичи, пакет, компоненты - их называют по-разному), которые хранят всё нужное внутри себя, не имеют внешних зависимостей и, таким образом, могут быть довольно легко экспортированы в другое место.\n\n*Область видимости определяется* в *момент вызова функции*.\n\n<!-- *Каждая функция автоматически создаёт новую локальную область видимости*.   -->\n\n```js\nconst fn = () => {\n  var foo = 1;\n}\nconsole.log(foo); // ReferenceError: foo is not defined\nfn();\nconsole.log(foo); // ReferenceError: foo is not defined\n```\n\n<!-- Если переменная не объявлена в одной функции, то она принадлежит глобальной области видимости.  \nВ браузере за глобальную область видимости отвечает объект `Window`. -->\n\n**Замыкание** (англ. `closure`) — это функция вместе с ссылками на её *окружение*, называемое **лексическим окружением** (Lexical Environment). По сути говоря, любая функция в JavaScript представляет собой замыкание.\n\n<!-- *Замыкание* даёт *доступ* к *области видимости внешней функции* из *внутренней функции*.   \n\n*Замыкания* также *создаются автоматически* при *создании функции*. -->\n\n### Примеры\n\nДля начала изолируем каждый интересующий нас случай, а затем посмотрим, как они работают все вместе.\n\n#### Переменная var\nОбъявление переменной `var` *всплывает* в самое начало скрипта, что позволяет использовать имя этой переменной до её объявления. Тем не менее, значение переменной `var` изменяется в области видимости лишь при инициализации.\n```js\n/* main.js */\n\n// >>> Global Scope: { a: undefined }\nvar a = 5;\n// >>> Global Scope: { a: 5 }\n```\n\n#### Переменная const\n\n#### Переменная let\n\n#### Блок кода {}\n\n<!-- Самый короткий и эффективный пример для демонстрации того, как работают замыкания:\n```js\n/* Запуск скрипта */\n// > Global Scope: {}\n/* В глобальной области видимости к началу выполнения скрипта уже может существовать\nмножество различных переменных, но мы их не будем рассматривать, чтоб не запутаться,\nда они и не важны сейчас */\n\nlet firstName; /* создаём переменную без инициализации */\n// > Global Scope: { firstName: undefined }\n\nlet lastName = \"Starling\"; /* создаём и инициализируем переменную строкой \"Starling\" */\n// > Global Scope: { firstName: undefined, lastName: \"Starling\" }\n\nfunction print(repo) { /* создаём функцию (Function Declaration) */\n  repo = \"Notes\";\n  console.log(`${firstName}-${lastName}/${repo}`);\n}\n// > Global Scope: { firstName: \"\", lastName: undefined, print: ƒ print() }\n\nfirstName = \"Max\";\n// > Global Scope: { firstName: \"Max\", print: ƒ print() }\n\nprint(); // Starling\n/* перед выполнением функции:\n      > Global Scope: { firstName: \"Starling\", print: ƒ print() }\n   к началу выполнения выполнения функции:\n      > Global Scope: { firstName: \"Starling\", print: ƒ print() }\n        > print() Scope: { bar: \"Max\" }\n   после инициализации переменной baz:\n      > Global Scope: { firstName: \"Starling\", print: ƒ print() }\n        > print() Scope: { bar: \"Starling\", baz: \"Notes\" }\n   по окончанию выполнения функции:\n      > Global Scope: { firstName: \"Starling\", print: ƒ print() }\n*/\n\nfirstName = \"Notes\"\n``` -->\n\n## Всплытие (hoisting)\n\nВ *JavaScript* *переменная* может быть *использована перед* тем, как она была *определена* (declared) в коде.\n\n**Всплытие** (англ. `hoising`) — *поведение* JavaScript, *размещающее объявления* (англ. `declarations`) *вверху текущей области видимости* (англ. `current scope`).\n\n```js\nfoo = 3;\nconsole.log(foo); // 3\nvar foo;\n```\n\nПри попытке использования *необъявленной переменной выдаётся ошибка*.\n```js\nconsole.log(bar); // ReferenceError: bar is not defined\n```\n\n*Всплывают* (англ. `hoist`) только сами *объявления* (англ. `declarations`), но *не* присвоенные им *значения* (англ. `initializations`).  \nЭто связано с тем, что *переменная создаётся* в *области видимости* на *первом этапе интерпретации*, а *инициализируется значением* на *втором этапе*.\n\n```js\nconsole.log(foo); // undefined\nvar foo = 3;\nconsole.log(bar); // undefined\nbar = 'notes';\n```\n\n*Всплывают переменные* `var` и *глобальные переменные*, а `let` и `const` *не всплывают*: *выдаётся ошибка*.\n\n```js\nconsole.log(bar); // ReferenceError: Cannot access 'bar' before initialization\nlet bar = 3;\n```\n\n## Контекст, контекст выполнения и ключевое слово `this`\n\nПрежде, чем приступать к определению контекста в рамках JavaScript, давайте разберёмся, что же такое контекст в широком смысле этого слова.\n\n### О контексте в литературе\n**Контекстом** (англ. `context`) называют *совокупность фактов* и *обстоятельств*, в *окружении* которых *происходит* некоторое *событие*, *существует* некоторое *явление* или некоторый *объект*.\n\n<!-- Рассмотрим несколько примеров тоже, что можно описывать (то есть того, о чём можно далее создать контекст). -->\nНапример, можно сфокусироваться на одной из следующих тем ниже и начать описывать их.\n1) Пример события. Например, *исторические события* - Битва под Оршей, принятие Билля о правах.\n2) Пример объекта. Например, *историческая личность* - Эммелин Панкхёрст и *исторически значимое место* - Собор Парижской Богоматери. \n3) Пример явления. Например, *северное сияние*.\nСбор информации на любую тему выше равносилен наполнению контекста, соответствующего этой теме.\n\nЧем больше мы узнаём о чём-то, тем точнее мы можем воспроизводить это. \nЧем больше рассказчик даёт вам деталей, тем детальнее вы представляете то, что он видел своими глазами.\n\nНапример, рассмотрим следующий пример описания человека.\n> Джек Лондон — это американский писатель, который отбрёл известность благодаря своим приключенческим рассказам и романам. Тем, кому довелось знать его лично, описывали его как мужественного, отважного, целенаправленного и решительного человека...\nКак видно, в первом предложении указано имя человека. И во втором предложение нам уже ясно, что местоимение \"его\" ссылается на Джека Лондона.\n\nЕсли бы компьютер мог выделять контекст из написанного, то он бы сделал это примерно следующим образом:\n```js\nconst he = {\n  name: 'Джек Лондон',\n  sex: 'мужчина',\n  profession: 'писатель',\n  genres: ['приключение', 'роман'],\n  traits: ['мужественный', 'отважный', 'целенаправленный', 'решительный'],\n}\n```\nС каждым новым предложением этот контекст бы продолжил наполняться новыми деталями.\n\n#### Вырывание из контекста\n\nЕсли убрать первое сообшение из примера выше, то тогда не понятно, о ком идёт речь. В таком случае говорят о нарушении целостности контекста или вырывании из контекста.\n> Тем, кому довелось знать его лично, описывали его как мужественного, отважного, целенаправленного и решительного человека...\n\n#### Ещё пример\n\nНапример, контекстом текущего документа `Notes/JavaScript.md` является язык JavaScript и всё, что с ним тесно связано. И в то же время любой другой язык программирования (скажем, Java) находится вне рассматриваемого контекста. Ещё пример: в контексте данной главы рассматриваются понятия \"контекст\", ключевое слово `this` и не рассматриваются типы данных.\n\n### Контекст выполнения в JavaScript\n\nЕсли вернуться к JavaScript, то под \"контекстом\" обычно подразумевают **контекст выполнения** (англ. `Execution Context`, `EC`). \n\nВсего можно выделить два контекста выполнения:\n1) **Контекст выполнения функции** (англ. `Function Execution Context`, `FEC`).\n2) **Глобальный контекст выполнения** (англ. `Global Execution Context`, `GEC`).\n\n### Глобальный контекст выполнения\n\n\n# Операторы\n- [Оператор typeof](#оператор-typeof)\n- [Оператор void](#оператор-void)\n- [Operator запятая](#оператор-запятая)\n- [Operator delete](#оператор-delete)\n\n## Оператор typeof\n\n**Оператор typeof** возвращает *тип аргумента*.  \n*Результатом* действия *оператора* является *строка*.  \n\nВ JavaScript *массивы* и *функции* так же являются *объектами*, но оператор `typeof` имеет *тип* `\"function\"` для *удобства*.  \n ```javascript\ntypeof undefined // \"undefined\"\ntypeof 0 // \"number\"\ntypeof true // \"boolean\"\ntypeof \"foo\" // \"string\"\ntypeof Symbol(\"foo\"); // \"symbol\" \ntypeof {} // \"object\"\ntypeof [] // \"object\"\ntypeof null // \"object\" (врождённая ошибка языка)\ntypeof function(){} // \"function\"\n```\nОператор `typeof` cчитает `null` *объектом*, что является *врождённой ошибкой* JavaScript, которую *не исправляют* в целях *поддержки совместимости* с предыдущими версиями.  \nТем не менее, `null` *не является объектом* как таковым: это *примитивное значение*.\n```js\nconsole.log(typeof null); // \"object\"\nconsole.log(null instanceof Object); // false\n```\nОператор `typeof` считает, что `NaN` (Not-a-Number) является `\"number\"`. Это объясняется тем, что `NaN` появляется только при операциях с числами, а также содержится в `Number.NaN` (как и метод `Number.isNaN(value)`).\n```js\nconsole.log(typeof NaN); // \"number\"\n```\n\n### Оператор typeof и undefined\n\nРаньше в JavaScript `undefined` являлся *названием глобальной переменной*, по умолчанию *не имеющей значения*. То есть *переменная* `undefined` имела *примитивное значение* `undefined`, но его можно было *переопределить*.\n```js\nvar foo = {};\nconsole.log(foo.prop === undefined); // true (нет такого свойства)\nundefined = 17;\nconsole.log(foo.prop === undefined); // false\n```\nИз-за *изменяемости* (mutability) `undefined` *не использовали явно*, а получали *другим способом*.\n\nНапример, `typeof foo.prop === 'undefined'`.\n\nСейчас такой *ошибки нет*.\n\n## Оператор void\n\n**Оператор void** — *унарный* оператор, *выполнящий принимаемое выражение* и *возвращающий undefined*.  \nЕго можно использовать *со скобками и без*:\n```javascript\nvoid 3 // undefined\nvoid(3) // undefined\nvoid(3 == '3') // undefined\nvoid 3 == '3';   // undefined == '3' --> false\n```\n*Преобразование Function Declaration* в *Function Expression* для *самовызывающихся* функций (IIFE):\n```js\n(function() { /* ... */ })()\n// эквивалентно\nvoid function(){ /* ... */ }()\n// дважды SyntaxError (название функции и круглые скобки), если\nfunction(){ /* ... */ }()\n```\n*Избегание явного* использования *undefined*, а также *краткий способ* его записать (иногда можно встретить в *минифицированном* коде):\n```js\nif (field === void 0)\n```\nИногда нужно просто *выполнить функцию*, *ничего не возвращая*, но *стрелочная* функция в своей *краткой форме всегда возвращает результат выражения*, что может иногда приводить к *неожиданным последствиям*.  \nМожно себя *обезопасить*:\n```js\nconst onClick = () => void this.setState({ isClicked: true });\n```\nЗдесь стоит обратить внимание, что код ниже выдаст ошибку: приоритет `=>` ниже, чем у `void`.\n```js\nconst onClick = void () => this.setState({ isClicked: true }); // SyntaxError: Malformed arrow function parameter list\n```\n[Приоритеты операторов.](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Operator_Precedence)\n\n## Оператор запятая\n\n**Оператор запятая** (comma operator) *выполняет каждый из его операндов слева направо* и *возвращает значение последнего*. *Операнды* могут быть представлены *выражениями*.\n\n*Оператор запятая* имеет *самый низкий приоритет среди операторов*, что может стать *причиной ошибок* при неправильном использовании.\n```js\nlet foo = 2, 3; // SyntaxError: Unexpected number\n```\nОшибка выше связана с тем, что *оператор присваивания* `=` *выполняется раньше*, чем *оператор запятая*, поскольку имеет *более высокий приоритет*. Впереди стоит `let`, применяющийся *ко всем операндам оператора запятая*: `let foo = 2` и `let 3` (*название переменной не может быть числом*). \n\n*Избежать ошибки* можно при помощи *оператора группировки* `( )`, имеющего *самый высокий приоритет среди операторов*.\n```js\nlet bar = (2, 3);\nconsole.log(bar); // 3 (последний операнд)\n```\nК слову, пример ниже отработает без ошибок. В первом операнде `foo = 2` происходит присвоение значения глобальной переменной, во втором просто возвращается `3`.\n```js\nfoo = 2, 3;\nconsole.log(foo); // 2\n```\n\nНе так часто удаётся *применить оператор запятая*, но иногда он может быть *полезен*.  \nНапример, можно *временно добавить* в стрелочную функцию *логирование*, если нужно что-то быстро посмотреть.\n```js\nconst getDataType = data => typeof data;\n// заменяем на\nconst getDataType = data => (console.log(data), typeof data);\n\ngetDataType('notes') // можно увидеть значение 'notes' в консоли\n```\nДругой пример: *выполнить операцию* над чем-то и сразу *вернуть* её *результат*.\n```js\nconst array = ['n', 'o', 't', 'e'];\nconsole.log(array.push('s')) // 5 (вернулась длина массива после добавления элемента)\n\n// хотим вернуть новый массив:\nconst array = ['n', 'o', 't', 'e'];\nconst push = (arr, val) => (arr.push(val), arr);\nconsole.log(push(array, 's')); // ['n', 'o', 't', 'e', 's']\n```\nЗдесь стоит ещё раз отметить *важность оператора группировки*.\n```js\nconst push = (arr, val) => arr.push(val), arr; // SyntaxError: Missing initializer in const declaration\n```\nКод выше воспринимается интерпретатором как `const push = /* ... */` и `const arr` (*константы обязаны иметь какое-то значение при создании*). С `let` ошибки бы не было.\n\n## Оператор delete\n\n**Оператор delete** — *унарный* оператор, *удаляющий свойство* из объекта (массива, функции и других наследников `Object`). \n\nПри *успешном удалении* `delete` возвращает `true` (в том числе, если удаляется несуществующее свойство), `false` иначе.\n\n```js\nconst foo = { a: 1, b: 7 };\nconsole.log(foo); // { a: 1, b: 7 }\ndelete foo.a; // true\nconsole.log(foo); // { b: 7 }\n```\nПри работе с *массивами*, `delete` создаёт *дыры* в них.\n```js\nconst bar = [1, 2, 3];\ndelete bar[0]; // true\nconsole.log(bar); // [empty, 2, 3]\nconsole.log(bar.length) // 3\ndelete bar[2]; // true\nconsole.log(bar); // [empty, 2, empty]\nconsole.log(bar.length) // 3\n```\n*Оператор delete* *может удалить глобальную переменную*, поскольку на самом деле она является *свойством глобального объекта* `window`.\n```js\nfoo = 'notes';\nconsole.log(window.foo); // 'notes'\ndelete foo; // true\nconsole.log(window.foo); // undefined\n```\n\n*Оператор delete* *не может удалять переменные* `var, let, const` и *функции*. \n```js\nvar foo = 'notes';\ndelete foo; // false\nconsole.log(foo); // 'notes'\n\nfunction bar () {}\ndelete bar; // false\nconsole.log(bar); // ƒ bar () {}\n```\n\nОператор `delete` *не связан* с *очисткой памяти*. *Очиста памяти* осуществляется *сборщиком мусора* при *разрыве ссылок*.\n\n## Оператор нулевого слияния `??` \n\n**Оператор нулевого слияния** `??` (англ. `Nullish coalescing operator`) — логический оператор, возвращающий значение правого операнда, если значение левого операнда содержит `null` или `undefined`, иначе возвращается значение левого операнда.\n\n```typescript\n// right operator\n(null ?? true) === true\n(undefined ?? true) === true\n\n// everything else - left operand\n('' ?? true) === ''\n(0 ?? true) === 0\n(false ?? true) === false\n(NaN ?? true) // NaN\n('hi' ?? true) === 'hi'\n(-1 ?? true) === -1\n([] ?? true) // []\n({} ?? true) // {}\n...\n```\n\n## Оператор нулевого присваивания `??=`\n\n**Истинноподобные значения** (англ. `truthy values`) - значения, эквивалентные `true` при их приведении к логическому типу (явному `Boolean(x)` и `!!x` и неявному `if (x)`, `x &&`, `x ||`):\n* `true`\n* `17`, `-17`, `1.7`, `17n` (любые ненулевые числа)\n* `Infinity`, `-Infinity` (бесконечности)\n* `' '`, `\"0\"`, `'hi'` (непустые строки)\n* `new Boolean(false)`, `{}`, `[]`, `function foo(){}` (любые объекты)\n\nИнтересный пример\n```js\nnew Boolean(false) === true // false\nnew Boolean(false) === false // false\nnew Boolean(false) == true // false\nnew Boolean(false) == false // true\n\n// Объяснение: при сравнении берётся `.valueOf()` объекта класса `Boolean`\n(new Boolean(false)).valueOf() // false \n\n// ещё примеры с неявным использованием `.valueOf()`:\n(new Number(0)) == false // 0 == false\n(new String('')) == false // '' == false\n```\nЕщё один интересный пример\n```js\n(new String('')) && 0 // 0\n// берётся `valueOf`: ''\n// приводится к `Boolean`: false\n// ложноподобное значение пропускается\n// берётся следующее значение: 0\n```\n\n**Ложноподобные значения** (англ. `falsy values`) - значения, эквивалентные `false `при их приведении к логическому типу (явному `Boolean(x)` и `!!x` и неявному `if (x)`, `x &&`, `x ||`):\n* `false`\n* `0` (ноль), `-0` (отрицательный ноль), `0n` (BigInt ноль)\n* `''`, `\"\"`,  (пустая строка)\n* `null`\n* `undefined`\n* `NaN`\n  \n```ts\nBoolean(false) === false\nBoolean(0) === false\nBoolean('') === Boolean('') === Boolean(``) === false\nBoolean(null) === false\nBoolean(undefined) === false\nBoolean(NaN) === false\n```\n\n**Значения, похожие на `null`** (англ. `nullish values`) - это `null` и `undefined`.\n\n**Оператор нулевого присваивания** `??=` (англ. `Nullish coalescing assignment`, `Logical nullish assignment`) — логический оператор, присваивающий правый операнд к левому только если левый операнд равняется `null` или `undefined`.\n\n```js\nlet x = null;\nx ??= 'foo'\nx ??= 'bar'\nconsole.log(x) // 'foo'\n\nlet y; // undefined\ny ??= 0\ny ??= 1\nconsole.log(y) // 0\n\nlet z; // undefined\nz ??= undefined\nz ??= null\nz ??= false\nz ??= true\nconsole.log(z) // false\n```\n## Оператор присваивания логического \"И\"\n\nОператор присваивание логического И\n\n\n\n# Объекты\n- [Перечисление свойств объекта](#перечисление-свойств-объекта)\n- [Является ли объектом](#является-ли-объектом)\n- [Клонирование объектов](#клонирование-объектов)\n  - [Плохие способы клонирования](#плохие-способы-клонирования)\n  - [Неглубокое клонирование](#неглубокое-клонирование)\n  - [Глубокое клонирование](#глубокое-клонирование)\n- [Сравнение объектов](#сравнение-объектов)\n  - [Операторы сравнения](#операторы-сравнения)\n  - [Неглубокое сравнение](#неглубокое-сравнение)\n  - [Глубокое сравнение](#глубокое-сравнение)\n- [Отслеживание мутаций](#отслеживание-мутаций)\n- [Иммутабельность](#иммутабельность)\n- [Итерируемые объекты](#итерируемые-объекты)\n\n## Перечисление свойств объекта\n\nЦикл `for...in` *перебирает все несимвольные* (non-Symbol) *перечисляемые свойства* (enumerable properties) объекта, включая свойства из цепочки прототипов (prototype chain).\n\nМетод `Object.keys(obj)` *возвращает массив* названий всех *собственных* (own) *перечисляемых свойств* объекта `obj` в том же *порядке*, в котором они обходились бы *циклом* `for..in`. Поскольку свойства собственные, цепочка прототипов не включается в перечисление.\n\nМетод `Object.getOwnPropertyNames(obj)` *возвращает массив* названий всех собственных свойств объекта `obj`.\n\n## Является ли объектом\n```js\nconst isObject = value => typeof value === 'object' && !Array.isArray(value) && value !== null;\n```\nТакая реализация обусловлена следующим поведением [оператора typeof](#оператор-typeof).\n```js\ntypeof({}) === 'object' // true\ntypeof([]) === 'object' // true\ntypeof(() => {}) === 'function' // true\ntypeof(null) === 'object' // true\n```\nМожно проще.\n```js\n({}) instanceof Object // true\n```\n\n## Клонирование объектов\nВ JavaScript *объекты* и *массивы* (тоже являющиеся объектами) *передаются по ссылке* (by reference). \n\nСуществует множество способов *клонировать объект* (object clone), среди которых есть плохие и хорошие.\n\n### Плохие способы клонирования\n\n**Клонирование через оператор присваивания** `=` означает *запись ссылки на объект* в *новую переменную*. Если *изменить* эту *переменную* (не заменить полностью, а изменить поля), то *изменится* и *оригинальный* объект.\n```js\nconst obj = { a: 7 };\nconst copy = obj; // передача ссылки\nconsole.log(foo === copy); // true\ncopy.a = 3;\nconsole.log(foo.a) // 3;\n```\nСледует *избегать поведения*, при котором *изменение копии влияет на оригинальный объект*.\n\n**Клонирование через Object.create()** не имеет смысла, поскольку `Object.create(proto)` создаёт новый объект, используя существующий объект `proto` в качество прототипа для нового.\n```js\nconst obj = { a: 7 };\nconst copy = Object.create(obj);\nconsole.log(copy); // {}\nconsole.log(copy.a); // 7 (не найдено в самом объекте, но найдено в прототипе)\nconsole.log(copy.__proto__); // { a: 7 }\nobj.hasOwnProperty('a'); // true\ncopy.hasOwnProperty('a'); // false\n```\n\n**Клонирование в цикле for..in** означает копирование не только собственных (own) свойств объекта, но и свойств прототипа. Само свойство, отвечающее за прототип, не копируется.\n```js\nconst cloneObject = (obj) => {\n  const copy = {};\n  for (let key in obj) {\n    copy[key] = obj[key];\n  }\n  return copy;\n};\n\nconst prototype = { prop: 'prototype property' };\nconst obj = { field: 'value' };\nobj.__proto__ = prototype; // так делать не желательно, но для примера можно\nconsole.log(obj); // { field: 'value' }\nconsole.log(obj.prop); // 'prototype property'\n\nconst copy = cloneObject(obj);\nconsole.log(copy); // { field: \"value\", prop: \"prototype property\" }\n```\n\n**Клонирование через eval** является самым худшим вариантом. \n```js\nconst cloneObject = obj => eval(uneval(obj));\n\nconst obj = { /* ... */ };\nconst copy = cloneObject(obj);\n```\n\nВо-первых, использование `eval` - это плохо.\n> Eval is not evil. Using eval poorly is. \n\nВо-вторых, требуется поддержки функции `uneval`, имеющаяся только у Firefox, но даже в нём это может не сработать из-за политики безопастности контента (Content Security Policy): `EvalError: call to eval() blocked by CSP`.\n\n### Неглубокое клонирование\n\n**Неглубокое клонирование** (shallow clone) подразумевает копирование неглубоких свойств (shallow properties) оригинального объекта в новый объект. Если свойство само является объектом (`prop: {}`), то оно передаётся по ссылке (оригинальное и скопированное свойство ссылаются на один объект). \n\n*Неглубокое свойство*  `obj.prop`, *глубокое свойство*: `obj.prop.nestedProp`. Глубокие свойства (deep properties) объекта клонируются автоматически, поскольку содержатся в объектах, являющихся неглубокими свойствами.\n\nЕсли вложенные объекты отсутствуют, неглубокое клонирование является оптимальным.\n\n**Клонирование через Object.assign()**.\n```js\nconst cloneObject = obj => Object.assign({}, obj);\n\nconst obj = {\n  field: {\n    nestedField: 'notes',\n  },\n};\nconst copy = cloneObject(obj);\nconsole.log(obj === copy); // false\ncopy.field.nestedField = 'changed';\nconsole.log(obj.field.nestedField); // 'changed' (изменение клона повлияло на оригинал)\nconsole.log(obj.field === copy.field); // true (ссылаются на один объект)\n```\n\n**Клонирование через Spread-оператор** `...` работает аналогично `Object.assign()`.\n```js\nconst cloneObject = obj => ({ ...obj });\n```\n\n**Клонирование при помощи Object.keys()** подразумевает перебор и копирование собственных свойств оригинального объекта.\n```js\nconst cloneObject = (obj) => {\n  const copy = {};\n  Object.keys(obj).forEach((key) => {\n    copy[key] = obj[key];\n  });\n  return copy;\n};\n```\nВ случае, если `Object.assign` и `...` не поддерживаются, можно написать полифилл с использованием `Object.keys`.\n\nАналогичного `Object.keys` поведения можно добиться от *клонирования в цикле for..in*, добавив в нём дополнительную проверку на принадлежность свойства.\n```js\nfor (let key in obj) {\n if (obj.hasOwnProperty(key)) { /* ... */ }\n}\n```\n\nГотовым решением неглубокого копирования является функция `_.clone(value)` из библиотеки `lodash`.\n\n### Глубокое клонирование\n**Глубокое клонирование** (deep clone) подразумевает копирование свойств на всех уровнях, то есть на каждом уровне вложенности вместо передаче по ссылке создаётся новый объект с теми же свойствами.\n\n**Клонирование через JSON-сериализацию** очень популярно благодаря простоте, скорости работы (JSON-сериализация реализована и оптимизирована браузером) и возможности глубокого клонирования. \n```js\nconst cloneObject = obj => JSON.parse(JSON.stringify(obj));\n\nconst obj = {\n  field: {\n    nestedField: 'notes',\n  },\n};\nconst copy = cloneObject(obj);\nconsole.log(obj === copy); // false\ncopy.field.nestedField = 'changed';\nconsole.log(obj.field.nestedField); // 'notes' (изменение клона не повлияло на оригинал)\nconsole.log(obj.field === copy.field); // false\n```\nНедостаток: утрата некоторых данных (data loss), а точнее тех данных, которые не поддерживаются в JSON.\n```js\nconst cloneObject = obj => JSON.parse(JSON.stringify(obj));\n\nconst copy = cloneObject({\n  a: () => {}, // поле опускается\n  b: Infinity, // значение заменяется на null\n  c: NaN, // значение заменяется на null\n  d: new Date(), // превратится в строку\n  e: undefined, // поле опускается\n  f: Symbol(''), // поле опускается\n});\nconsole.log(copy); // { b: null, c: null, d: \"XXXX-XX-XXTXX:XX:XX.XXXZ\" }\n```\nБолее того, некоторые данные вообще не могут быть преобразованы в JSON. Например, *циклическая ссылка* (англ. `circular reference`) или `BigInt` вызовут исключение (ошибку), которое нужно будет где-то обработать.\n```js\n// циклическая ссылка\nlet foo = {};\nfoo.foo = foo;\nJSON.stringify(foo); // TypeError: Converting circular structure to JSON\n    --> starting at object with constructor 'Object'\n    --- property 'foo' closes the circle\n```\n![image](https://user-images.githubusercontent.com/22237384/160062353-ebe0e4a2-9817-44fa-8c0b-18edd07e234e.png)\n\n```js\nJSON.stringify({ a: BigInt(124) }); // TypeError: Do not know how to serialize a BigInt\n```\n\n**Клонирование через V8-сериализацию** в Node.js (экспериментальная функциональность).\n```js\nconst v8 = require('v8');\nconst clone = obj => v8.deserialize(v8.serialize(obj));\n```\n\nПример глубокого клонирования конкретного объекта без всяких функций.\n```js\nconst user = {\n  email: 'user@email.com',\n  settings: { theme: 'dark' },\n  comments: ['Hi!', 'Agree'].\n};\n\nconst clone = {\n  ...user,\n  settings: { ...user.settings },\n  comments: [...user.comments],\n};\n```\nТакое поведение можно было бы реализовать рекурсивной функцией `cloneObject`. Например,\n```js\nconst isObject = value => typeof value === 'object' && !Array.isArray(value) && value !== null;\n\nconst cloneObject = (obj) => {\n  let copy = {};\n  for (let prop in obj) {\n    if (obj.hasOwnProperty(prop)) {\n      const value = obj[prop];\n      /* если значение является объектом, рекурсивно копируем его свойства */\n      copy[prop] = isObject(value) ? cloneObject(value) : value;\n    }\n  }\n  return copy;\n}\n\nconst foo = { g: { h: 'h' } };\ncondt bar = cloneObject(foo); // { g: { h: 'h' } }\nconsole.log(foo === bar); // false\nconsole.log(foo.g === bar.g); // false\n```\nЭта функция не может обработать все случаи. Например, отдельно следует описывать работу с массивами и функциями, а также с циклическими ссылками, выбрасывающими исключения `«too much recursion»` и подобные.\n```js\nconst copy = {};\ncopy.proto = copy; // циклическая ссылка\nconsole.log(copy); // { proto: {...} }\nconsole.log(copy.proto); // { proto: {...} }\nconsole.log(copy.proto.proto.proto); // { proto: {...} }\n```\n\nТаким образом, для глубокого клонирования лучше всего использовать готовые решения. Такими являются  `_.cloneDeep(obj)` в библиотеке `lodash`, `jQuery.extend(true, {}, obj)`, `angular.clone(obj)` и другие.\n\n## Сравнение объектов\n\n### Операторы сравнения\n\nВ JavaScript есть два оператора сравнения: нестрогий (abstract) `==` и строгий (strict) `===`.\n\nПри сравнении объектов `A` и `B` оба оператора вернут `true` лишь в том случае, если ссылки `A` и `B` будут указывать на один и тот же объект.\n```js\nconst a = {};\nconst b = {};\nconst c = a;\nconsole.log(a == b, a === b); // false false\nconsole.log(a == c, a === c); // true true\n```\n\n### Неглубокое сравнение\n\n**Неглубокое сравнение** (shallow comparison) объектов A и B подразумевает проверку на строгое равенство (`===`) *только неглубоких свойств* (shallow properties) объектов (*проверка не рекурсивна*). Если все неглубокие свойства совпадают, то объекты считаются эквивалентными (shallow equal). Если `A === B`, то A и B по определению считаются эквивалентными, поскольку ссылаются на один объект.\n\n*Неглубокое свойство*  `obj.prop`, *глубокое свойство*: `obj.prop.nestedProp`.\n\nПримеры *неглубокого сравнения*.\n* `{ a: 1 }` и `{ a: 1 }` считаются эквивалентными, поскольку их неглубокие свойства `a` совпадают (`1 === 1`).\n* `{ a: {}}` и `{ a: {}}` считаются не эквивалентными, поскольку их неглубокие свойства `a` представленны объектами с разными ссылками (`{} !== {}`).\n\nРеализация *неглубокого сравнения* для *любых значений*.\n```js\nconst isObject = value => typeof value === 'object' && value !== null;\n\nconst compareObjects = (A, B) => {\n  const keysA = Object.keys(A);\n  const keysB = Object.keys(B);\n \n  /* Если количество свойств не совпадает, то объекты не эквивалентны. */\n  if (keysA.length !== keysB.length) {\n    return false;\n  }\n \n  /* Рассматриваются свойства объекта A в объекте B. Если объект B не имеет хотя бы одно \n  собственное (own) свойство или значения свойств не строго равны, то объекты не эквивалентны. */\n  return !keysA.some(key => !B.hasOwnProperty(key) || A[key] !== B[key]);\n};\n\nconst shallowEqual = (A, B) => {\n  /* Если значения A и B проходят строгое равенство, то они эквивалентны. */\n  if (A === B) {\n    return true;\n  }\n \n  /* Если оба значения равны NaN, то они эквивалентны. */\n  if ([A, B].every(Number.isNaN)) {\n    return true;\n  }\n  \n  /* Eсли A и/или B не являются объектами, то они не эквивалентны,  \n  поскольку не прошли проверки выше. */\n  if (![A, B].every(isObject)) {\n    return false;\n  }\n  \n  /* Остался случай, когда A и B — объекты */\n  return compareObjects(A, B);\n};\n\nconst a = { field: 1 };\nconst b = { field: 2 };\nconst c = { field: { field: 1 } };\nconst d = { field: { field: 1 } };\n\nconsole.log(shallowEqual(1, 1)); // true\nconsole.log(shallowEqual(1, 2)); // false\nconsole.log(shallowEqual(null, null)); // true\nconsole.log(shallowEqual(NaN, NaN)); // true\nconsole.log(shallowEqual([], [])); // true\nconsole.log(shallowEqual([1], [2])); // false\nconsole.log(shallowEqual({}, {})); // true\nconsole.log(shallowEqual({}, a)); // false\nconsole.log(shallowEqual(a, b)); // false\nconsole.log(shallowEqual(a, c)); // false\nconsole.log(shallowEqual(c, d)); // false\n```\n\nПрименение неглубокого сравнения в React, чтобы сделать `PureComponent`.\n```js\nimport shallowCompare from 'react-addons-shallow-compare'; \n\nclass Foo extends Component {\n  shouldComponentUpdate(nextProps, nextState) {\n    return shallowCompare(this, nextProps, nextState);\n  }\n  render() { /* ... */ }\n}\n```\nНеглубокого сравнение применяется в Redux: `shallowEqual(oldState, newState)`, чтобы выяснить, изменился ли *State*. \nИменно поэтому очень важно *не мутировать State*: измененяются и новый, и старый State одновременно — неглубокое сравнение не видит различий между ними.\n\n### Глубокое сравнение\n\n**Глубокое сравнение** (deep comparison) объектов A и B подразумевает рекурсивный обход и сравнение всех свойств (в том числе и глубоких) объектов A и B.\n\nОдним из способов провести глубокое сравнения является JSON-сериализация (сравниваются получившиеся строки). Это достаточно быстрый способ.\n```js\nconst deepEqual = (A, B) => JSON.stringify(A) === JSON.stringify(B);\n\nconst c = { field: { field: 1 } };\nconst d = { field: { field: 1 } };\nconst e = { field: { field: 2 } };\nconsole.log(deepEqual(c, d)); // true\nconsole.log(deepEqual(c, e)); // false\n```\nЕго большим недостатком является то, что порядок свойств в сравниваемых объектах имеет значение.\n```js\nconst a = { f1: '1', f2: '2' };\nconst b = { f2: '2', f1: '1' };\ndeepEqual(a, b); // false\n```\n\nДругих встроенных решений не существует.  \nМожно переписать функцию `shallowEqual`, сделав её рекурсивной, или подключить готовые функции из сторонних библиотек.\nВ Node.js есть встроенная функция `assert.deepEqual()`, которая также представлена в виде отдельного модуля `deep-equal`.\n\nГлубокое сравнение работает медленнее, чем неглубокое.  \nНе стоит его использовать, если в этом нет необходимости.\n\n## Мутабельность\n\n**Мутабельность объекта** — его способность изменяться после создания, **мутация** (mutation) — соответствующее изменение.\n\nОбъекты в JavaScript передаются по ссылке, поэтому по умолчанию являются мутабельными.\n```js\nconst foo = {\n  a: 'value',\n};\n\nfoo.a = 'new value'; // мутация\nfoo.b = 17; // мутация\n```\nПеременная `const` разрешает мутацию объекта, поскольку хранит лишь ссылку на объект, которая не меняется (остаётся константой).\n\n## Отслеживание мутаций\n\n### Object.observe()\nДля отслеживания мутаций раньше был доступен метод `Object.observe()`. Сейчас он запрещен (deprecated), поскольку были добавлены другие, более эффективные способы отслеживать мутации.\n```js\nObject.observe(obj, callback);\n```\n* `obj` — объект, изменения которого должны отслеживаться.\n* `callback` — функция обратного вызова, принимающая массив объектов, описывающих изменения.\n\nМетод `Object.observe()` работает *асинхронно*. Он возвращет массив объектов со всеми изменениями. Объекты в массиве расположены в том же порядке, в котором происходили изменения при выполнении скрипта.\n```js\nconst foo = {\n  a: 17,\n  b: 'notes',\n};\n\nconst callback = changes => console.log(changes);\nObject.observe(foo, callback);\n\nfoo.c = 'mutations'; // добавление\nfoo.a = 7; // изменение\ndelete foo.b; // удаление\n\n/* changes: [{\n  name: 'c',\n  object: <foo>,\n  type: 'add',\n}, {\n  name: 'a',\n  object: <foo>,\n  type: 'update',\n  oldValue: 17,\n}, {\n  name: 'b',\n  object: <foo>,\n  type: 'delete',\n  oldValue: 'notes',\n}] */\n```\n\n### MutationObserver\n\n**MutationObserver** — встроенный объект, отслеживающий изменения DOM-элементов. \n```js\nconst observer = new MutationObserver(callback); // инициализация\nobserver.observe(element, observerOptions); // подписка на изменения DOM-элемента\n```\n* `callback` — функция обратного вызова, принимающая список объектов, описывающих изменения.\n* `element` — DOM-элемент, изменения которого должны отслеживаться.\n* `observerOptions` — объект с параметрами, определяющими, какие изменения должны отслеживаться.\n\nБолее развернётый пример.\n```js\nconst callback = (mutationList) => {\n  for (let mutation of mutationList) {\n    if (mutation.type === 'childList') {\n      console.log('Дочерний элемент добавлен или удален');\n      /* mutation: { type, addedNodes, deletedNodes } */\n    } else if (mutation.type === 'attributes') {\n      console.log(`Атрибут ${mutation.attributeName} был изменён`);\n      /* mutation: { type, target, attributeName, oldValue } */\n    }\n  }\n};\n\nconst observer = new MutationObserver(callback);\n\nconst el = document.querySelector('.elem');\nconst observerOptions = {\n  childList: true,\n  attributes: true,\n  subtree: true, // false - только родительская вершина, true - родительская и дочерние\n};\n\nobserver.observe(el, observerOptions);\n```\n\n### Proxy\n**Proxy** (прокси) — встроенный объект, позволяющий не только отлавливать любое совершаемое над объектом действие, но и влиять на его исход.\n\nПри помощи `Proxy` можно временно откладывать совершение действия, производить валидацю, отмену дейсвтия, устанавливать значение по умолчанию (если устанавливаемое значение невалидно), логгирование и многое другое.\n```js\nconst proxy = new Proxy(target, handler);\n```\n* `target` — **проксируемый объект**.\n* `handler`— объект с **методами-ловушками** (traps), каждый из которых отвечает за определённый тип действий над объектом.\n\n*Основные методы-ловушки*\n* `set` — запись свойства (внутренний метод `[[Set]]`).\n* `get` — чтение свойства (внутренний метод `[[Get]]`).\n* `deleteProperty` — удаление свойства (внутренний метод `[[Delete]]`).\n* `has` — проверка наличия свойства при помощи `in` (внутренний метод `[[HasProperty]]`).\n* `construct` — создание через `new` (внутренний метод `[[Construct]]`).\n* `apply` — вызов функции (внутренний метод `[[Call]]`).\n* `getOwnPropertyDescriptor` — переборы через `Object.keys`, `Object.values`, `Object.entries`, `for..in` и `Object.getOwnPropertyDescriptor` (внутренний метод `[[GetOwnProperty]]`).\n\nJavaScript накладывает условия на использование некоторых ловушек. Например, методы `set` и `delete` должны возвращать `true`, если изменения вступили в силу, и `false` — иначе.\n\nПример валидации перед установкой свойства проксируемому объекту (ловушка `set`). Проверяется, что передаваемое значение также является объектом.\n```js\nconst storage = {};\n\nconst proxy = new Proxy(storage, {\n  set(target, property, value) {\n    if (value instanceof Object) {\n      target[property] = value;\n      return true;\n    }\n    return false;\n  },\n});\n\nproxy.a = 17;\nconsole.log(proxy.a); // undefined\nconsole.log(storage); // {}\n\nproxy.b = { name: 'Alen' };\nconsole.log(proxy.b); // { name: \"Alen\" }\nconsole.log(storage); // { b: { name: \"Alen\" } }\n```\n\nПример логирования проксируемой функции при её вызове (ловушка `apply`).\n```js\nconst increment = a => a + 1;\n\nconst proxy = new Proxy(increment, {\n  apply(target, thisArg, args) {\n    console.log(`Incrementing the value \"${args[0]}\"`);\n    const result = target(...args);\n    console.log(`Result: \"${result}\"`);\n    return result;\n  },\n});\n\nincrement(5); \n/* ничего не выводится */\n\nproxy(5);\n/* Incrementing the value \"5\".\nResult: \"6\" */\n```\nНа примере выше заметно, что прямое взаимодействие с проксируемым объектом не имеет никакого эффекта — нужно всегда использовать созданный `Proxy` вместо него.\n\n### Reflect\n\n**Reflect** — встроенный JavaScript-объект, предоставляющий методы для всех действий, которые перехватывает `Proxy` (для каждой ловушки).\n\n*Reflect* не является функциональным объектом, поэтому его нельзя вызвать как функцию или использовать в качестве конструктора. Все его методы статические.\n* `Reflect.get(target, property)` эквивалетно `target[property]`.\n* `Reflect.set(target, property, value)` эквивалетно `target[property] = value`.\nи так далее.\n\nПример создания экземпляра класса при помощи `Reflect.construct`.\n```js\nclass Animal {\n  constructor(kind, sex, age) {\n    this.kind = kind;\n    this.sex = sex;\n    this.age = age;\n  }\n}\n\nconst elephant = Reflect.construct(Animal, ['elephant', 'male', 7]);\nconsole.log(elephant);\n/* Animal { kind: \"elephant\", sex: \"male\", age: 7 } */\n```\n\nПример с установкой значения по умолчанию при помощи `Proxy` и `Reflect.get`.\n```js\nconst guest = { type: 'guest' }; // пользователь по умолчанию\n\nconst userTable = {\n  tom: { type: 'user', username: 'Tom' },\n  max: { type: 'user', username: 'Max' },\n  frank: { type: 'user', username: 'Frank' }\n};\n\nconst proxy = new Proxy(userTable, {\n  get(target, property) {\n    if (property in target) {\n      return Reflect.get(target, property); // эквивалетно target[property];\n    } else {\n      return guest;\n    }\n  },\n});\n\nconsole.log(proxy['garry']); // { type: \"guest\" }\nconsole.log(proxy['max']); // { type: \"user\", username: \"Max\" }\n```\n\n## Иммутабельность\n\n**Неизменяемый, иммутабельный** (immutable) объект — объект, состояние которого не может быть изменено после создания.\n\nИзменение иммутабельного объекта приводит к созданию нового объекта, но не затрагивает старый.\n\nИммутабельность затрагивает только сам объект, но не его свойства. Это работает как неглубокое копирование: ссылки на объекты-свойства остаются прежними.\n\n\n## Итерируемые объекты\n\n**Итерируемый объект** (iterable) — *любой* объект, *элементы* которого можно *перебрать* в *цикле for..of*.  \n\nПо умолчанию *итерируемыми* являются *встроенные типы Array*, *Set*, *Map* и *String*, в то время как *Object не является*.\n\n*Любой объект* можно *сделать итерируемым*, *реализовав* метод `Symbol.iterator`.\n\nВ примере ниже реализуется итератор для объекта `notes`, содержащего буквенные значения по индексам. В функции итератора замкнуты две переменные: начальный и конечный индексы. Для реализации метода `next()` используется стрелочная функция, поскольку необходим доступ к буквам.\n```js\nconst notes = {\n  0: 'n',\n  1: 'o',\n  2: 't',\n  3: 'e',\n  4: 's',\n  [Symbol.iterator]: function() {\n    let current = 0;\n    let last = 4;\n    return {\n      next: () => {\n        if (current <= last) {\n          return { done: false, value: this[current++] }\n        }\n        return { done: true };\n      },\n    };\n  },\n};\n\nfor (i of notes) {\n  console.log(i); // n, o, t, e, s\n}\nconsole.log(notes.length); // undefined\n```\n \n# Массивы\n\n- [Создание массива](#создание-массива)\n- [Обращение к элементам массива](#обращение-к-элементам-массива)\n- [Добавление и удаление элементов](#добавление-и-удаление-элементов)\n- [Является ли массивом](#является-ли-массивом)\n- [Сортировка](#сортировка)\n- [Псевдомассивы](#псевдомассивы)\n\n**Массив** (Array) — *встроенный итерируемый объект* (можно перебрать через `for..of`), который хранит элементы по индексам `0, 1, 2, ...`, имеет свойство `length`, а также имеет доступ к методам `Array.prototype` (`find`, `includes`, `reduce` и другие).\n\n## Создание массива\n\nСоздать массив можно двумя способами: через синтаксис `[]` или при помощи класса `Array` и его методов.\n```js\nconst foo = [1, 3, 7];\nconsole.log(foo); // [1, 3, 7];\n\nconst bar = Array(1, 3, 7);\nconsole.log(bar); // [1, 3, 7];\n```\n\nВ массиве по некоторым индекстам могут лежать пустые элементы (`empty`).\n```js\nconst foo = [, 0, 1, 2]; \nconsole.log(foo); // [empty, 0, 1, 2]\nconsole.log(foo[0]); // undefined\n\nconst bar = [,,,,,];\nconsole.log(bar); // [empty × 5]\n\nconst baz = Array(100); // пустой массив длины 100\nconsole.log(baz); // [empty × 100]\n\nconst qaz = [];\nqaz[1000] = 7; \nconsole.log(qaz); // [empty × 1000, 7]\n```\n\nМассив можно создать из любого итерируемого объекта при помощи `Array.from(iterable)` или оператора `...`.\n```js\nconst iterable = 'notes'; /* строка - итерируемый объект */\n\nconst foo = Array.from(iterable);\nconsole.log(foo); // ['n', 'o', 't', 'e', 's']\n\nconst bar = [...iterable];\nconsole.log(bar); //['n', 'o', 't', 'e', 's']\n```\n\nИнтересные примеры создания массивов.\n```js\nconst foo = Array(100).fill(0);\nconsole.log(foo); // [0 x 100]\n\nconst bar = Array.from(Array(100).keys());\nconsole.log(bar); // [0, 1, 2, ..., 99]\n\nconst baz = Array.from({ length: 100 }, (item, index) => index + 1); \nconsole.log(baz); // [1, 2, 3, ..., 100]\n```\n\n## Обращение к элементам массива\n\nОбращение к элементам массива не отличается от обращения к объектам, то есть производится по ключу (`[]`).\n\nКак и у обычного объекта, ключи массива являются строками.\n```js\nconst foo = [1, 3, 7];\nconsole.log(Object.keys(foo)); // [\"0\", \"1\", \"2\"]\nconsole.log(foo[1]); // 3\nconsole.log(foo[\"1\"]); // 3\n```\n\n## Добавление и удаление элементов\n\n*Массив* в JavaScript имеет методы, характерные **двухсторонней очереди** (deque, double ended queue), что позволяет достаточно просто добавлять и удалять элементы на обоих концах массива.\n```js\nlet foo = [2];\n\n/* добавление элемента в конец */\nfoo.push(3);\nconsole.log(foo); // [2, 3]\n\n/* добавление элемента в начало */\nfoo.unshift(1);\nconsole.log(foo); // [1, 2, 3]\n\n/* удаление последнего элемента */\nconst lastElem = foo.pop();\nconsole.log(lastElem); // 3\nconsole.log(foo); // [1, 2]\n\n/* удаление первого элемента */\nconst firstElem = foo.shift();\nconsole.log(firstElem); // 1\nconsole.log(foo); // [2]\n```\n\nДобавлять элементы можно и при помощи оператора `...`.\n```js\nlet bar = [2];\n\n/* добавление элемента в начало */\nbar = [1, ...bar];\nconsole.log(bar); // [1, 2]\n\n/* добавление элемента в конец */\nbar = [...bar, 3];\nconsole.log(bar); // [1, 2, 3]\n```\n\nУдаление при помощи `delete` создаёт пустую ячейку в массиве.\n```js\nlet baz = [3];\ndelete baz[0];\nconsole.log(baz); // [empty]\nconsole.log(baz[0]); // undefined\n```\n\n<!-- написать про split, slice, splice -->\n\n## Является ли массивом\n```js\n[] instanceof Array // true\nArray.isArray([]) // true\n```\n\n## Сортировка\n\n**Сортировка** (sorting) — *упорядочивание элементов* в *списке* (массиве) по какому-то *правилу*.\n\nВ JavaScript для *сортировки массива* имеется метод `Array.prototype.sort(comparator)`, принимающий в качестве аргумента **компаратор** (comparator) — функцию `comparator(a, b)`, задающую порядок сортировки. Если `a` и `b` равны, то функция должна вернуть `0`, если `a > b` — что-то больше нуля (например, `1`), если `a < b` — что-то меньше нуля (например, `-1`).\n```js\nconst numbers = [3, 2, 1];\nconsole.log(numbers.sort()); //  [1, 2, 3]\n``` \n**Компаратор** (в электронике) — устройство, принимающее два входных сигнала и определяющее, какой из них больше (возвращает `1`, если больше первый, `0` — если второй).\n\nВ объектно-ориентированных языках программирования компаратор может быть классом или интерфейсом, имеющим метод `compare`.\n\nЕсли не задать компаратор в методе `sort()`, то применится компаратор по умолчанию, сравнивающий элементы в лексикографическом порядке (как строки, посимвольно).\n```js\nconst numbers = [11, 1, 8, 10, 9];\nconsole.log(numbers.sort()); // [1, 10, 11, 8, 9]\n// поскольку '1' > '8', то '10' > '8' и `11` > `8`\n```\n\nОпределим *компараторы* для *сортировки* массива из *чисел* *по возрастанию* (ascending) и *по убыванию* (descending).\n```js\n/* по возрастанию */\nconst ascendingComparator = (a, b) => a - b; // если a > b, то a - b > 0\n\n/* более делальная версия, делающая то же самое  */\nconst anotherAscendingComparator = (a, b) => {\n  /* оператор > приводит свои операнды к числу */\n  if (a > b) {\n    return 1;\n  }\n  if (b > a) {\n    return -1;\n  }\n  return 0;\n}\n\n/* по убыванию */\nconst descendingComparator = (a, b) => b - a; // если a > b, то b - a < 0\n\nconst numbers = [11, 1, 8, 10, 9];\nconsole.log(numbers.sort(ascendingComparator)); // [1, 8, 9, 10, 11]\nconsole.log(numbers.sort(anotherAscendingComparator)); // [1, 8, 9, 10, 11]\nconsole.log(numbers.sort(descendingComparator)); // [11, 10, 9, 8, 1]\n```\nАналогично можно сортировать и более сложные сущности.  \nНапример: объекты по их конкретным полям.\n```js\nconst enginerComparator = (a, b) => b.skill - a.skill;\n\nconst enginers = [{ skill: 3 }, { skill: 1 }, { skill: 2 }];\nconsole.log(enginers.sort(enginerComparator));\n// [{ skill: 3 }, { skill: 2 }, { skill: 1 }]\n```\n\n## Псевдомассивы\n\n**Псевдомассив** (pseudo-array) — обычный *объект*, который как и массив, в качестве ключей имеет индексы `0, 1, 2, ...` и свойство `length`, но при этом не является итерируемым и не имеет доступа к методам `Array.prototype`.\n\n*Псевдомассив можно сделать итерируемым объектом*. \n\nПримером итерируемого псевдомассива является `arguments`, хранящий все аргументы функции `function`, в которой он используется.\n```js\n(function fn() {\n  console.log(arguments instanceof Array); // false\n  console.log(arguments instanceof Object); // true\n  console.log(arguments); // { 0: 1, 1: 2, 2: 3 callee: f, length: 3, Symbol(Symbol.iterator): f }\n  for (i of arguments) {\n    console.log(i); // 1, 2, 3\n  }\n})(1, 2, 3);\n```\n\n# Функции\n\n## Параметры и аргументы функции\n\n**Параметры функции** — имена, перечисленные в определении функции.\n\n**Аргументы функции** — значения, передаваемые в функцию.\n\n*Параметр функции* является *переменной*, *копирующей значение аргумента*.\n\nФактически, *параметры ведут себя* следующим образом.\n```js\n  const f = (param = {}) => {\n    var param = param || {}; // скрытое поведение\n  };\n```\n\nПоскольку в *JavaScript примитивные значения копируются напрямую*, а *объекты передаются по ссылке*, имеем следующее *поведение параметров фунцкии*.\n```js\nconst foo = 1;\nconst bar = { a: 1 };\nconst baz = { c: 1 };\n\nconst fn = (param1, param2, param3) => {\n  param1 = 2;\n  console.log(param1 === foo); // false (притимивные значения копируются напрямую)\n\n  param2.b = 2;\n  console.log(param2 === bar); // true (мутация аргумента по ссылке)\n  console.log(bar); // { a: 1, b: 2 }\n\n  param3 = { d: 2 };\n  console.log(param3 === baz); // false (перезапись переменной, утрата ссылки)\n  console.log(baz); // { c: 1 }\n};\n\nfn(foo, bar, baz);\n```\n\n## Стрелочная функция\n\n**Стрелочная функция** (Arrow Function Expression) является *функциональным выражением*, которое, помимо *укороченного синтаксиса*, обладает *рядом свойств* по сравнению с *функциональным выражением*, объявленным через `function` (Function Expression).\n```js\nconst inc = val => val += 1;\nconst sum = (a, b) => a + b;\nconst mul = (a, b) => {\n  return a * b;\n};\n```\n\n*Стрелочная функция* не имеет своих `this` и `arguments` — их значения *ищутся снаружи* (из *внешнего лексического окружения*).\n```js\nconst Foo = () => {\n  console.log(this); \n  console.log(arguments); \n};\nFoo();\n// Window {...}\n// ReferenceError: arguments is not defined\n```\n```js\nfunction Bar () {\n  console.log(this);\n  const Foo = () => {\n    console.log(this);\n    console.log(arguments);\n  };\n  Foo();\n}\nBar();\n// Window {...}\n// Window {...}\n// Arguments [...]\nnew Bar();\n// Bar {...}\n// Bar {...}\n// Arguments [...]\n```\n*Отсутствие* своего `this` влечёт за собой другую особенность: *стрелочная функция* *не может* быть использована как *функция-конструктор*, то есть *не может* быть вызвана с *конструкцией* `new`.\n```js\nconst Article = () => {};\nconst article = new Article(); // TypeError: Article is not a constructor\n```\n\nЕщё одной интересной *особенностью стрелочных функций* является то, что *оператор* `=>` *имеет очень низкий приоритет*, что делает *невозможным использование стрелочных функций* в качестве *операндов других операторов*.\n\nНапример, следующий пример *вызовет ошибку*, поскольку у `void` приоритет выше, чем у `=>`, и он *обрабатывается раньше*.\n```js\nconst fn = void () => console.log('notes'); // SyntaxError: Malformed arrow function parameter list\n```\n\nС `async` такой ошибки не возникает, поскольку `async` вообще *не является оператором*, поскольку не рассматривается отдельно от `function`.\n```js\nconst fn = async () => 'Notes';\n```\n\n# Встроенные объекты\n- [Error и его наследники](#error-и-его-наследники)\n  - [ReferenceError](#referenceerror)\n  - [SyntaxError](#syntaxerror)\n  - [TypeError](#typeerror)\n  - [RangeError](#rangeerror)\n  - [EvalError](#evalerror)\n  - [URIError](#urierror)\n  - [InternalError](#internalerror)\n- [Promise](#promise)\n  - [Порядок в Promise.all()](#порядок-в-promiseall)\n\n## Error и его наследники\n\n### ReferenceError\n**ReferenceError** — ошибка при обращении к *несуществующей переменной*.\n```js\nfoo.field; // ReferenceError: foo is not defined\n```\n```js\nconsole.log(foo) // ReferenceError: Cannot access 'foo' before initialization\nconst foo = {};\n```\n\n### SyntaxError\n**SyntaxError** - ошибка при попытке интерпретировать *синтаксически неправильный* код.\n```js\nconst foo; // SyntaxError: Missing initializer in const declaration\n```\n```js\nfunction(){ /* ... */ }() // SyntaxError: Function statements require a function name\n```\n```js\nfunction foo(){ /* ... */ }() // SyntaxError: Unexpected token )\n```\n```js\nJSON.parse('{ \"field\":\"value\", }'); // SyntaxError: Unexpected token } in JSON at position 19\n```\n### TypeError\n**TypeError** - ошибка при наличии *значения несовместимого* (неожидаемого) *типа*.\n```js\nconst foo = {};\nfoo.method(); // TypeError: foo.method is not a function\n```\n```js\nconst foo = 1;\nfoo = 7; // TypeError: Assignment to constant variable\n```\n### RangeError\n**RangeError** — ошибка в случае нахождения *значения за пределами допустимого диапазона*.\n```js\nconst foo = new Array(-1); // RangeError: Invalid array length\n```\n```js\nconst foo = 3;\nfoo.toFixed(101); // RangeError: toFixed() digits argument must be between 0 and 100\n```\n```js\nfunction foo() { foo() }\nfoo(); // RangeError: Maximum call stack size exceeded (везде, кроме Firefox)\n```\n### EvalError\n**EvalError** — ошибка в *глобальной функции eval()*. В *текущей* спецификации *не используется* и остаётся лишь для *совместимости*.\n\nОшибка ниже связана с проведением браузерами *политики безопастности контента* (Content Security Policy), которая помогает *избежать* многих *потенциальных XSS* (cross-site scripting) *атак*.  \nРанее её тип был *EvalError*, сейчас он просто *опускается*:\n```js\nwindow.setInterval(\"alert('notes')\", 25); // Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \"script-src github.githubassets.com\".\n```\n### URIError\n**URIError** - ошибка при передаче *недопустимых параметров* в *encodeURI()* или *decodeURI()*.\n```js\nencodeURI('\\uD900'); // URIError: malformed URI sequence (Firefox)\nencodeURI('\\uD900'); // URIError: The URI to be encoded contains an invalid character (Edge)\nencodeURI('\\uD900'); // URIError: URI malformed (Chrome and others)\n```\n### InternalError\n**InternalError** - *внутренняя* ошибка в *движке JavaScript*. (только *Firefox*)\n```js\nfunction foo() { foo() }\nfoo(); // InternalError: too much recursion\n```\n\n*Все* рассмотренные *типы ошибок* можно *сгенерировать* так же, как и *Error*, *наследниками* которого они являются:\n```js\nthrow new Error(/* ... */);\n```\n\n## Promise\n\n**Promise** (промис) – *специальный объект*, *содержащий своё состояние*.  \n*Изначально состояние* `pending` (ожидание).  \nЗатем либо `resolved`/`fulfilled` (выполнено успешно), либо `rejected` (выполнено с ошибкой).\n\n```javascript\n/* создание Promise */\nconst executor = (resolve, reject) => { /* ... */ };\nconst promise = new Promise(executor); \n```\nФункция `executor(resolve, reject)` *вызывается автоматически*. В ней можно выполнять *любые асинхронные операции*. По их завершении следует вызвать либо `resolve(value)`, либо `reject(reason)`.\n\nПосле вызова `resolve` или `reject` *промис меняет* своё *состояние*, которое становится *конечным* (больше его изменить нельзя). \n\n*Отреагировать* на *изменение состояния промиса* можно при помощи `then` и `catch`.\n```js\nconst onResolved = value => { /* ... */ };\nconst onRejected = reason => { /* ... */ };\n\n// функция onResolved сработает при успешном выполнении\npromise.then(onResolved);\n// функция onRejected – при выполнении с ошибкой\npromise.then(onResolved, onRejected);\npromise.catch(onRejected);\n```\n\nПример с *setTimeout*, где *промис успешно выполнится* не менее, чем через *3 секунды*.\n```js\nconst executor = resolve => void setTimeout(resolve, 3000);\nconst promise = new Promise(executor);\npromise.then(() => console.log('resolved!'));\n// через ~3 секунды выведется 'resolved!'\n```\nПример с *передачей значения* в `resolve`.\n```js\nconst executor = resolve => void setTimeout(() => resolve('resolved!'), 3000);\nconst promise = new Promise(executor);\npromise.then(console.log);\n// через ~3 секунды выведется 'resolved!'\n```\nПример с *передачей причины* в `reject`.\n```js\nconst executor = (resolve, reject) => void setTimeout(() => reject('rejected!'), 3000);\nconst promise = new Promise(executor);\npromise.catch(console.log);\n// через ~3 секунды выведется 'rejected!'\n```\n\n### Промисификация\n**Промисификация** – *создание обёртки*, *возвращающей Promise*, вокруг *асинхронной* функциональности.\n\nОбычно *промисифицируют асинхронные функции*, построенные на *функциях обратного вызова* (callbacks).\n```js\n/* Принимается функция fn и возвращается функция-обёртка, возвращающая Promise. */\nconst promisify = fn => (...args) => new Promise((resolve, reject) => {    \n  const callback = (err, data) => err ? reject(err) : resolve(data);\n  fn(...args, callback);\n});\n```\n\n<!-- fn.call(this, [...args, callback]); // вызывается fn с обновлёнными аргументами -->\n\n### Цепочка промисов\n\nЕсли нужно выполнять асинхронные операции в определённой последовательности, можно каждую из них обернуть в промис и создать **цепочку промисов** (Promise chain). Для *создания* такой *цепочки* необходимо в `.then()` или `.catch()` *вернуть промис*.\n\n### Порядок в Promise.all()\n\nФункция `Promise.all(iterable)` *принимает итерируемый объект* (обычно массив), содержащий промисы (элементы, не являющиеся промисами, помещаются в `Promise.resolve()`), *дожидается выполнения каждого из промисов* и *возвращает массив*, состоящий из их *значений*.\n\nНесмотря на то, что *промисы выполняются асинхронно*, *порядок в результирующем массиве значений совпадает* с *порядком промисов* в *начальном итерируемом объекте* благодаря *внутреннему свойству [[Index]]*:\n```js\nconst slow = new Promise(resolve => setTimeout(resolve, 250, 'slow'));\nconst instant = 'instant'; // тип не Promise , поэтому преобразуется в Promise.resolve('instant')\nconst quick = new Promise(resolve => setTimeout(resolve, 50, 'quick'));\n\nconst onResolved = responses => responses.map(response => console.log(response));\nPromise.all([slow, instant, quick]).then(onResolved);\n\n// или то же самое с помощью async/await\ntry {\n  const responses = await Promise.all([slow, instant, quick]);\n  responses.map(response => console.log(response)); // 'slow', 'instant', 'quick'\n} catch (e) {\n  /* ... */\n}\n```\n\nПоскольку тип *String* является *итерируемым*, его тоже *можно передать* в *Promise.all()*:\n```js\nPromise.all('notes').then(res => console.log(res)); // ['n', 'o', 't', 'e', 's']\n// что эквивалентно\nPromise.all(['n', 'o', 't', 'e', 's']).then(res => console.log(res));\n```\n\n### Promise и async/await\n\n```js\nasync function f(time) {\n  const foo = await new Promise(res => setTimeout(() => res('Notes 1'), time));\n  console.log(foo);\n  const bar = await Promise.resolve('Notes 2');\n  console.log(bar);\n  await Promise.reject('Error');\n}\n\nf(2000);\n// Notes 1\n// Notes 2\n// Uncaught (in promise) Error\n```\n```js\nf(2000).catch(e => console.log(e));\n// Notes 1\n// Notes 2\n// Error\n```\n\n```js\nconst asyncGeneratorStep = (gen, resolve, reject, _next, _throw, method, arg) => {\n  try {\n    const { value, done } = gen[method](arg);\n    if (done) {\n      resolve(value);\n    } else {\n      Promise.resolve(value).then(_next, _throw);\n    }\n  } catch (error) {\n    reject(error);\n  }\n}\n\nconst _asyncToGenerator = fn => (...args) =>\n  new Promise((resolve, reject) => {\n    const _next = value => void step('next', value);\n    const _throw = error => void step('throw', error);\n    const gen = fn(args);\n    function step (method, arg) {\n      asyncGeneratorStep(gen, resolve, reject, _next, _throw, method, arg);\n    }\n    _next(undefined);\n  });\n\nconst generator = function* (time) {\n  const foo = yield new Promise(res => setTimeout(() => res('Notes 1'), time));\n  console.log(foo);\n  const bar = yield Promise.resolve('Notes 2');\n  console.log(bar);\n  yield Promise.reject('Error');\n};\n\nfunction f() {\n  return _asyncToGenerator(generator).apply(this, arguments);\n}\n\nf(2000);\n// Notes 1\n// Notes 2\n// Uncaught (in promise) Error\n```\n```js\nf(2000).catch(e => console.log(e));\n// Notes 1\n// Notes 2\n// Error\n```\n\n# Спецификация\n\n## Функции под капотом JavaScript\n\n**Функциональный объект** (function object) — *объект*, поддерживающий *внутренний метод [[Call]]*.\n\n**Фукция-конструктор** (constructor function), или просто **конструктор** (constructor), — *функциональный объект*, поддерживающий *внутренний метод [[Construct]]*.\n\n### Метод [[Call]]\n\n**Метод [[Call]] (thisArgument, argumentsList)** *выполняет код*, связанный с *его* функциональным объектом.  \n\n*Вызывается* при помощи *выражения вызова функции*: \n```js\nobject()\n```\n\n*Аргументы*: значение *this* и *список аргументов*, переданных *функции выражением вызова*.  \n\nОбъекты, которые *реализуют* внутренний метод *[[Call]]*, называются **вызываемыми** (callable).\n\n### Метод [[Construct]]\n\n**Метод [[Construct]] (argumentsList, newTarget)** *cоздаёт и возвращает объекты*.\n\n*Вызывается* при помощи *операторов* **new** и **super**.\n\n*Аргументы*: *список аргументов оператора* и *объект*, к которому изначально был *применён оператор new*.  \n\n\n### Инстанцирование функционального объекта \n\n**Инстанцирование** (instantiation) — *создание экземпляра класса* (instance).  \nСлово *инстанционирование* применяется *к классу*, *создание* (creation) - *к объекту*.  \n\nНесмотря на то, что *функции* в JavaScript являются *объектами*, в то же время они могут быть и *классами*, поэтому к ним и *применяется* слово *инстанционирование*.\n\n*Функциональные объекты инстанционируются* при помощи:\n```js\nInstantiateFunctionObject(scope)\n```\n\n### Function Declaration\n```js\nfunction BindingIdentifier ( FormalParameters ) { FunctionBody }\n```\n#### I этап интерпретации - инстанционирование\n1) Положить в переменную *strict* *true*, если к коду функции *применён strict мод*, *false иначе*.  \n2) Положить в переменную *name строку BindingIdentifier* или строку *\"default\"*, если *значение не задано*.\n3) Положить в переменную *F результат* выполнения `FunctionCreate(Normal, FormalParameters, FunctionBody, scope, strict)`.  \n4) *Создать конструктор* с помощью `MakeConstructor(F)`.  \n5) *Установить имя* функции с помощью `SetFunctionName(F, name)`.  \n6) *Вернуть F*.\n\n#### II этап интерпретации - оценка (Evaluation)\n1) Вернуть `NormalCompletion(empty)`.\n\n### Function Expression\n```js\nfunction ( FormalParameters ) { FunctionBody }\n```\n\n#### I этап интерпретации\nОтсутствует.\n\n#### II этап интерпретации - оценка (Evaluation)\n1) Положить в переменную *strict* *true*, если к коду функции *применён strict мод*, *false иначе*.  \n2) Положить в переменную *scope* *LexicalEnvironment* из *контекста выполнения*.\n3) Положить в переменную *closure результат выполнения* `FunctionCreate(Normal, FormalParameters, FunctionBody, scope, strict)`.  \n4) *Создать конструктор* с помощью  `MakeConstructor(F)`.  \n6) *Вернуть closure*.\n"
  },
  {
    "path": "JavaScriptDOM.md",
    "content": "\n# DOM\n\n# Всплытие и погружение\n\n# Обработка пользовательских событий\n\n## Обработка данных формы\n```html\n<form name=\"valform\" onsubmit=\"handleSubmit(event)\">\n  Name: <input type=\"text\" name=\"name\">\n  <br/>\n  Card Type:\n  <select name=\"cardType\">\n    <option value=\"visa\">Visa</option>\n    <option value=\"mastercard\">MasterCard</option>\n    <option value=\"discover\">Discover</option>\n    <option value=\"amex\">Amex</option>\n    <option value=\"diners\">Diners Club</option>\n  </select>\n  <br/>\n  <button type=\"submit\">Submit</button>\n</form>\n```\n```js\nfunction handleSubmit(e) {\n  e.preventDefault();\n  const formData = new FormData(e.target);\n  const values = Object.fromEntries(formData);\n  console.log(values); // { name: '...', cardType: '...' }\n}\n```\n![image](https://user-images.githubusercontent.com/22237384/155226176-9895af8e-86c9-4ae9-a19e-7a4a5cdca933.png)\n![image](https://user-images.githubusercontent.com/22237384/155226480-9a4ef714-8a1a-4051-90e7-0e8ce224588b.png)\n\n\n"
  },
  {
    "path": "NodeJS.md",
    "content": "- [Основные определения](#основные-определения)\n  - [Что такое NodeJS](#что-такое-nodejs)\n  - [Событийно-ориентированное программирование](#событийно-ориентированное-программирование)\n- [Асинхронность в NodeJS](#асинхронность-в-nodejs)\n  - [Функция обратного вызова (callback)](#функция-обратного-вызова-callback)\n  - [Промиссы](#промиссы)\n  - [async..await](#asyncawait)\n- [Стек вызовов](#стек-вызовов)\n- [Цикл событий NodeJS](#цикл-событий-nodejs)\n- [Библиотека libuv в деталях](#библиотека-libuv-в-деталях)\n- [Аргументы process.argv](#аргументы-processargv)\n\n# Основные определения\n- [Что такое NodeJS](#что-такое-nodejs)\n- [Событийно-ориентированное программирование](#событийно-ориентированное-программирование)\n \n## Что такое NodeJS\n\n**NodeJS** — *асинхронная*, *управляемая событиями* (event-driven) *серверная среда выполнения JavaScript* (runtime environment), построенная на JavaScript-движке *V8*, который используется в *Google Chrome*.\n\nВ NodeJS используется *кроссплатформенная библиотека* `libuv`, нацеленная на *асинхронный ввод-вывод*. При помощи `libuv` выполняются операции ввода-вывода, которые считаются слишком трудоёмкими для NodeJS.\n\nНесмотря на то, что NodeJS является *однопоточным*, он может использовать возможности нескольких ядер при помощи *дочерних процессов* из модуля `child_process` и модуля `cluster`.\n\n\n\n## Событийно-ориентированное программирование\n\n**Событие** (Event) — действие, сгенерированное пользователем или системой. \n\nПримеры событий: успешное завершение операции, ошибка, нажатие на клавишу или кнопку мыши, истечение времени таймера и так далее.\n```js\n/* действия пользователя */\ninput.onchange = event => { /* ... */ };\nbutton.onclick = event => { /* ... */ };\n/* успех и ошибки */\nwindow.onload = event => { /* ... */ };\nwindow.onerror = event => { /* ... */ };\n```\n\n**Событийно-ориентированное программирование** (Event-driven programming) — парадигма, в которой управление программой (flow control) определяется событиями. \n\nВ событийно-ориентированной программе помимо событий должны присутствовать слушатели событий (event listeners), которые будут реагировать на наступление событий какими-то действиями. В контексте JavaScript — вызовами функций обратного вызова.\n\nNodeJS предоставляет класс `EventEmitter` для создания пользовательских событий.\n```js\nconst EventEmitter = require('events');\nclass MyEmitter extends EventEmitter {}\nconst emitter = new MyEmitter();\n\n/* слушатель события */\nemitter.on('myevent', () => console.log('myevent occurred!'));\n/* генерация события */\nemitter.emit('myevent');\n```\nСтоит отметить, что метод `EventEmitter` работает синхронно. Поэтому важно, чтобы подписка на событие `on()` находилась в коде раньше его генерации `emit()`, иначе событие не обработается.\n\n\n\n# Асинхронность в NodeJS\n\n## Функция обратного вызова (callback)\n- [О функциях обратного вызова](#о-функциях-обратного-вызова)\n- [Паттерн error-first callback](#паттерн-error-first-callback)\n- [Callback hell](#callback-hell)\n\n### О функциях обратного вызова\n\n**Функция обратного вызова**, **колбэк** (англ. callback, cb) — *функция*, которую *передают аргументом* в *другую функцию*. \n\nФункция обратного вызова может быть вызвана в любое время, когда это будет необходимо.\n\n*Функции обратного вызова* могут быть использованы как *алгоритмы* для *решения задач*, имеющих *несколько способов решения*. Одной из таких задач является сортировка. \n\nНапример, в JavaScript метод `Array.prototype.sort(compareFunction)` принимает функцию обратного вызова `compareFunction`, использующуюся как алгоритм сравнения двух элементов массива.\n```js\nconst compareFunction = (a, b) => a - b;\n[1, 3, 2].sort(compareFunction); // [1, 2, 3]\n```\nАналогично работают методы `find`, `filter` (поиск, фильтрация по заданному алгоритму) и многие другие.\n\nНо чаще всего *функции обратного вызова* используются как какой-то *набор инструкций*, который нужно *выполнить после совершения* какого-то *действия* или *наступления события*. Использование колбэков позволяет убедиться, что код внутри них выполнится не раньше, чем асинхронная функция завершит своё выполнение. \n\nИменно такой вариант использования колбэков является ключом к асинхронности в NodeJS. \n\nСам подход появился в функциональном программировании ещё задолго до появления NodeJS и назывался **Continuation-passing Style** (CPS) и подразумевал, что *управление* (какой-то набор инструкций) *передавалось* далее *в форме продолжения* (continuation). То есть одна функция могла принять другую функцию — **функцию продолжения** (continuation function). По завершению своей работы вместо возвращения стандартного результата она вызывала функцию продолжения, передавая ей результат в качестве аргумента.\n\nБольшинство методов NodeJS сейчас использует колбэки. Когда асинхронная, блокирующая операция поступает на вход и запускается, колбэк сохраняется в памяти и его выполнение откладывается, последующий код может продолжить своё выполнение. Как только основной код и асинхронная функция завершают своё выполнение, колбэк достаётся из памяти и выполняетсся в том же потоке, в котором выполнялся основной код. Такой подход весьма эффективен, поскольку значительно увеличивает пропускную способность и скорость NodeJS-приложений.\n\nПример асинхронной функции `setTimeout(callback, delay)`, принимающей *колбэк*, который вызовется *не ранее*, чем *через* `delay` *миллисекунд*.\n```js\nsetTimeout(() => console.log('Notes'), 300);\n// выведется строка 'Notes' не ранее, чем через 300мс\n```\n\n### Паттерн error-first callback\n\nБольшинство асинхронных методов в *NodeJS* следуют *идиоматическому* (свойственному языку) *паттерну* **Error-first callback** (сперва ошибка), позволяющий довольно просто узнать, произошла ли ошибка при выполнении операции. \n\nЕсли паттерн не используется, пользователь самостоятельно должен по аргументам колбэка выяснять, была ли ошибка или нет, что значительно сложнее.\n\n*Идея паттерна* заключается в следующем\n* *Функция обратного вызова* *передаётся последним аргументом* в метод, производящий некоторую *асинхронную операцию*.\n* Если *операция завершается ошибкой*, *первым аргументом функции обратного вызова* станет *экземпляр* `Error`. \n* Если *операция завершается успешно*, *первым аргументом* станет `null`, а *результат операции* (данные) будет передан *последующими аргументами аргументом* (чаще всего только вторым аргументом).\n```js\nconst asyncFn = (param1, param2, /* ... */, callback) => {\n  if (/* error occurs */) {\n    return callback(new Error('reason'));\n  }\n  /* ... */\n  const data = { /* ... */ };\n  callback(null, data, /* ... */);\n}\n```\n```js\nconst callback = (err, data, /* ... */) => { /* ... */ };\nasyncFn(1, 2, /* ... */, callback);\n```\n\n*Обработка ошибок* и *данных* на примере *асинхронного чтения файла*\n```js\nconst fs = require('fs');\n\nconst readFileCallback = (err, data) => {\n  if (err) {\n    console.error('Read file error occured: ', err);\n    /* обработать ошибку здесь */\n  } else {\n    console.log(data);\n    /* обработать данные здесь */\n  }\n};\n\n/* асинхронное чтение файла */\nfs.readFile('/* ... */', readFileCallback);\n```\n\nПопытка выбросить ошибку `err` из функции обратного вызова является распространённой ошибкой, поскольку к моменту вызова `readFileCallback` код вокруг `fs.readFile` завершит своё выполнение. Ошибка не попадёт в `try..catch` и, скорее всего, приведёт к краху всего приложения.\n```js\nconst readFileCallback = (err, data) => {\n  if (err) {\n    throw err; // так лучше не делать\n  } else {\n    console.log(data);\n  }\n};\n\ntry {\n  fs.readFile('/* ... */', readFileCallback);\n} catch (err) {\n  /* ошибка сюда не попадёт */\n  console.log(err);\n}\n```\nАналогичная ситуация с данными `data`. Их возврат при помощи `return` ничего не даст. Функция `fs.readFile` просто вызовет операцию чтения, но код не будет ждать её выполнения и пойдёт дальше. Поэтому возвращённое значение будет `undefined`.\n```js\nconst readFileCallback = (err, data) => {\n  if (err) {\n    console.error('Read file error occured: ', err);\n  } else {\n    console.log(data);\n    return data;\n  }\n};\n\nconst data = fs.readFile('/* ... */', readFileCallback);\nconsole.log(data); // undefined\n```\n\n### Callback hell\n\nКак мы уже выяснили, использование кобэков помогает сделать код асинхронным.\n\n<!-- асинхронные операции вызываются, но дальнейший код не ждёт их завершения, продолжая свою работу. В итоге ошибки и данные из функций обратного вызова не могут быть возвращены: их необходимо обрататывать на месте. -->\n\nОперации, которые должны выполниться после асинхронной функции, должны быть переданы в её аргументы колбэком, иначе правильная последовательность выполнения не гарантируется.\n\nЕсли несколько асинхронных операций должны выполниться последовательно, появляется цепочка (каскад) вложенных колбэков, своеобразное ветвление кода.\n\nНапример, асинхронное последовательное создание, чтение, удаление файла выглядит следующим образом.\n```js\nconst fs = require('fs');\n\nfs.writeFile('./input.txt', 'Hello!', 'utf8', (writeErr) => {\n  if (writeErr) {\n    console.error('White file error occured: ', writeErr);\n  } else {\n    fs.readFile('./input.txt', 'Hello!', 'utf8', (readErr, data) => {\n      if (readErr) {\n        console.error('Read file error occured: ', readErr);\n      } else {\n        console.log(data.toString());\n        fs.unlink('./input.txt', (removeError) => {\n          if (removeError) {\n            console.error('Remove file error occured: ', readErr);\n          }\n        });\n      }\n    });\n  }\n});\n```\nТакое ветвление называют **Callback Hell**, а в других языках программирования можно встретить аналогичное понятие **Pyramid of doom**. Чем больше последовательных асинхронных операций, тем сложнее работать с таким кодом.\n\n### Способы разрешения Callback Hell\n\nНа сегодняшний день эффективным решением проблемы является использование `Promise` и `async..await` \n\nДо их появляения разработчики пытались уменьшить негативное влияние *Callback Hell*, вынося функции обратного вызова в переменные или даже в отдельные модули.\n\nЕсли вынести функцию не удаётся (например, в неё используются переменные из замыкания), можно использовать именованные функции вместо анонимных. Это особенно может упростить понимание кода, когда асинхронная функция принимает несколько функций обратного вызова.\n```js\nfs.writeFile('./input.txt', 'Hello!', 'utf8', function writeFileCallback(err) {\n  /* ... */\n});\n```\nИспользование паттерна *Error-first callback* также помогает бороться с *Callback Hell*: в каждой функции обратного вызова обрабатывается именно свойственная ей ошибка. В противном случае довольно трудно было бы понять, где ошибка возникла.\n\n## Промиссы\n\nЕсть *другой способ разрешения Callback Hell*: *промиссификация асинхронных функций* и объединение их в цепочку промиссов (promise chaining). \n\nPromise не заменяет функции обратного вызова совсем (они всё ещё передаются в `then` и `catch`), но код читать становится намного проще.\n\nНапример, промиссифицируем асинхронные функции создания, чтения и удаления файла.\n```js\nconst fs = require('fs');\n\n/* промиссификация вручную */\nconst writeFile = (filePath, fileData, encoding) => new Promise((resolve, reject) => {\n  fs.writeFile(filePath, fileData, encoding, (err, data) => err ? reject(err) : resolve(data));\n});\n\n/* промиссификация с помощью promisify */\nconst promisify = fn => (...args) => new Promise((resolve, reject) => {    \n  const callback = (err, data) => err ? reject(err) : resolve(data);\n  fn(...args, callback);\n});\nconst readFile = promisify(fs.readFile);\nconst removeFile = promisify(fs.unlink);\n```\n```js\nwriteFile('input.txt', 'Notes', 'utf-8')\n  .then(() => readFile('input.txt', 'utf-8');\n  .catch(writeErr => console.error('Write file error occured: ', writeErr))\n  .then(() => removeFile('input.txt')\n  .catch(readErr => console.error('Read file error occured: ', readErr))\n  .catch(removeErr => console.error('Remove file error occured: ', readErr));\n```\n\nНачиная с Node v11.0.0 можно использовать встроенные промиссы.\n```js\nconst fs = require('fs').promises;\n\nfs.readFile('input.txt')\n  .then(() => { /* ... */ })\n  .catch(() => { /* ... */ });\n```\n\n## async..await\n\nСамым современным способом решения **Callback Hell**, который вообще исключает использование функций обратного вызова, является `async..await`.\n\nЭта конструкция позволяет писать асинхронный код в синхронном стиле, поскольку под капотом оборачивает его в промиссы.\n\n`await` дожидается выполнения промисса или цепочки промиссов (вызывает `.then` до тех пор, пока не вернётся ошибка или примитивное значение). Использование `await` доступно только внутри `async`.\n\n`async` оборачивает результат выполнения функции или метода в промисс. \n\n```js\nconst asyncFn = async () => {\n  try {\n    const data = await readFile('input.txt');\n    await removeFile('input.txt');\n  } catch (e) {\n    console.log(e);\n  }\n};\n\nasyncFn(); // Promise\n```\nПри использовании `async..await` ошибка или данные так же, как и в случае с функциями обратного вызова, не могут быть возвращены в код, в котором была вызвана функция: в данном случае они не покидают промисс.\n```js\nconst asyncFn = async () => {\n  try {\n    const data = await readFile('input.txt');\n    await removeFile('input.txt');\n    return data;\n  } catch (e) {\n    console.log(e);\n    throw e;\n  }\n};\n\nasyncFn\n  .then(data => { /* ... */ })\n  .catch(err => { /* ... */ })\n```\nНичего глобально не изменилось, код просто стал чище.\n\n# Стек вызовов\n\n**Стек вызовов** (Call Stack, Stack) — вспомогательная структура данных в виде LIFO-очереди (Last In First Out), хранящая информацию о том, в каком месте в коде выполняется скипт в данный момент. С помощью стека вызовов можно узнать, какая функция выполняется в данный момент и в пределах какой функции она вызвана.\n\nПоскольку JavaScript однопоточен, в нём имеется лишь один стек вызовов.\n\nЕсли стек вызовов не пуст, это означает, что основной поток занят выполнением JavaScript-кода.\n\nВ стек вызовов помещается следующая информация\n* Адрес места (строка и столбец), откуда функция была вызвана. Это позволяет продолжить с места вызова функции после её выполнения.\n* Параметры и локальные переменные функции. Таким образом во вложенных функциях переменные доступны из замыкания.\n\nКогда создаются локальные переменные с примитивными значениями и их функция удаляется из стека, они также удаляются из памяти.\n\nКогда создаются локальные переменные с объектам, значения объектов созданяются в кучу (heap), а в переменной сохраняется лишь указатель, ссылка на них, по которой значение можно получить  или изменить. Когда функция, в которой был создан объект, удаляется из стека, объект остаётся в памяти до тех пор, пока его не очистит сборщик мусора (garbage collector).\n\nСтек вызовов является внутренней реализацией V8 и не доступен для JavaScript-разработчика.\n\nЧастично стек вызовов можно явно увидеть, когда в приложении возникает ошибка.\n```js\nconst foo = () => {\n  throw new Error('something wrong');\n};\n\nconst bar = () => foo();\n\nbar();\n/*\nUncaught Error: something wrong\n    at foo (<anonymous>:2:9)\n    at bar (<anonymous>:5:19)\n    at <anonymous>:7:1\n*/\n```\nУ стека вызовов есть максимально допустимая глубина (~10000). Если она достигается (например, посредством бесконечной рекурсии), выбрасывается ошибка о переполнении стека (`stack overflow`).\n```js\nconst foo = () => foo();\nfoo();\n/*\nUncaught RangeError: Maximum call stack size exceeded\n    at foo (<anonymous>:1:13)\n    at foo (<anonymous>:1:19)\n    at foo (<anonymous>:1:19)\n    at foo (<anonymous>:1:19)\n*/\n```\n\n## Пример работы стека вызовов\n\nПусть имеется следующий скрипт.\n```js\n/* main.js */\n\nconst bar = () => console.log('bar');\nconst baz = () => console.log('baz');\n\nconst foo = () => {\n  console.log('foo');\n  bar();\n  baz();\n};\n\nfoo();\n```\nКогда начинает выполняться скрипт, он помещается в стек вызовов (скрипт по сути является одной большой функцией).\n```js\n/* Call Stack */\n- main()\n```\nКаждая вызванная в скрипте функция добавляется в стэк.\n```js\n/* Call Stack */\n- foo()\n- main()\n```\nЕсли внутри неё вызываются другая функция, то она помещается в стек поверх предыдущей.\n```js\n/* Call Stack */\n- bar()\n- foo()\n- main()\n```\nКогда функция завершает своё выполнение, она удаляется из стека.\n```js\n/* Call Stack */\n- foo()\n- main()\n```\nДалее аналогично, пока скрипт не завершит свою работу и стек не опустеет.\n```js\n/* Call Stack */\n- baz()\n- foo()\n- main()\n```\n```js\n/* Call Stack */\n- foo()\n- main()\n```\n```js\n/* Call Stack */\n- main()\n```\n```js\n/* Call Stack */\n```\n\n# Цикл событий NodeJS\n\n**Цикл событий** (Event Loop) — механизм, позволяющий *NodeJS выполнять неблокирующие операции ввода-вывода*. Когда NodeJS встречает в запущенном скрипте асинхронную операцию ввода-вывода, он выгружает её в ядро системы и продолжает выполнение других операций в основном потоке. Когда асинхронная операция заканчивает своё выполнение, генерируется событие о том, что её колбэк может быть помещён в очередь на исполнение. \n\nПоскольку большинство ядер многопоточны, они могут обрабатывать несколько задач одновременно в фоновом режиме.\n\nЦикл событий выполняется в том же потоке, что и основной скрипт, но запускается только после завершения работы скрипта. Это позволяет циклу событий не блокировать основной поток при запуске проекта и даёт преимущество перед похожими технологиями в других языках.\n\nЦикл событий не может работать, пока стек вызовов не пуст. Он ждёт, пока освободится стек вызовов и, если в очереди есть готовые к исполнению колбеки, берёт первый из них и помещает его в стек. Когда стек снова освобождается, в него помещается следующий колбек и так далее.\n\nКак в браузере, так и в NodeJS цикл событий скрыт от программиста, является частью внутренней реализацией.\n\n<!-- Если в скрипте встречается какая-то асинхронная операция, её выполнение выносится из основного потока, а её колбэк выносится в цикл событий. Это позволяет продолжить выполнение скрипта и больше не возвращаться в это же место. Когда скрипт завершает работу, начинается работа цикла событий (если операции заканчивают своё выполнение, то в основном потоке запускаются их колбэки). Когда колбэки заканчиваются в цикле событий, NodeJS усыпляет его до дальнейших указаний.\n-->\n\n*Один полный обход* (итерация) *цикла событий* называется **тиком** (tick).\n\n## Фазы цикла событий\n\nВо время выполнения основного скрипта вызываются асинхронные операции (таймеры, операции ввода-вывода), происходит подписка на события. Каждая такая операция имеет колбэк, который по завершению операции в зависимости от её типа помещается в характерную очередь. Каждая очередь представляет собой определённую **фазу** (phase) цикла событий.\n\nЦикл событий не совсем является циклом, он состоит из набора фаз, которые повторяются по кругу.\n\n*Основные фазы тика цикла событий*\n* **Таймеры** (Timers) — *колбэки истёкших* `setTimeout` и `setInterval`.\n* **Ожидающие колбэки** (Pending callbacks) — *колбэки системных операций* (например, ошибки TCP).\n* **Опрос** (Poll) — *ожадание новых колбэков* (таймеров, ожидающих колбэков) и их выполнение. Здесь выполняется большинство *колбэков завершённых операции ввода-вывода* (I/O).\n* **Проверка** (Check) — *колбэки* `setImmediate`.\n* **Закрывающие колбэки** (Close callbacks) — *колбэки закрывающихся соединений*.\n\nВсе фазы выполняются последовательно.\n\nКогда цикл событий останавливается на определённой фазе, он выполняет все её колбэки до тех пор, пока они не закончатся или не будет достигнут лимит их выполнения. Затем цикл событий переходи к следующей фазе.\n\nВсе операции, относящиеся к основным фазам, можно назвать **задачами** (Tasks).\n\nЛюбая операция ввода-вывода может планировать другие операции ввода-вывода и события.\n\nПеред каждой фазой последовательно выполняются две промежуточные (intermediate) фазы:\n* **Очередь для nextTick** (nextTick Queue). Колбэки `process.nextTick`.\n* **Очередь микрозадач** (Microtasks Queue). Колбэки разрешённых (resolved) `Promise`.\n\nФактически, все операции, относящиеся к промежуточным фазам, можно назвать **микрозадачами** (microtasks). Они не совсем являются частью цикла событий, предоставляемого библиотекой `libuv`, но являются частью NodeJS.\n\nПереход к следующей фазе не происходит, пока имеются невыполненные микрозадачи.\n\nNodeJS следит за выполнением незаконченных асинхронных операций и прогоняет цикл событий до тех пор, пока они все не завершат своё выполнение. Когда все операции завершили своё выполнение, NodeJS усыпляет цикл событий до наступления новых событий.\n\n## Пример работы цикла событий\n\n* Программист запускает приложение, точкой входа в которое является скрипт `app.js`. В этот момент создаётся поток (thread), в котором будет выполняться приложение и инициализируется цикл событий (event loop).\n```\nnode app.js\n```\n\n\n\n<!--\n\nКод ниже отработает синхронно, последовательно.\n```js\nconsole.log('before');\nconsole.log('after');\n// выведет \"before\", \"after\"\n```\nСледующие два примера отработают асинхронно, непоследовательно.\n```js\nPromise.resolve('before').then(console.log);\nconsole.log('after');\n// выведет \"after\", \"before\"\n```\n```js\nsetTimeout(() => console.log('before'), 0);\nPromise.resolve('after').then(console.log);\n// выведет \"after\", \"before\"\n```\nЧтобы симулировать синхронное поведение (выполнить инструкции с логированием в правильном порядке), можно написать следующее.\n```js\nconst after = () => console.log('after');\n\nPromise.resolve('before')\n  .then(console.log)\n  .then(after);\n// выведет \"before\", \"after\"\n\nsetTimeout((after) => {\n  console.log('before');\n  after();\n}, 0, after);\n// выведет \"before\", \"after\"\n```\n-->\n\n# Библиотека libuv в деталях\n\nРабота с ядром системы в NodeJS происходит при помощи сторонней библиотеки `libuv`, написанной на `C`.\n\n`libuv` предоставляет Event Loop для NodeJS, а также возможности для работы с сетью (network I/O), файлами (File I/O), операционной системой (Operating System) и другими вещами. \n\n`libuv` имеет фиксированное количество потоков (по умолчанию 4), вместе называемых `thread pool`. NodeJS отправляет `libuv` очередь задач, откуда потоки из `thread pool` берут (pull) задачи и исполняют их. Когда какой-то поток завершает задачу, он информирует об этом NodeJS и колбэк выполненной задачи поступает в очередь на исполнение.\n\nКогда это возможно, `libuv` избегает использования `thread pool` в пользу использования асинхронных интерфейсов, предоставляемых операционными системами и базами данных.`thead pool` используется лишь в том случае, если такие интерфейсы отсутствуют (или отсутствовали на момент написания библиотеки). На данный момент `thread pool` используется только при работе с файлами (File I/O, модуль `fs`). При работе с сетью (Network I/O, модуль `net` для TCP и IPC, модуль `dgram` для UDP) `thread pool` не используется.\n\nEvent Loop однопоточен.\n\n<!-- Reactor pattern. -->\n\nРабота с сетью: TCP и UDP обработчики. Операционная система (Thread pool не используется).\nРабота с файлами: stream и pipe в fs (потоки ввода-вывода). Thread pool (работает только с file I/O).\n\n# Аргументы process.argv\n\nПри запуске приложения из консоли можно передавать ему параметры, которые сохраняются в `process.argv` и доступны в приложении.\n```js\nconst [nodePath, filePath, ...args] = process.argv;\n```\nПервым параметром всегда передаётся полный путь к NodeJS, вторым - полный путь к исполняему js-файлу. \n```cmd\nnode index.js test\n```\n```js\nconst mode = process.argv[3];\nconsole.log(mode); // test\n```\n"
  },
  {
    "path": "ProductQuality.md",
    "content": "## О чистоте кода\n\n## О логировании\n\n**Лог** (англ. `log`, дословно *переводится* как *бревно*) - это *информация* о некотором *событии*, которое *произошло в системе*.\n\n**Логирование** (англ. `logging`) - это *написание логов*, то есть *вывод информации* о том, что *происходит* в *системе*.\n\nПример *событий* в *системе*: \"Сервер запущен\", \"База данных подключена\", \"Произошла ошибка\", \"Пришёл запрос с клиента\", \"Пользователь авторизировался\" и так далее.\n\nЛоги могут иметь любую структуру, которую определяет пишущий их человек. \n\nНапример, самый примитивный лог предтавляет собой обычный текст\n```log\nServer is running on PORT: 3000\n```\nБолее продвинутый лог может содержать время с часовым поясом, имя файла или класса, тип события, текст и так далее\n```log\nJan 4, 2022 17:46:27 (UTCMSK) | index.js | SUCCESS | App is running on the port: 8080\nJan 4, 2022 22:00:15 (UTCMSK) | dbContext.js | ERROR | Connection to MongoDB was interrupted!\n```\n\nЛоги могут выводиться в консоли, записываться в базу данных или в файл. Поскольку события в системе происходят довольно часто, со временем логов может накопиться слишком много. Поэтому логи наделяют сроком годности (скажем, месяц), а затем удаляют их.\n\n**Логером** называют сервис, который позволяет записывать логи. Существует множество библиотек логеров, но его также несложно написать самому.\n\nЛогирование очень важно, поскольку оно отражает историю событий в системе. Оно позволяет отслеживать любые интерисующие вас действия в системе. И при возникновении ошибки вы всегда можете знать, какое действие предшествовало этой ошибке и в чём ошибка заключалась. \n\nВсегда довольно неприятно, когда в какой-либо программе появляется ошибка, а в консоли пусто или написано что-то вроде `ERROR CODE 110050` без всяких пояснений и без возможности найти описание ошибки в интернете. Поэтому давайте писать подробные, отчётливые логи, которые будут понятны и помогут вам или вашему товарищу по проекту быстро найти и исправить проблему.\n\n## Об аналитике\n\nАналитика похожа на логирование тем, что работает с событиями, но она имеет немного другую цель. \n\nЛоги нацелены на то, чтобы сохранить историю того, что происходило в системе. Аналитика направлена на то, чтобы отследить, какие действия были совершены пользователем.\n\nЛоги становятся бесполезными через некоторый, достаточно короткий промежуток времени. Аналитика, наоборот, стремится получить статистику действий пользователей: на какие страницы заходят чаще, какие товары чаще просматриваются и покупаются, какие кнопки больше всего кликаются. Эта статистика позволяет бизнесу понять, что пользователям интереснее всего. На основании этой статистики можно делать рекламу, предложения пользователю. \n\nЕсли на проекте используется Server Side Rendering (SSR), то аналитика может подсказать, какие страницы стоит загрузить в первую очередь, поскольку велика вероятность, что именно на них пользователь зайдёт скорее всего (а время отклика играет большую роль, когда речь идёт о вебсайтах).\n\n## Об отладке\n\n## О валидации\n\n## О тестировании\n\nАвтоматизированное тестирование подобно лабораторным анализам или техосмотру. \n\n<!-- Визуально вы можете не видеть проблему, но внутри вас, скрытно от глаз может что-то происходить, о чём вы не подозреваете. -->\n\nДействительно, врачи советуют в качестве профилактики многих заболеваний ежегодно сдавать некоторые примитивные лабораторные анализы и не менее базовые обследования (анализ крови, УЗИ, флюрография и так далее). Эти анализы и обследования позволяют убедиться, что с вашим организмов сейчас всё в норме.\n\nВодителям необходимо проходить ежегодный техосмотр, который позволяет убедиться, что ничего страшного с техникой за год не произошло и можно смело продолжать на ней ездить.\n\nЧем быстрее вы находите отклонение в вашем организме или неисправность в вашей технике, тем больше шансов, что вы сможете исправить её с малыми затратами. \n\nИ далеко не всегда такие вещи можно рассмотреть невооружённым глазом, иногда требуется специальное оборудование, некоторый инструмент.\n\n<!-- Тем не менее, иногда находится что-то, о чём мы не знаем, что визуально не видно. И обычно чем раньше вы это находите, тем проще это исправить. -->\n\nКогда дело касается приложений, таким инструментом выступают автоматизированные тесты. Они позволяют проверить, соответствует ли приложение всем указанным требованиям на данный момент.\n\nЧем шире покрытие приложения тестами, чем детальнее тесты описывают требования бизнеса, тем больше шансов быстро и безболезненно найти и устранить отклонение в коде.\n\nКогда вы приступаете к написанию тестов, вы начинаете продумывать все возможные варианты (англ. `edge cases`), исходы, случаи. И на каждый случай пишете отдельный тест. Затем все тесты запускаются последовательно. И те, которые провалились, указывают на проблему.\n\n<!-- Иногда еория вероятностей и комбинаторика позволяют  -->\n\nЭто тем более важно, поскольку каждый новый кусочек кода может непредсказуемо повлиять на весь предыдущий код. Чем больше кода написано за всё время, тем больше потенциальных багов и тем больше времени необходимо на поиск бага (дебаг). И чтобы не проверять каждый раз все возможные случаи вручную, достаточно один раз их описать в виде тестов и время от времени их запускать (или настроить автоматический запуск тестов при каждом пуше или деплое).\n\n\n<!--  И именно запуск тестов позволяет убедиться, что ничего не сломалось, что приложение по-прежнему удовлетворяет всем требованиям. -->\n\n<!-- Если после каждого нового обновления не делать проверки, то шанс появления непредсказуемого бага, который повлечёт за собой многочасовой безудержный дебаг, существенно возрастают. -->\n<!-- \nВсё это позволяет быть уверенным, что с вашим о -->\n\n<!--  и тем больше шансов, что удастся найти какое-то отклонение от первоначального задания. -->\n<!--   из технического задания -->\n\n## Об обработке ошибок\n\n## О безопасности\n\nСледует помнить, что под безопасностью подразумевается не только безопасность приложения, но и безопасность любого пользователя, которому довелось это приложение использовать.\n\n**Безопасность** (англ. `safety`) - состояние защищённости кого-либо или чего-либо.\n\nПриложение в безопасности, если злоумышленники не могут подвернуть его взлому.\n\nПриложение безопасно для пользователя, если данные пользователя, который пользуется данным приложением, находятся в безопасности и не могут быть украдены ни создателем сайта, ни злоумышленниками.\n\nБезопасность является довольно обширной темой, которая в каждом приложении раскрывается по-разному, но я постараюсь привести список довольно часто встречающихся моментов.\n\n### Некоторые общие правила безопасности\n1) Не храните важные данные (пароли, ключи, номера карточек и так далее) в Local Storage.\n2) Если в системе есть логгирование, следует отключить логирование тех параметров запросов, которые содержат введённые имя пользователя и пароль.\n3) Не храните ключи, пароли и другие важные данные в константах, в свойстве `scripts` . Используйте для этих нужд `dotenv` или другие библиотеки, работающие с переменными окружения.\n4) Регулярно делайте `npm audit`, обновляйте пакеты, в которых были обнаружены уязвимости.\n5) Не сохраняйте пароль пользователя в явном виде в базу данных. Вместо этого сохраняйте хэш, который получается из настоящего пароля и соли при использовании `bcrypt`.\n6) Не позволяйте пользователям системы выбирать пароль, состоящий менее, чем из 8 символов латинского алфавита. Такие пароли будет сложнее подобрать.\n7) Для аутентификации/авторизации старайтесь использовать сторонние сервисы (например, Auth0, Google Sign-In, Github и другие). При соблюдении требуемых этими сервисами условий вы можете быть уверены, что ваши пользователи находятся в безопасности.\n8) Используйте протокол HTTPS вместо HTTP, если сайт находится в публичном доступе (доступен не через localhost). HTTPS шифрует данные тела запроса, что не позволяет злоумышленникам перехватить ценную информацию.\n9) Не используйте формат `md5` для шифрования каких-либо важных данных. Этот формат можно с лёгкостью перевести в обычный текст, не имея при этом никакого ключа. Для этого существует множество сервисов в интернете.\n10) Не используйте примитивные пароли для админки приложения (по типу `admin - admin`, `admin - qwerty12345789`, `username - password`)\n11) По возможности используйте двухфакторную аутентификацию. Так, если злоумышленник украдёт или подберёт ваш пароль, он всё равно не сможет войти без телефона, который будет всегда при вас.\n\n##\n"
  },
  {
    "path": "Programming.md",
    "content": "\n- [Работа со строками](#работа-со-строками)\n- [Регулярные выражения](#регулярные-выражения)\n- [Семиотика, синтаксис и семантика](#семиотика-синтаксис-и-семантика)\n \n# Работа со строками\n- [Конкатенация](#конкатенация)\n\n## Конкатенация\n\n# Регулярные выражения\n- [О регулярных выражениях](#о-регулярных-выражениях)\n- [Классы символов](#классы-символов)\n\n## О регулярных выражениях\n\n**Регулярное выражение** (англ. `regular expression`, `regex`, `regexp`) - это **поисковый шаблон** (англ. `search pattern`), который *позволяет* по *заданным* в нём *правилам* *найти подстроку* (некоторую *последовательность символов*) в некоторой *строке*. \n\nТаким образом, *основной целью использования регулярного выражения* является *проверка на соответствие* (англ. `match`) некоторой *строки заданному шаблону*. \n\nДля *написания поискового шаблона* используется специальный *формальный язык*, который *поддерживается* во многих *системах* и *языках программирования*.\n\n### Регулярное выражение позволяет задавать в шаблоне\n* *конкретные символы* алфавита,\n* *класс символов* (цифры, буквы, знаки пунктуации),\n* *промежуток символов*, которые должны быть *включены* в поиск или должны быть *исключены* из него,\n* *количество символов заданного вида*,\n* *логические условия* и многое другое.\n\nВсё это мы *рассмотрим далее* на *примерах*.\n\n### Когда регулярные выражения могут быть полезны\n* при *поиске*: для *включения* удовлетворяющих шаблону *строк* в некоторую *выборку*\n* при *валидации* строки: проверка сложность пароля, проверка правильности введённого номера телефона или электронной почты\n* при *необходимости обрабатывать строку по-разному* в *зависимости* от того, что она *содержит*.\n\n### Простейшее регулярное выражение\nПодобно тому, как *строки* обычно *заключаются* в *одинарные*, *двойные* или *косые ковычки*:\n```javascript\n'hello'\n\"hi\"\n`welcome`\n```\n*регулярные выражения размещают между двумя косыми чертами* `/` (англ. `slash`):\n```regex\n/hey/\n```\nЭто *позволяет интерпретатору (компилятору) отличить регулярное выражение* от *других выражений*. \n\n*Самим простым поисковым шаблоном* является *конкретная последовательность символов*. \n\nНапример, выясним, *какие* из *указанных строк содержат подстроку* `\"ay\"`. *Регулярное выражение* примет вид `/ay/`.\n```js\n'Ray Palmer'.match(/ay/); // ['ay', index: 1] (начинается с 1-ого символа)\n'Dorian Gray'.match(/ay/); // ['ay', index: 9] (начинается с 9-ого символа)\n'Max Starling'.match(/ay/); // null (соответствий не найдено)\n```\n\n## Перечисление допустимых символов\n\n### Перечисление символов\n\nРегулярное выражение позволяет *перечислить допустимые символы*. Для этого используются **квадратные скобки** `[]` (англ. `square brackets`. *Читается* как *\"любой символ из символов ...\"*.\n\nНапример,\n* регулярному выражению `[abc]` удовлетворяет каждый из символов `a`, `b`, `c` и никакой другой символ,\n* регулярному выражению `[13579]` удовлетворяют только символы `1`, `3`, `5`, `7`, `9`\n```js\n'May'.match(/[abc]/) // ['a', index: 1]\n'#ff00ff'.match(/[0123456789]/) // ['0', index: 2]\n```\n\n### Исключение перечисленных символов\n\nДля *исключения* из *регулярного выражения символов* используется символ **карет** `^` (англ. `caret`) *внутри квадратных скобок*. *Читается* как *\"любой символ кроме символов из интервала\"*.\n\n### Включение символа из интервала символов\n\nДля *включения* в *регулярное выражение* *символов* из *некоторого интервала символов* используется символ `-` (англ. hyphen) внутри *квадратных скобок*. *Читается* как *\"любой символ из интервала ...\"*.\n\nНапример,\n* *регулярное выражение* `/[0-9]/` *удовлетворяет любому* из *символов* `0, 1, 2, 3, 4, 5, 6, 7, 8, 9` (то есть *любой цифре*),\n* *регулярное выражение* `[A-Z]` - *любой большой букве латинского алфавита*,\n* *регулярное выражение* `[a-z]` - *любой маленькой букве латинсткого алфавита*,\n* а *выражение* `[А-я]` - *любой букве* (*большой* или *маленькой*) русского алфавита*\n```js\n'Hello'.match(/[A-Z]/) // ['H', index: 0]\n'Hi'.match(/[a-z]/) // ['i', index: 1]\n'Hey'.match(/[0-9]/) // null\n'Greetings user2000'.match(/[0-9]/) // ['2', index: 14]\n```\n\n### Исключение символа из интервала символов\n\nДля *исключения* из *регулярного выражения символов* из *некоторого интервала* так же используется символ **карет** `^` (англ. `caret`).\n\nНапример,\n```js\n'Greetings user2000'.match(/[^0-9]/) // ['G', index: 0]\n\n'15,000 dollars'.match(/[0-9]/) // ['1', index: 0]\n'15,000 dollars'.match(/[^0-9]/) // [',', index: 2]\n\n'GitHub'.match(/[^A-z]/) // null\n```\n\n ### Комбинация перечислений символов\n\nНапример, *регулярное выражение* `[A-Za-z0-9]` удовлетворяет *любой букве латинского алфавита или цифре*, а *регулярное выражение* `[^A-Za-z0-9]` удовлетворяет *любому символу*, который *не является буквой латинского алфавита и числом*.\n```js\n\"Hey! How is it going?\".match(/[A-Za-z0-9.]/) // ['H', index: 0]\n\"Hey! How is it going?\".match(/[^A-Za-z0-9]/) // ['!', index: 3]\n```\n\n\n## Классы символов\n\n**Классы символов** (англ. `character classes`) чем-то *напоминают типы данных*: они ограничивают область допустимых значений (в данном случае, область допустимых символов).\n\n# Семиотика, синтаксис и семантика\n\nГоворя простыми словами, **язык программирования** - это средство коммуникации между программистом и компьютером. С помощью него программист может \"рассказать\" компьютеру, что нужно сделать (исполнить, выполнить) последнему.\n\nВ любой системе, где используются знаки, выделяют \n\n Рассматривая любую знаковую систему (в том числе и язык программирования), обычно выделяют синтаксис -- правила построения сообщений в этой системе, семантику -- правила истолкования сообщений тем, кому они адресованы, а также прагматику, сопоставляющую сообщения желаниям того, от кого они исходят.\n"
  },
  {
    "path": "ProgrammingLanguageCharacteristics.md",
    "content": "# Оглавление\n- [О характеристиках языков программирования](#о-характеристиках-языков-программирования)\n- [Типизация](#типизация)\n- [Компилируемость и интерпретируемость](#компилируемость-и-интерпретируемость)\n- [Потоки, однопоточность и многопоточность](#потоки-однопоточность-и-многопоточность)\n- [Синхронность и асинхронность](#синхронность-и-асинхронность)\n- [Кроссплатформенность и нативность](#кроссплатформенность-и-нативность)\n- [Поддержка парадигм программирования](#поддержка-парадигм-программирования)\n<!--\n  Работа на различных платформах, работа в различных браузерах\n- [Синтаксис](#синтакс)\n-->\n\n## Потоки, однопоточность и многопоточность\n\n**Потоком выполнения**, **тредом** (англ. `a thread of execution`, `thread`) называют *наименьший набор инструкций*, который может быть *независимо обработан диспетчером операционной системы* (англ. `scheduler`).\n\n**Однопоточным** (англ. `single-threaded`) называют *язык программирования*, который имеет *лишь один поток выполнения*, называемый **основным потоком** (англ. `main thread`). \n\n*Примером однопоточного языка* является *JavaScript*.\n\n*Наличие одного потока означает*, что *в один момент времени* может исполняться *только одна операция*.\n\nЕсли *язык программирования использует несколько потоков выполнения*, то его называют **многопоточным** (англ. `multi-threaded`).\n\nОбычно многопоточные языка нацелены на серверную разработку для того, чтобы можно было распределять трудоёмкие вычисления (операции ввода-вывода, которые могут занимать продолжительное время) между несколькими потоками. \n\nПримеры многопоточных языков: Java, Go, C#, Rust.\n\nБлагодаря асинхронной среде выполнения NodeJS, язык JavaScript так же может выполняться на сервере, а его создатели считают, что разработка с использованием нескольких потоков неэффективна и довольно сложна. \n\nИспользование нескольких потоков заставляет решать программистов проблемы параллелизма (concurrency issues), в том числе проблему блокировки потоков (dead-locking): только один поток может использовать определённый ресурс в один момент времени, другие потоки должны ожидать освобождения ресурса.\n\n- [Операции ввода-вывода](#операции-ввода-вывода)\n- [Синхронное и асинхронное программирование](#синхронное-и-асинхронное-программирование)\n- [Блокирующие и неблокирующие операции ввода-вывода](#блокирующие-и-неблокирующие-операции-ввода-вывода)\n\n### Операции ввода-вывода\n\nОперациями **ввода-выводы** (англ. `input/output`, `I/O`) называют *взаимодействие* некоторого *обработчика информации* (*компьютера*, *системы*) с *окружающим миром* (*человеком* или другим *компьютером*).\n\n**Ввод** (англ. `input`) — любые *данные* или *сигналы*, которые *получает система*. \n\n*Ввод* можно рассматривать как *команду*, которую *получает система*. Такую *команду* можно *задать программным кодом* на некотором *языке программирования*. \n\nНапример, *ниже* приведён *пример создания текстового файла* `README.md`, который *содержит строку* `Notes` и имеет *кодировку* `Unicode`.\n```js\nconst fs = require('fs');\nfs.writeFileSync('./README.md', 'Notes', 'utf-8');\n```\n\n**Вывод** (англ. `output`) — любые *данные* или *сигналы*, которые *система отправляет окружающему миру*.\n\nНапример, *отображение информации* на *экране* или в *консоли* являются *операциями вывода*.\n\n*Ниже* представлен *пример вывода содержимого файла*, который был *создан* в *примере выше*.\n```js\nconst fs = require('fs');\nconst data = fs.readFileSync('./NodeJS.md', 'utf-8');\nconsole.log(data.toString()); // 'Notes'\n```\n\nИтак, создание переменных, арифметические операции, работа со строками, сравнения - подобные инструкции являются операциями ввода. Любая реакция системы, которую может увидеть пользователь, - операция вывода.\n\n<!-- Фактически, любые операции (действия) в коде можно рассматривать как операции ввода-вывода (создание переменных, арифметические операции, логирование, отправка данных из одного места в другое и так далее). -->\n\n<!-- Но в *контексте NodeJS* *понятие операций ввода-вывода относятся* к *работе с диском* (файлами), *сетью* (network, HTTP-запросы), *базой данных*, *DNS*. Эти *операции* достаточно *трудоёмкие* и *выполняются за пределами JavaScript* такими *библиотеками*, как `libuv`. Такие операции являются достаточно медленными по сравнению вычислениями при помощи CPU. -->\n\n\n\n## Блокирующие и неблокирующие операции ввода-вывода\n\n**Блокировка** (англ. `blocking`) — ситуация, при которой *следующий блок кода не может быть запущен*, поскольку *основной поток занят выполнением* (ожиданием выполнения) *предыдущего блока кода*.\n\nОперации, которые приводят к *блокировке основного потока* называют **блокирующими операциями ввода-выввода** (англ. `blocking I/O`). Обычно к ним относят *трудоёмкие синхронные операции ввода-вывода*.\n\n<!-- Например, следующий код NodeJS работает с файлами синхронно.\n```js\nconst fs = require('fs');\n\nfs.writeFileSync('./input.txt', 'Hello!', 'utf8');\nconst data = fs.readFileSync('./input.txt', 'utf8');\nconsole.log(data);\nfs.unlinkSync('./input.txt');\n```\n\n*Логирование* также является *блокирующей операцией ввода-вывода*, поскольку *затрагивает системный поток ввода-вывода*, но такая операция не занимает слишком много времени, а асинхронный вариант было бы совсем не удобно использовать.\n```js\nvar a = 1;\nconsole.log(a); // 1 (асинхронный вариант мог бы вывести здесь 2).\na += 1;\n```\n\n<!-- В браузере основной поток может быть заблокирован функцией `alert` до тех пор, пока пользователь не закроет всплывшее окно. -->\n<!-- \nОперации ввода-вывода, которые не блокируют основной поток, называют **неблокирующими операциями ввода-вывода** (Non-blocking I/O). К ним обычно относят *асинхронные операции ввода-вывода*.\n\nJavaScript-код выполняется довольно быстро и считается неблокирующим. Тем не менее, такая блокировка возможно, если запущен код, который выполняется бесконечно (бесконечный цикл, бесконечная рекурсия) или очень долго (цикл до 1000000). \n\nНапример, код ниже никогда не завершится. \n```js\nwhile (true) { /* ... */ }\n```\n\nJavaScript в браузере и NodeJS на сервере ориентированны на события. На клиенте важно быстро реагировать на действия пользователя, на сервере — отвечать на запросы клиента. Нельзя терять время на ожидание завершения операций, которые выполняются вне основного скрипта. Поэтому очень важно стремиться использовать асинхронные операции: они не блокируют основной поток, позволяя обрабатывать другие события.\n\nНапример, есть запрос, который обрабатывается 100 миллисекунд, из которых 80 уходят на выполнение операций, которые могут блокировать основной поток. В синхронном режиме эти 80 миллисекунд сервер простаивает, в асинхронном он может потратить это время на обработку других запросов. \n\nТем не менее, просто сделать все функции асинхронными не получится, некоторые операции всё же должны выполняться последовательно. Если заменить синхронность на асинхронность в примере выше, то удаление файла может начаться раньше, чем закончится чтение, а вывод результата — раньше его получения. \n```js\nwriteFile('./input.txt', 'Hello!', 'utf8');\nconst data = readFile('./input.txt', 'utf8');\nconsole.log(data);\nunlink('./input.txt');\n```\nТут требуется особый подход: использование *функции обратного вызова*. -->\n\n## Синхронное и асинхронное программирование\n- [О синхронном программировании](#о-синхронном-программировании)\n- [Об асинхронном программировании](#об-асинхронном-программировании)\n\n### О синхронном программировании\n\n**Синхронным программированием** (англ. `synchronous programming`) называют такое *поведение языка программирования*, при котором *операции* (*инструкции*) в некотором *блоке кода* выполняются *последовательно* (*синхронно*), то есть в *порядке* их *указания* в *коде*.\n\nПри таком подходе *следующая операция не может быть запущена*, *пока не завершится текущая* операция.\n\n**Синхронная операция ввода-вывода** (англ. `synchronous I/O`, `sync I/O`) — *операция ввода-вывода*, которая *выполняется синхронно*. *Основной поток дожидается окончания выполнения* такой *операции* и *только потом запускает следующую операцию*.\n\n<!-- Все базовые операции выполняются синхронно (логирование, арифметические операции, циклы, вызовы функций и другое). -->\n\n*Большинство операций* являются *синхронными вне зависимости* от *языка программирования*. Например, *синхронными* являются *операции*:\n* Арифметические, логические и строковые операции.\n* Операции присваивания `=` и сравнения `<,>,=`.\n<!-- * Создание переменных, классов, объектов, функий. -->\n* Вызов функций и методов `()`.\n* Вывод на консоль.\n\n*Конструкции* `if..else`, `switch`, `while`, `for`, `for..of` также *работают синхронно*.\n\nНапример, *код ниже* на языке *JavaScript выполнится синхронно*.\n```js\nconsole.log('start');\nconst sum = (a, b) => a + b;\nconsole.log(sum(1, 7));\nconsole.log('end');\n/*\n> 'start'\n> 8\n> 'end'\n*/\n```\n\n### Об асинхронном программировании\n\nВ *широм смысле* под **асинхронной операцией** понимают *операциию*, которая *выполнится в будущем*, *не прямо сейчас*. \n\n**Асинхронным программированием** (англ. `synchronous programming`) называют такое *поведение языка программирования*, при котором *определённые операции* в некотором *блоке кода* выполняются *асинхронно*, то есть *основной поток не дожидается* их *завершения* и *приступает* к *выполнению следующих задач*.\n\n**Асинхронная операция ввода-вывода** (англ. `asynchronous I/O`, `async I/O`) — операция ввода-вывода, выполнение которой не препятствует запуску следующей операции.\n\n#### Какие операции делают асинхронными в синхронном языке программирования?\nТакие операции, выполнение которых может занять продолжительное время, а именно:\n* Работа с *файловой системой* (англ. `file system`). Например, *запись в файл* и *чтение из файла*.\n* Работа с *сетью* (англ. `networking`), чаще всего это `HTTP`-запросы, но также могут использоваться `TCP`, `UDP`, вебсокеты (англ. `websockets`).\n* Работа с *базами данных*.\n* *Таймеры* и другие *планируемые задачи*.\n\n#### Как реализуется асинхронность?\n\nПри помощи функций обратного вызова. В них содержится набор инструкций, который должен быть выполнен после того, как выполнена трудоёмкая операция. \n\n<!-- позволяющая во время своего выполнения продолжить выполнение других задач. Выполнение такой операции не препятствует запуску следующей операции. -->\n\n\n### Пример асинхронного кода\n\nНиже представлен *пример асинхронной функции* `setTimeout`, которая *создаёт таймер* и *по истечению указанного времени* вызывает *функцию обратного вызова* `() => console.log('2')`.\n\n```js\nconsole.log('1');\nsetTimeout(() => console.log('2'), 0);\nconsole.log('3');\n/*\n> 1\n> 3\n> 2\n*/\n```\n\nКак можно заметить, `setTimeout` не припятствует вызову следующего `console.log()`. *Код выполнился асинхронно (не последовательно)*.\n\n<!-- В следующем примере результат будет `1, 3, 2` несмотря на порядок в коде. Выполнение `setTimeout()` происходит асинхронно и не препятствует запуску следующего `console.log()`.  -->\n\n\n<!-- NodeJS предоставляет большинство операций ввода-вывода в синхронном (оканчиваются на `Sync`) и асинхронном вариантах. -->\n\n\n# Типизация\n- [О системе типов и типизации](#о-системе-типов-и-типизации)\n- [Статическая и динамическая типизация](#статическая-и-динамическая-типизация)\n- [Слабая и сильная типизация](#слабая-и-сильная-типизация)\n- [Явная и неявная типизация](#явная-и-неявная-типизация)\n\n## О системе типов и типизации\n- [Система типов](#система-типов)\n- [Ошибка типа](#ошибка-типа)\n- [Типизация и её виды](#типизация-и-её-виды)\n\n### Система типов\n*Рекомендуется почитать* о [**типах данных**](./DataTypes.md).\n\nВ *рамках изучения языков программирования* существует *понятие системы типов*.\n\n**Система типов** (англ. type system) - такая *логическая система*, которая *связывает* некоторую *переменную* (*область памяти*, хранящую *данные*) с *определённым* **типом данных**. Эта *связь* означает, что после *установления типа переменная приобретает множество допустимых значений* и *ограниченный набор операций* над этими *значениями*. \n\nНапример, для *переменной числового типа* могут быть *доступны операции инкремента* и *возведения в степень*: `x++`, `x^2`, а для *переменной строкового типа* - *операции поиска* и *получения подстроки*: `str.find(/* ... */)`, `str.substring(/* ... */)`.\n\n### Ошибка типа\n\n*Попытка выполнить операцию* над *типом данных*, которая *выходит за пределы допустимых операций* данного *типа*, обычно *приводит* к **ошибке типа** (англ. type error). Например, *нельзя выполнить разность строк* или *поиск подстроки в числе*. \n\nВо многих языках программирования также нельзя выполнить бинарную операцию с операндами разных типов данных. Например, в Java нельзя сложить строку и число, при этом в JavaScript это допустимо, поскольку в этом случае происходит приведение операндов к одному типу.\n\n### Типизация и её виды\n\n*Язык программирования*, *использующий систему типов*<!--  *обладающий* своей собственной *системой типов* -->, называется **типизированным языком** (англ.typed language).\n\nВ более *широком смысле* слова под **типизацией** (англ typing) подразумевают *классификацию* по *типам*. Конкретно же в *рамках* изучения некоторого *языка программирования* под *типизацией* подразумевают то, *каким образом система типов* этого языка *обрабатывает типы данных*.\n\n*Типизированный язык программирования* может иметь:\n- [Статическую или динамическую типизацию](#статическая-и-динамическая-типизация)\n- [Слабую или сильную типизацию](#слабая-и-сильная-типизация)\n- [Явную или неявную типизацию](#явная-и-неявная-типизация)\n\nНапример, *JavaScript* является *типизированным языком* и имеет *динамическую*, *слабую*, *неявную типизацию*.\n\n## Статическая и динамическая типизация\n\n*Типизированный язык* в определённый момент времени производит **проверку типа** (англ. type checking). \n\n<!-- о типобезопасности -->\n\n*Проверка типа* может *производиться* во *время компиляции* (англ. compile time) или в *режиме реального времени* (англ. run-time), то есть *по ходу выполнения программы*.\n\nПри **стратической типизации** *типы устанавливаются* на *этапе компиляции*. К *моменту выполнения программы* они уже *установлены* и компилятор знает, где какой тип находится.\n\nПример языков со *статической типизацией*: *Java*, *C#*.\n```java\n/* Java */\npublic class Notes {\n  public static void main(String []args){\n    int number = 1; // числовой тип\n    number = true; // error: incompatible types: boolean cannot be converted to int\n  }\n}\n```\n\nПри **динамической типизации** *типы определяются во время работы программы*.\n\nПример языков с *динамической типизацией*: *Python*, *JavaScript*.\n```js\n/* JavaScript */\nlet a; // тип неизвестен\na = 1; // числовой тип\na = true; // логический тип\n```\n\n## Слабая и сильная типизация\n\nПри **слабой** (нестрогой) **типизации** *автоматически* выполняется множество *неявных преобразований типов* даже при условии *неоднозначности преобразования* или возможности *потери точности данных*.\n\nПример языка со *слабой типизацией*: *JavaScript*.\n```js\n/* JavaScript */\nconsole.log(1 + [] + {} + 'notes'); // \"1[object Object]notes\"\nconsole.log(1 - []); // 1\n```\n\nПри **сильной** (строгой) **типизации** в выражениях *не разрешено смешивать различные типы*. *Автоматическое неявное преобразование не производится*. \n\nПример языков с *сильной типизацией*: *Java*, *Python*.\n\nНапример, *нельзя сложить число* и *массив*.\n```java\n/* Java */\npublic class Notes {\n  public static void main(String []args){\n    int number = 17;\n    int array[] = new int[3];\n    System.out.println(number + array); // error: bad operand types for binary operator '+'\n  }\n}\n```\n\n## Явная и неявная типизация\n\nПри **явной типизации** *тип* новых *переменных*, *функции*, их *аргументов* и *возвращаемых* ими *значений* нужно задавать *явно*. \n\nПример языков с *явной типизацией*: *C++*, *C#*.\n\n```cpp\n/* C++ */\nint sum(int a, int b) {\n    return a + b;\n}\n```\n\nПри **неявной типизации** эта *задание типов* производится *автоматически компиляторами* и *интерпретаторами*.\n\nПример языка с *неявной типизацией*: *JavaScript*.\n\n```js\nlet a; // неизвестно, какого типа будет значение переменной\na = 17;\na = 'Notes';\na = () => {};\na = null;\n```\n```js\nfunction fn (a, b) { a + b } // неизвестно, какого типа параметр функции и что она возвращает\nfn(1, 7) // 8\nfn (1, '7') // 17\n```\n\n## Кроссплатформенность и нативность\n<!-- кроссбраузерность -->\n\n**Кроссплатформенностью программного обеспечения** (англ. `crossplatform software`) называют *способность* программного обеспечения *работать* на *нескольких аппаратных платформах* (поддерживаются *разными типами процессоров*) или *операционных системах* (`Windows`/`MacOS`/`Linux`).\n\n**Нативностью программного обеспечения** (англ. `native software`) называют программное обеспечение, которое было написано для конкретной аппаратной платформы, операционной системы. Такое ПО может быть запущенно только на своей платформе, на другой платформе такое ПО можно запустить только при помощи эмулятора, что обычно приводит к значительному снижению производительности.\n\n**Нативные приложения** (англ. `native application`, `native app`) пишут на **нативном языке программирования** (англ. `native programming languare`, то есть на \"родном\" (*характерном* для *платформы*) языке. Поэтому нативные приложения обычно отличаются очень хорошей совместимостью и производительностью, так как используют максимум из преимуществ своей целевой платформы.\n"
  },
  {
    "path": "README.md",
    "content": "# Заметки программиста\n<!--\nMy simple notes about everything related to programming.\n-->\n\nМои *конспекты* обо *всём*, что связано с *информационными технологиями*  (англ. `information technology`, `IT`), *программированием* и *смежными* ему *областями*.\n\n## Оглавление\n- [Почему я решил всё это написать](#почему-я-решил-всё-это-написать)\n- [Как подготовиться к собеседованию по этим заметкам](#как-подготовиться-к-собеседованию-по-этим-заметкам)\n- [Общая теория](#общая-теория)\n  - [Дискретная математика](#дискретная-математика)\n  - [Информация и данные](#информация-и-данные)\n  - [Типы данных](#типы-данных)\n  - [Структуры данных и алгоритмы](#структуры-данных-и-алгоритмы)\n  - [Модели данных и базы данных](#модели-данных-и-базы-данных)\n  - [Программирование](#программирование)\n  - [Тестирование](#тестирование)\n  - [Архитектурные стили и архитектурные паттерны](#архитектурные-стили-и-архитектурные-паттерны)\n  - [Паттерны проектирования и принципы проектирования](#паттерны-проектирования-и-принципы-проектирования)\n- [Технологии, библиотеки, фреймворки и языки программирования](#технологии-библиотеки-фреймворки-и-языки-программирования)\n  - [CSS](./CSS.md)\n  - [Docker](./Docker.md)\n  - [Elasticsearch](./Elasticsearch.md)\n  - [Git](./Git.md)\n  - [JavaScript](./JavaScript.md)\n  - [Сравнение Flux, Redux, Vuex и Mobx](./Flux-Redux-Vuex-Mobx.md)\n\n## Почему я решил всё это написать\n\nПоскольку я *всей душой за Open Source*, я хочу в *открытом доступе поделиться* с *вами* всеми моими *познаниями*, *моим опытом*, *моим виденьем* всего, с чем мне *доводилось сталкиваться* за *последние годы работы*, что *можно было написать словами* и *о чём мне хотелось писать*. К данным *заметкам* я *стараюсь подходить* с *практической точки зрения*, поскольку *большая часть материалов* была *подготолена* на *основании теоритических* и *практических вопросов*, которые мне *доводилось встречать* как на *собеседованиях*, так и при *непосредственной работе* на *проектах*.\n\n> *Человеческая память* - это *самое ненадёжное хранилище информации*.\n\n*Никогда не знаешь, что и когда оттуда пропадёт*. *Ни в чём нельзя быть уверенным*, если *дело касается воспоминаний*. Как бы ты хорошо не разобрал какой-то материал, твоя уверенность в своих знаниях касательно него тает спустя несколько лет без практики, без перечитывания, возвращения к материалу.\n> *Самая лучшая память находится на кончике карандаша*\n\n> То, что нам *покорилось однажды*, обычно намного *проще* и *быстрее можно наверстать* по *сравнению* с тем, сколько *времени* и *сил* мы *затрачиваем на это* при *первом знакомстве*.\n\nДаже *если* порой *кажется*, что мы *забыли что-то безвозвратно*, *одно слабое упоминание*, *один мимолётный триггер* может *помочь восстановить почти достоверную картину*.\n\nИтак, *изначальная цель данного проекта* - это *сохранение моих мыслей, сравнений, умозаключений* в *моменты наивысшего подъёма* в *какой-либо области* (в *каком-либо домене*), когда и *основательнее всего подошёл* к *разбору материалов*. Это *позволяет* в *любой момент времени* (через неделю, месяц или даже через 5 лет) *практически мгновенно найти решение проблемы*, которую *я уже решал*, или *ответ на вопрос*, на который *мне уже доводилось отвечать* (*себе* или *кому-либо ещё*). *Ускоряет этот процесс* ещё и то, что *материал однороден* (ведь *всё написано одни человеком и на одном языке*) и *имеет структуру*, *оптимальную для повторения*.\n\n*В какой-то момент* я *осознал*, что этот *мысли и опыт можно запечатлить таким образом*, чтобы *их могли перенять и другие*. *С тех пор я всегда стараюсь придерживаться такой формы повествования*: *пишу не для себя* - *стараюсь писать доступно для всех*.\n\n## Как подготовиться к собеседованию по этим заметкам\n\n[*Примерный список вопросов* по этим *конспектам* я *прикладываю* здесь](https://github.com/Max-Starling/Notes/blob/master/InterviewQuestions.md), чтобы *читатель* мог *проверить свои знания* и *убедиться*, что я *пишу многие материалы не просто так*, а потому что *такие вопросы уже встречались*. *Ответы будут прилагаться* в *виде ссылок* на *отдельные параграфы* из *заметок по мере их написания*.\n\n*Стоит* также *отметить*, что *некоторые вещи покоряются лишь с опытом, с практикой, спустя некоторое время* (например, *абстракция*, *паттерны проектирования*, *принципы по типу SOLID*, *архитектуры приложений*). И это *не беда*, если у вас *сразу что-то постигнуть не получается*. Возможно, *время ещё не пришло*. *Главное* - *не стоит сдаваться* и *отчаиваться*. Просто *идите вперёд так, как можете и умеете сейчас*! А я *постараюсь облегчить* этот *путь* так, как только смогу.\n\n## Общая теория\n\n### [Дискретная математика](./DiscreteMath.md)\n***«Дискретная математика*** занимается *изучением* ***дискретных** (конечных) математических **структур*** - таких *структур*, *количество элементов* которых *конечно*, а значит эти *элементы* *можно пересчитать, перечислить*. Из-за *ограниченности ресурсов* *компьютера* на нём *можно реализовывать (рассматривать) только дискретные структуры*. Именно эти *структуры* легли в *основу компьютера*, стали *фундаментом* при его *создании»*. \n<!--Поскольку при работе с компьютером только такие объекты и могут рассматриваться, дискретная математика особенно важна для программиста-->\n- [Введение](./DiscreteMath.md#введение)\n- [Теория множеств](./DiscreteMath.md#теория-множеств)\n- [Теория мультимножеств](./DiscreteMath.md#теория-мультимножеств)\n- [Теория графов](./DiscreteMath.md#теория-графов)\n- [Математическая логика](./DiscreteMath.md#математическая-логика)\n- [Комбинаторика](./DiscreteMath.md#комбинаторика)\n\n### [Информация и данные](./Data.md)\n\n<!-- ***«Данными*** называют *цифровое представление информации*, то есть такую *последовательность нулей и единиц*, которую *ЭВМ может интерпретировать* как *число*, *текст*, *изображение*, *видео* или что-либо ещё. В *данном разделе* мы узнаем, как можно *хранить* и *обрабатывать данные»*. -->\n\n*«Всё*, что мы *видим* и *слышим* каждый день, *несёт* в себе какую-то ***информацию***. По сути говоря, вся наша *жизнь связана* с *получением*, *обработкой* и *передачей информации*. ***Данные*** - это *цифровая оболочка* *информации*, *последовательность нулей и единиц*. *Представить информацию* как *данные означает* дать возможность *компьютеру обрабатывать её*. *Данные* могут быть *представлены текстом*, *изображением*, *видео*, *аудио* и *другими способами»*.\n- [Оглавление](./Data.md#оглавление)\n- [Информация и данные](./Data.md#информация-и-данные)\n- [Кодирование информации](./Data.md#кодирование-информации)\n- [Метаданные](./Data.md#метаданные)\n\n### [Типы данных](./DataTypes.md)\n***«Типом данных*** называют *множество допустимых значений* и *совокупность операций* над этими *значениями*. *Типы данных* позволяют *разбить разнородную информацию* на *несколько заданных типов* (строковый, числовой, логический, дата и другие типы). К *каждому типу* может *применяться* лишь *ограниченное множество операций* (например, *разность чисел*, *конкатенация строк* и так далее)*»*.\n- [О типе данных](./DataTypes.md#о-типе-данных)\n- [Классификация типов данных](./DataTypes.md#классификация-типов-данных)\n- [Логический тип](./DataTypes.md#логический-тип)\n- [Строковый тип](./DataTypes.md#строковый-тип)\n- [Символьный тип](./DataTypes.md#символьный-тип)\n- [Целочисленный тип](./DataTypes.md#целочисленный-тип)\n- [Число с плавающей точкой](./DataTypes.md#число-с-плавающей-точкой)\n- [Ссылка](./DataTypes.md#ссылка)\n- [Указатель](./DataTypes.md#указатель)\n\n### [Структуры данных и алгоритмы]()\n- [Структуры данных](./DataStructures.md#структуры-данных)\n\n### [Модели данных и базы данных]()\n\n- [Общие понятия баз данных](./DataModels-Databases.md#общие-понятия-баз-данных)\n- [Реляционная модель данных](./DataModels-Databases.md#реляционная-модель-данных)\n- [Масштабирование баз данных](./DataModels-Databases.md#масштабирование-баз-данных)\n\n### [Программирование](./Programming.md)\n\n### [Характеристики языков программирования](./ProgrammingLanguageCharacteristics.md)\n«*Не было бы смысла* в таком *количестве языков программирования*, если бы они *хотя бы немного не отличались* друг от друга. И дело *не только* в *синтаксисе* - *критериев сравнения* (***характеристик***) с годами выработалось очень *много*. *Не существует идеального языка* программирования (этакой *\"серебрянной пули\"*), превосходно *подходящего* под *каждый проект*. Наоборот, *язык должен выбираться* в *соответствии* с *техническими требованиями проекта*. И где *один язык прекрасно вписывается*, *реализуя все свои преимущества*, *другой проявляет* себя как нельзя *хуже из-за* своих *\"узких мест\"*, *ограничений*. В *данном разделе* мы детально рассмотрим *каждый* из *критериев сравнения*, чтобы вы могли *научиться описанию* и *сравнению языков программирования*, а также *принятию решения* о том, *какой язык подходит* больше именно *под нужды вашего текущего проекта*».\n- [О характеристиках языков программирования](./ProgrammingLanguageCharacteristics.md#о-характеристиках-языков-программирования)\n- [Типизация](./ProgrammingLanguageCharacteristics.md#типизация)\n- [Компилируемость и интерпретируемость](./ProgrammingLanguageCharacteristics.md#компилируемость-и-интерпретируемость)\n- [Потоки, однопоточность и многопоточность](./ProgrammingLanguageCharacteristics.md#потоки-однопоточность-и-многопоточность)\n- [Синхронность и асинхронность](./ProgrammingLanguageCharacteristics.md#синхронность-и-асинхронность)\n- [Кроссплатформенность и нативность](./ProgrammingLanguageCharacteristics.md#кроссплатформенность-и-нативность)\n- [Поддержка парадигм программирования](./ProgrammingLanguageCharacteristics.md#поддержка-парадигм-программирования)\n\n\n### [Тестирование](./Testing.md)\n***«Тестированием*** называют *процесс испытания* некоторого *продукта* с *целью проверки* *соответствия* *готовой реализации продукта* *первоначальным требованиям заказчика*. *Качественное тестирование* очень *важно* для *приложения*. *Хороший тестировщик продумывает все возможные* и *невозможные исходы* и *проверяет каждый* из них. *Без* такого *тестирования невозможно гарантировать корректность работы прилолжения*, а значит оно *может сломаться* в *любом месте* и в *любой момент времени»*. Здесь мы рассмотрим *подходы к тестированию*, *основные понятия* и *инстументы*, *используемые* при *тестировании* приложений.\n- [Зачем тестировать приложение](./Testing.md#зачем-тестировать-приложение)\n- [Тестирование и его разновидности](./Testing.md#тестирование-и-его-разновидности)\n- [Подходы к написанию тестов](./Testing.md#подходы-к-написанию-тестов)\n- [Тестовые объекты](./Testing.md#тестовые-объекты)\n- [Паттерны тестирования](./Testing.md#паттерны)\n\n\n### [Архитектурные стили и архитектурные паттерны](./Architecture-Design.md)\n\n### [Паттерны проектирования и принципы проектирования](./Architecture-Design.md)\n\n\n<!--\n### [Продвинутый JavaScript](./JavaScript.md)\n\n### [Архитектурные стили и паттерны, паттерны и принципы проектирования](./Architecture-Design.md)\n### [Тестирование, подходы к тестированию, тестовые объекты и паттерны](./Testing.md)\n\n### [Асинхронность в NodeJS, цикл событий, стек вызовов и многое другое](./NodeJS.md)\n### [Элементы функционального программирования](./FunctionalProgramming.md)\n### [Всё,что нужно знать о Docker](./Docker.md)\n### [Elascticsearch и как работает индексация](./Elasticsearch.md)\n### [Работа браузеров, рендеринг критического пути](./Browsers.md)\n\n-->\n\n\n\n## Технологии, библиотеки, фреймворки и языки программирования\n  - [CSS](./CSS.md)\n  - [Docker](./Docker.md)\n  - [Elasticsearch](./Elasticsearch.md)\n  - [Git](./Git.md)\n  - [JavaScript](./JavaScript.md)\n  - [Сравнение Flux, Redux, Vuex и Mobx](./Flux-Redux-Vuex-Mobx.md)\n\n\n\n\n\n\n\n"
  },
  {
    "path": "React.md",
    "content": "- [Что такое React и зачем он нужен](#что-такое-react-и-зачем-он-нужен)\n- [Чем отличаются фреймворк и библиотека](#чем-отличаются-фреймворк-и-библиотека)\n- [Синтаксическое расширение JSX](#синтаксическое-расширение-jsx)\n- [Элементы и компоненты](#элементы-и-компоненты)\n- [Функциональные и классовые компоненты](#функциональные-и-классовые-компоненты)\n- [Параметры компонента `props`](#параметры-компонента)\n- [Состояние компонента `state`](#состояние-компонента-state)\n- [Однонаправленный поток данных (`one-way data flow`)](#однонаправленный-поток-данных-one-way-data-flow)\n- [Жизненный цикл компонента (`component lifecycle`)](#жизненный-цикл-компонента-component-lifecycle)\n- [React Hooks](#react-hooks)\n- [Контролируемые и неконтролируемые компоненты](#контролируемые-и-неконтролируемые-компоненты)\n- [Компоненты высшего порядка](#компоненты-высшего-порядка)\n- [Работа React под капотом](#работа-react-под-капотом)\n  - [Виртуальный DOM (`Virtual DOM`)](#virtual-dom)\n  - [Согласование (`Reconciliation`)](#согласование)\n    - [Ключи (`keys`)](#ключи)\n  - [Алгоритм Fiber](#алгоритм-fiber)\n\n\n## Что такое React и зачем он нужен\n\nОпределение ниже как нельзя точно описывает, что такое React.\n> React is a free and open-source front-end JavaScript library for building user interfaces based on UI components.\nРазберём его.\n\nИтак, начнём.\n### React как библиотека\nВо-первых, React - это JavaScript-библиотека.\n\nДля полноценной работы с React достаточно просто подключить скрипт библиотеки к документу страницы (например, добавив его `index.html`).\n\nИногда React называют JavaScript-фреймворком и ставят его в сравнение с такими фреймворками как Angular и View. Действительно, эта троица стоит сравнения, но всё же называть React фреймворком не стоит.\n\n### React находится в открытом доступе\nВо-вторых, React код библиотеки находится в исходном доступе, а её использование на вашем проекте является абсолютно бесплатным.\n\n### React и компоненты\nВ-третьих, библиотека React нацелена на облегчения написания интерактивных UI-компонент на языке JavaScript. \n\nТаким образом, при использовании React склоняются к компонентному подходу (англ. `component-based approach`). Это значит, что сайт состоит из набора компонент, которые могут переиспользоваться в любом месте сайта.\n\nПримерами UI-компонент являются кнопки, формы, поля для ввода, меню, блоки с информацией - проще говоря, любой DOM-элемент или группу элементов можно обернуть в компонент, чтобы его можно было переиспользовать.\n\n## Чем отличаются фреймворк и библиотека\n\n\n## Композиция как основной принцип React\n\n\n## Синтаксическое расширение JSX\n\n<!-- Что такое синтаксис, что такое семантика и чем они отличаются -->\n\n**JavaScript XML**, **JSX** - это *синтаксическое расширение языка JavaScript*, которое *позволяет писать HTML в одном файле с JavaScript*.\n\nДопустим, мы имеем следующий *HTML-документ* нашего *сайта*. \n```html\n<!-- public/index.html -->\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head><!-- метаданные --></head>\n  <body>\n    <div id=\"root\">\n      <!-- пустой блок, в который React в последствии будет вставлять элементы, составляющие приложение -->\n   </div>\n  </body>\n</html>\n```\nРассмотрим *простейшее React-приложение*, *состоящее* всего из *одного файла* с использованием *синтаксиса* `JSX` (расширение `.jsx`):\n```jsx\n/* App.jsx - корневой файл React-приложения */\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nconst headerElement = (\n <div className=\"header\">\n  <h1 className=\"author\">Max-Starling</h1>\n  <h2 className=\"repository\">Notes</h2>\n  <h3 className=\"topic\">React JSX</h3>\n </div>\n);\nconst rootElement = document.getElementById('root');\nReactDOM.render(headerElement, rootElement);\n```\n\n*Без* `JSX` *код приложения бы выглядел* следующим образом:\n```jsx\n/* App.js - корневой файл React-приложения */\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nconst headerElement = React.createElement(\n  \"div\",\n  { className: \"header\" },\n  React.createElement(\"h1\", { className: \"author\" }, \"Max-Starling\"), \n  React.createElement(\"h2\", { className: \"repository\" }, \"Notes\"), \n  React.createElement(\"h3\", { className: \"topic\" }, \"React JSX\")\n);\nconst rootElement = document.getElementById('root');\nReactDOM.render(headerElement, rootElement);\n```\nМы *сможем ещё раз убедиться* в *таком результате*, когда будем вести речь о `Babel`.\n\nПопробуйте теперь на основании этого примера представить приложение побольше. Становится понятно, что в больших приложениях, при большой вложенности элементов, HTML-подобный синтаксис читается проще, чем JS-подобный. Это понятно разработчику, но не понятно браузеру, поэтому в следующей главе затронем тему совместимости JSX и браузера.\n\n\n### Поддержка браузерами синтаксических расширений\n\nСинтаксические расширения могут значительно облегчить жизнь любому разработчику, однако браузеры способны распознавать лишь чистый синтаксис HTML5, CSS3 и JavaScript - они не понимают JSX, TypeScript, SCSS, Handlebars и другие синтаксические расширения.\n\nНапример, \n1) `JSX` позволяет вам хранить HTML и JS в одном месте,\n2) `TypeScript` наделяет JavaScript статической типизацией (то есть *переменныым* можно *явно задавать типы данных*, *изменение* которых *по ходу выполнения программы* *приводит к ошибке*),\n3) `SCSS` позволяет использовать *переменные*, *вложенные селекторы* и *циклы* в `CSS`,\n4) `Handlebars` позволяет использовать *переменные* в `HTML`.\n\nДля того, чтобы программист мог использовать подобные возможности, нужны специальные программы, которые смогут понять нужный синтаксис, обработать его и перевести его на понятный браузеру манер (а именно в `HTML5`, `CSS3` и `JavaScript`, *соответствующий поддерживаемой браузером спецификации* `EcmaScript`).\n\nТакие программы называют по-разному: транспайлеры, компиляторы, препроцессоры, шаблонизаторы и так далее, но результатом их выполнения всегда будет понятный браузеру синтаксис либо ошибка (чаще всего синтаксическая ошибка приводит к ошибке компиляции).\n\n<!-- Babel - это -->\n\n<!-- \nA source-to-source translator, source-to-source compiler, transcompiler, or transpiler is a type of translator that takes the source code of a program written in a programming language as its input and produces an equivalent source code in the same or a different programming language. -->\n\n\n![image](https://user-images.githubusercontent.com/22237384/165184703-54e45b67-e55f-448c-a87d-ba41bc865629.png)\nСлева то, что видит пользователь, справа то, что видит браузер, а значит и пользователь.\n\n*Проверить* это вы можете сами [здесь](https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wG4AoUSWRYmAEQHkBZObXAo9GAWgBNcZcuTQQAdgGd4AC1R8kUAKIAbJCCRj4AXjgAKcnAA8fYADc4aZSgkSAcinVaARLJTyoTgHwGj0gIwWVjb2jk4oAK4w0tBezCgAHjwAyjAoUMrAYgDmhgD0_t5wvgBMgdZ2DkjORJASwDDQAJ5ethAwSBJ50sWFvgDMZcGVzg1gwGheyNxwAFJJABpdfYV5JqbeAJQUopLwOG0qahracAJo4eqaAHRZSDCHlzAAQo0Akny6hBBt-FvkUxgmMwrkQxO5dK53A9jgAaOD7e6qR5bIA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Creact%2Cstage-2&prettier=false&targets=&version=7.17.9&externalPlugins=&assumptions=%7B%7D).\n\n<!-- Одно -->\n\n<!-- выполнить необходимые операции, запрашиваемые программистом в рамках этого синтаксиса (например, провести статическую типизацию) -->\n\n\n\n## Элементы и компоненты\n- [Элемент](#элемент)\n- [Компонент](#компонент)\n- [Различие между элементом и компонентом](#различие-между-элементом-и-компонентом)\n- [Вызов компонента как функции](#вызов-компонента-как-функции)\n- [Детальнее о React.createElement](#детальнее-о-reactcreateelement)\n\n### Элемент\n**React-элемент** (англ. `element`) имеет *такое же предназначение*, как и *HTML-элемент*: он является *строительным блоком*, при помощи которого задаётся *разметка страницы*. Но, в *отличии* от *HTML-элемета*, *React-элемент* является *обыкновенным объектом*.\n \nПри этом *JSX* позволяет использовать *синтаксис*, похожий на *HTML*, в *JavaScript*, что делает *React-элементы визуально неотличимыми* от *HTML-элементов*.\n```jsx\nconst element = (<span>Notes</span>);\n```\nНа самом же деле, *подобный синтаксис* является *синтаксическим сахаром*, который *после обработки транспайлером Babel преобразуется* в обычный *вызов JavaScript-функции*:\n```jsx\nconst element = React.createElement('span', null, 'Notes');\n```\n\nДля того, чтобы понял, как React-элемент преобразуется в HTML-элемент, читайте далее про **Virtual DOM**.\n\n<!-- \nимеет собственную объектную модель, которая называется **Virtual DOM**. \n\nЭта абстракция над реальным **DOM** браузера позволяет React отслежить изменения React-элементов и вносить в реальный DOM только необходимые изменения \n\nReact хранит элементы в виде объектов, чтобы было проще отследить изменения. Как тогда  -->\n\n### Компонент\nЕсли *элемент* является *константой* (каким-то *заданным*, *неизменяемым блоком*), то **компонент** (англ. `component`) является *функцией* (*классом*), которая может *создавать элементы* в *зависимости* от переданных ей *параметров*.\n\n*Единственным входным параметром компонента* является объект \"**props**\" (англ `properties` - *свойства*).\n```jsx\n/* функциональный компонент */\nconst Title = props => (<span>{props.text}</span>);\n```\n```jsx\n/* классовый компонент */\nclass Title extends React.Component {\n  render() {\n    return (\n      <span>{this.props.text}</span>  \n    );\n  }\n}\n```\n*Синтаксис JSX* позволяет *использовать компоненты* так же, как и *React-элементы*:\n```jsx\n<Title text=\"Notes\" />\n```\nТакой *синтаксис эквивалентен вызову функции* `Title`, которая *возвращает React-элемент*, или *созданию экземпляра класса* `Title`, который *возвращает React-элемент* в своём *методе* `render()`.\n\nПри этом *атрибут* `text` (как и *другие атрибуты*, если они есть) *вместе* со своим *значеним* `\"Notes\"` попадает в `props` *функциональной компоненты* `Title`, то есть `props = { text: 'Notes' }`, или в `this.props` *классовой компоненты* `Title`, то есть `this.props = { text: 'Notes' }`.\n\nТаким образом, *строка*\n```jsx\n<Title text=\"Notes\" />\n```\n*неявно преобразуется* в\n```jsx\nReact.createElement(Title, { text: 'Notes' }, null);\n```\n\n<!-- со своими *значениями* передаются в\n -->\n<!-- такого элемента* становятся полями объекта `props` для функционального компонента или `this.props` для классового.  -->\n\n\n<!-- Такой *синтаксис эквивалентен вызову функции* `Title` или *созданию экземпляра класса* `Title` и *преобразуется* в\n```jsx\nReact.createElement(Title, { text: 'Notes' }, null);\n```\nКак мы уже знаем, *вызов React-функции возвращает элемент*. -->\n\n### Различие между элементом и компонентом\n\n*React-элемент* выступает в роли чего-то *статического*, *неизменного*, то есть *некоторой константы*, *экземпляра класса* - проще говоря, *обычного объекта*, а его *приближённым аналогом* является *DOM-элемент*.\n\n*React-компонент* выступает в роли *конструктора элементов*, то есть в роли *функции*, *строителя* (англ. `builder`), *класса*, но при этом у *компонента имеются* *жизненный цикл*, *состояние*, *поведение*, поэтому он всегда *ассоциируется* у меня с чем-то *динамическим*, *меняющимся* (*реагирующим*) *в ответ* на некоторые *внешние* и *внутренние фатороы* (здесь *имеются в виду параметры компонента* `props` и *внутреннее состояние* `state`, с которыми мы *познакомимся позже*).\n\nНа каком-то *безумном уровне абстракции* вы можете *сравнить элементы* с чем-то *неодушевлённым* (скажем, с камнем), а *компоненты* с *одушевлённым* (живущим своей полноценной жизнью организмом).\n\n### Вызов компонента как функции\n*Вызов компонента* как *функции* был *запрещён* в *одной* из *версий React*.\n```jsx\nTitle({ title }); // Error\n```\nТакое *ограничение* можно *снять*, если *создать фабрику* с помощью *метода* `React.createFactory`. \n```jsx\nconst Title = React.createFactory(({ text }) => (<span>{text}</span>));\n```\nДанный *подход* может быть *полезен* для тех, кто по каким-то причинам *не может использовать JSX*.\n\n### Детальнее о React.createElement\nМетод `React.createElement(element, props, ...children)` *принимает три параметра*.\n\nЕсли используется *React-элемент*, то\n* `element` — это *тег элемента*, *переданный* в виде *строки*. Например, `'div'`.\n* `props` — это *атрибуты React-элемента*. В большинстве случаев *совпадают* с *атрибутами HTML-элемента*, но есть исключения: *HTML-атрибут* `class` *заменён* на `className`, *атрибуты* по *типу* `z-index` - на `zIndex`, а *атрибуты* по *типу* `onclick` - на `onClick`.\n* `children` — *список дочерних элементов текущего элемента* (*все параметры, начиная с третьего, относятся* к *списку дочерних компонент*). \n\nЕсли используется *React-компонент*, то\n* `element` — *название компоненты* (функции или класса). Например, `Title`.\n* `props` — *атрибуты React-элемента* (в большинстве случаев *совпадают* с *атрибутами HTML-элемента*) или *объект* `props` *компонента*.\n* `children` — *список дочерних элементов компонента*.\n\nИтак, *JSX ниже*\n```jsx\nconst Header = (props) => { /* .... */ };\nconst Menu = (props) => { /* .... */ };\nconst logout = () =>  { /* .... */ };\n\nconst headerElement = (\n  <Header>\n    <Menu items={['home', 'user']} />\n    <button onClick={logout} />\n  </Header>\n);\n```\n*преобразуется* в\n```jsx\nconst Header = (props) => { /* .... */ };\nconst Menu = (props) => { /* .... */ };\nconst logout = () =>  { /* .... */ };\n\nconst headerElement = React.createElement(\n  Header,\n  {},\n  React.createElement(Menu, { items: ['home', 'user'] }, null),\n  React.createElement('button', { onClick: logout }, null),\n);\n```\n\n[Полноценный пример](https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=MYewdgzgLgBAEgUwIYBMECcYF4YAoAO6I-EAlNgHwwDeM6CUArumDADwWHEQB0wAFgEsANinpg2AeioBfANwAoBaEixxadAFkEYRgEkoCALbY8gw0fJYqbYYIrVzxmVLsVFK6DG27TBIiRWVLT0TCzsjMIOXCQ8Tka8Rkj4uOoYPvoWpC6SkbKKyuBewiAA5iCMsDi4QTCeIMIIPCWluADkLRVQAIRtpAWesPzIGgCijUY6VXgKMOyIqBgUs3PsGTDxEFjUANpt_CCTbQA0MG2MEBhtALoyMNIrc2wARpVQ4DDgAMJ2wADW206lTuDyekgWGmW_SUgzoIBAUHGximphQIGAjEmYCgPFKDCRWKgACEAJ56FDtIgIvqKABKyGAUAAIgB5TQ8NLoXDDRboAlTU5UxETKb9IA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Creact%2Cstage-2&prettier=false&targets=&version=7.17.9&externalPlugins=&assumptions=%7B%7D).\n\n![image](https://user-images.githubusercontent.com/22237384/165189375-311c7dbf-9db0-45c5-88cf-e5805fd5c08c.png)\n\n# Жизненный цикл компонента (Component life cycle)\n- [О жизненном цикле компонента](#о-жизненном-цикле-компонента)\n- [Жизненный цикл классового компонента](#жизненный-цикл-классового-компонента)\n - [Монтирование классового компонента](#монтирование-классового-компонента)\n - [Обновление классового компонента](#обновление-классового-компонента)\n - [Размонтирование классового компонента](#размонтирование-классовоого-компонента)\n\n## О жизненном цикле компонента\n\nЕсли говорить простыми словами, то *жизненный цикл компонента* *не* сильно *отличается* от *жизненного цикла любого живого существа*. \n\n*Живое существо*:\n1) *Рождается*,\n2) *Живёт* и *изменяется* с течением времени (то есть *изменяет своё состояние* в *следствие* каких-либо *внутренних* или *внешних причин*),\n3) *Умирает*.\n\n*Компонент*:\n1) *Монтируется*, то есть *инициализируется*, *создаётся экземпляр компонента* (*в памяти компьютера*) и *компонент отрисовывается первый раз*. Конечным результатом является то, что *реальный пользователь браузера* может видеть *результат отрисовки компонента* у себя *на экране*.\n<!-- (становится частью рельного DOM и дерева рендера, которое затем отрисовывается браузером) -->\n2) *Обновляется*, то есть *перерисовывается* при *изменениях* во *внутреннем состояннии* `state` и *внешнем состоянии* `props`.\n3) *Размонтируется*, то есть *удаляется* (*из памяти компьютера сборщиком мусора*).\n\n## Жизненный цикл классового компонента\nРассмотрим *жизненный цикл классового компонента* и *методы* его *жизненного цикла* (англ. `lifecycle methods`, `lifecycle hooks`).\n\n### Монтирование классового компонента\n* Метод `constructor()` отвечает за *инициализацию компонента*.\n* `static getDerivedStateFromProps()` - статический метод, позволяющщий \n* `render()`\n* `componentDidMount()`\n\n### Обновление классового компонента\n* `static getDerivedStateFromProps()`\n* `shouldComponentUpdate()`\n* `render()`\n* `getSnapshotBeforeUpdate()`\n* `componentDidUpdate()`\nи уже устаревшие:\n* `UNSAFE_componentWillUpdate()`\n* `UNSAFE_componentWillReceiveProps()`\n\n### Размонтирование классового компонента\n* `componentWillUnmount()`\n\n\nКомпонент создаётся, изменяется и удаляется из памяти, когда в нём нет больше не обходимости.\n\nЛюбое изменение компонента можно отловить при помощи **методов жизненного цикла** (англ.  `lifecycle methods`).\n\nРаньше жизненный цикл компонента можно было отслеживать лишь в классавых компонентах, но с появленем React Hooks такая возможность появилась и для функциональных компонент.\n\n<!-- ## Жизненный цикл классового компонента\n- [Инициализация]\n- [Создание]\n- [Обновление]\n- [Уничтожение]\n\n\nTODO -->\n<!-- \n### Инициализация\n\nПри инициализации проиходит установка Props и начального (initial) State.\n\n### Создание (Mounting)\n\n### Обновнение (Updating) -->\n\n## React Hooks\n\n### `useEffect` vs `useLayoutEffect`\n\nРазличия во *времени выполнения*:\n* Хук `useEffect` выполняется *асинхронно* (англ. `asynchronously`) *после рендера компонента* и *после отрисовки* (англ. `painting`), то есть хук *не блокирует процесс отрисовки*.\n* Хук `useLayoutEffect` выполняется *синхронно* (англ. `synchronously`) *после* всех *DOM-мутаций*, но *перед отрисовкой*, то есть как только *DOM* готов, но ещё не отрисован. Это позволяет произвести вычисления или действия с *DOM*, которые повлияют на последующую отрисовку.\nОба хука.\n* Таким образом, `useLayoutEffect` отрабатывает всегда *раньше*, чем `useEffect`.\n\nРазличия в *производительности*:\n* Хук `useEffect` выполняется *асинхронно*, а значит не блокирует отрисовку, на фоне и не создаёт задержек отрисовки, не влияет на производительность.\n* Хук `useLayoutEffect` выполняется *синхронно*, а значит отрисовка ожидает его выполнения. Тяжелые вычисления или дополнительные обновления DOM в этом хуке затормаживают отрисовку, влияя на производительность сайта в целом.\n\n*Когда использовать*:\n* Хук `useEffect` используется в 99% случаев для *большинства асинхронных задач* (*запросы к серверу* (`async..await`, `promise`), `dispath` событий по какому-то условию, *таймеры* `setTimeout`, *подписка на события* (англ. `subscribtion`) и так далее.\n* Хук `useLayoutEffect` следует использовать лишь в крайних случаях, когда как можно быстрее нужно вычислить размеры элементов DOM или перехватить и изменить DOM напрямую перед отрисовкой (применить стили, переместить элементы и так далее).\n\n\n### Мемоизация с useMemo, useCallback, memo\n\nКомпонент высшего порядка `memo` служит заменой метода `shouldComponentUpdate` и `PureComponent` для функциональных компонент.\n```js\nconst Article = ({ title }) => (<div>{title}</div>);\n\nconst areEqual = (props, nextProps) => {\n  if (props.title === nextProps.title) {\n    return true; // компонент не будет перерендерен\n  }\n  return false; // компонент будет перерендерен\n};\n\nconst React.memo(Article, areEqual);\n```\n\n### useCallback\nВсе действия в функциональном компоненте (не считая React Hooks) проделываются каждый раз, когда он рендерится. В том числе и создание переменных.\n```js\nconst Form = ({ title }) => {\n  /* функция onSubmit пересоздаётся в компоненте Form на каждый рендер */\n  const onSubmit = () => console.log('submit', { title });\n  /* console.log запускается каждый раз, когда рендерится компонента */\n  console.log('rerender!');\n  return (<button onClick={onSubmit}>{title}</button>);\n};\n```\nМожно столкнуться со следующей проблемой.  \nПусть есть список из 100 элементов, состояние которых меняется по клику.\n```jsx\nimport React, { memo, useState, useCallback } from \"react\";\nimport ReactDOM from \"react-dom\";\n\nconst ListItem = ({ value, index, onClick } => {\n  console.log(\"rendered!\");\n  const onButtonClick = () => onClick(index, 1);\n  return <button onClick={onButtonClick}>{value}</button>;\n};\n\nconst List = () => {\n  const [items, setItems] = useState(Array.from(Array(100).fill(0)));\n\n  const onClick = (index, value) =>\n    setItems(items => items.map((item, i) => (i === index ? value : item))),\n  );\n\n  const renderItem = (value, index) => (\n    <ListItem key={index} index={index} value={value} onClick={onClick} />\n  );\n  return <div>{items.map(renderItem)}</div>;\n};\n\nReactDOM.render(<List />, document.getElementById(\"root\"));\n```\nПри клике на любой элемент списка в консоли можно увидеть `(100) rendered!`. Это означает, что клик по одному элементу заставить перерендериться все остальные. Попробуем это исправить.\n\nОбернём компонент `ListItem` в `memo`, чтобы он перерендеривался не каждый раз при рендере родительского компонента, а только при обновлении его `props`.\n```jsx\nconst ListItem = memo({ value, index, onClick }) => {\n  console.log(\"rendered!\");\n  const onButtonClick = () => onClick(index, 1);\n  return <button onClick={onButtonClick}>{value}</button>;\n});\n```\nПри клике на любой элемент списка всё ещё можно видеть `(100) rendered!`. Это связано с тем, что функция `onClick` пересоздаётся каждый раз и в `Props` каждому элементу приходит её новая версия. Пересоздания функции можно избежать, если мемоизировать функцию при помощи `useCallback`.\n```jsx\nconst onClick = useCallback(\n  (index, value) =>\n    setItems(items => items.map((item, i) => (i === index ? value : item))),\n  []\n);\n```\n`useCallback(callback, [dependencies])`.\n\n## Контролируемые и неконтролируемые компоненты\n\n**Контролируемый компонент** (Controlled component) *контролирует данные форм* при помощи возможностей *React*. Происходит *двухстороннее связывание* при помощи `value` и `onChange` с *полями ввода*, а *данные* этих полей *должны* где-то *сохранятся* (например, в React State или Redux Store).\n```jsx\nconst Form = ({ onSend, value, onChange }) => (\n  <form onSubmit={onSend}>\n    <input\n      type=\"text\"\n      value={value}\n      onChange={onChange}\n    />\n    <button type=\"submit\">Send</button>\n  </form>\n);\n```\n**Неконтролируемый компонент** (Uncontrolled component) *контролирует данные форм* при помощи *DOM*. *Данные полей ввода сохраняются* в *атрибутах* их *DOM-элементов*.\n```jsx\nconst Form = ({ onSend }) => (\n  <form onSubmit={onSend}>\n    <input type=\"text\" />\n    <button type=\"submit\">Send</button>\n  </form>\n);\n```\nМожно задать *значение по умолчанию* для *некотролируемого компонента* при помощи свойства `defaultValue` и *получить текущее значение*, используя `ref`.\n```jsx\nconst Form = ({ onSend }) => {\n  const inputRef = React.useRef(null);\n  \n  const onSubmit = (event) => {\n    event.preventDefault();\n    console.log('value', inputRef.current.value);\n    onSend(event);\n  }\n\n  return(\n    <form onSubmit={onSubmit}>\n      <input\n        ref={inputRef}\n        defaultValue=\"Text\"\n        type=\"text\"\n      />\n      <button type=\"submit\">Send</button>\n    </form>\n  );\n};\n```\n*Неконтролируемые компоненты* могут использоваться\n* когда нужно написать код быстро (например, что-то проверить).\n* когда нет необходимости контролировать промежуточное состояние полей ввода, важен только конечный результат при нажатии на `submit`.\n* когда нужно интегрировать в приложение не React-код (HTML + JavaScript, Web Components или что-то ещё).\n* всегда в случае `<input type=\"file\" />`, поскольку его значение нельзя контролировать программно.\n\nВо всех остальных случаях не рекомендуется использовать неконтролируемые компоненты, поскольку они хранят свои данные в DOM и React перестаёт быть единственным местом хранения данных.\n\n## Компоненты высшего порядка\n\nМинимальная реализация компонента высшего порядка `withRouter`.\n```jsx\nconst router = { route: 'qq' };\nconst withRouter = Component => props => (\n  <Component {...props} router={router} />\n);\n```\n```jsx\nconst Navbar = withRouter(props => <div>Route: {props.router.route}</div>));\n\nconst render = () => (<Navbar />); // <div> Route: qq </div>\n```\nМинимальная реализация компонента высшего порядка `connect`.\n```jsx\nconst state = { username: 'Max' };\nconst dispatch = action => console.log(action);\n\nconst connect = (mapStateToProps, mapDispatchToProps) => Component => props => (\n  <Component\n    {...props}\n    {...mapStateToProps(state, props)}\n    {...mapDispatchToProps(dispatch)}\n  />\n));\n```\n```jsx\nconst ProfileComponent = props => (\n  <div>\n    <p>{props.username}</p>\n    <button onClick={props.changeUsername}>Change name</button>\n  </div>\n);\n\nconst mapStateToProps = state => state.username;\n\nconst mapDispatchToProps = dispatch => ({\n  changeUsername: name => dispatch({ type: 'SET_USERNAME', payload: name }),\n});\n\nconst Profile = connect(ProfileComponent)(mapStateToProps, mapDispatchToProps);\n\nconst render = () => (<Profile >/);\n```\n\n# Работа React под капотом\n\n\n## Virtual DOM\n<!-- Как React-элемент превращается в Html-элемент -->\nКак мы знаем, в браузере есть представление HTML в виде дерева, которое называется объектной моделью документа (англ. `Document Object Model, DOM`). \n\nВзаимодействие с DOM происходит медленно. Операции над DOM являются достаточно трудоёмкими.\n\nПоэтому разработчики React создали свою собственную документную модель - абстракцию над реальным DOM браузера, которая называется виртуальной объектной моделью документа (англ. `Virtual DOM`).\n\nТаким образом, `DOM` - это *абстракция над* `HTML`, а `Virtual DOM` - это *абстракция над* `DOM`.\n\nВиртуальный DOM позволяет ослеживать изменения React-элементов и затем вносить в реальный DOM (в html) только те изменения, которые на самом деле произошли.\n\n## Согласование\n\nReact самостоятельно следит за обновлениями.\n\n**Согласование, сверка** (Reconciliation) — *алгоритм сравнения* (diffing algorithm) *двух деревьев*, используемый в *React* для *определения различий* между ними.\n\n*Сравнение* двух *деревьев начинается* с их *корневых элементов*  (root elements), от типа которых зависит дальнейшее поведение. \n\nЕсли *тип корневых элементов*  *различен*, *React уничтожает* (tear down) *старое дерево* и *строит новое* с нуля.\n\nПри *уничтожении* дерева *старые DOM-узлы* (DOM nodes) *удаляются*. *Экземпляры компонента* (component instances) получают `componentWillUnmount()`. При *построении нового* дерева *новые DOM-узлы* *добавляются* в *DOM*. *Экземпляры компонента* получают `componentWillMount()` и затем `componentDidMount()`. *Любое состояние*, связанное со *старым деревом*, *теряется*.\n\nПример: `<div>`, `<p>`, `<Title>`, `<input>`, `<Description>` — все перечисленные далее элементы имеют *различные типы* и их *замена* друг на друга приведёт к *уничтожению дерева*, то есть корневого элемента вместе с его детьми.\n```jsx\n<!-- /* замена <div> */ -->\n<div>\n  <Title>Notes</Title>\n  <Description>My simple notes abount everything.</Description>\n</div>\n<!-- /* на <article> */ -->\n<article>\n  <Title>Notes</Title>\n  <Description>My simple notes abount everything.</Description>\n</article>\n<!-- /* приведёт к уничтожению (unmount) старых <Text>, <Description>\nи созданию (remount) новых /* -->\n```\n\nЕсли *корневые элементы* имеют *одинаковый тип*, *React сравнивает* их *атрибуты* и *обновляет* *только изменённые атрибуты* (в случае *атрибута* `style` — *только изменённые стили*).\n```jsx\n<!-- /* при замене */ -->\n<button\n  className=\"btn\"\n  style={{ border: 'none', background: 'black' }}\n  onClick={onClick}\n  tabIndex={1}\n/>\n<!-- /* на */ -->\n<button\n  className=\"button\"\n  style={{ border: 'none', background: 'grey' }}\n  onClick={onClick}\n  tabIndex={1}\n/>\n<!-- /* React обновит только className и background в style  */ -->\n```\n\n*После обработки корневого элемента* *React* *рекурсивно проходится* по *дочерним элементам*, являющимися *корневыми* в *поддеревьях*.\n\nЕсли *корневые элементы* являются *компонентами одного типа*, *React* *оставляет* тот же *экземпляр* компонента (его *состояние не теряется* между двумя `render()`), *обновляет Props экземпляра* и вызывает в нём `componentWillReceiveProps()` и `componentWillUpdate()`. Далее *вызывается* `render()` и *начинается рекурсивный алгоритм сравнения результатов предыдущего и нового* `render()`.\n```jsx\n<!-- /* при замене */ -->\n<Article>Text</Article>\n<!-- /* на */ -->\n<Article>Notes</Article>\n<!-- /* тип компонента остался без изменений: изменился только props.children;\nзначит экземпляр остаётся прежним и обновляются его Props */ -->\n```\n### Ключи\n\nПри *рекурсивном обходе дочерних элементов React* проходит по *двум спискам потомков* (старое и новое дерево) корневого элемента *одновременно* и *создаёт мутацию*, если находит *отличие*.\n\nТаким образом, если удалить первый элемент списка, то порядок сместится и все элементы будут считаться различными.\n\nПусть есть список, отображающий элементы массива `items`, состоящего чисел от 0 до 99.\n```jsx\nconst items = Array.from(Array(100).keys()); // [0, 1, ..., 99]\n\nconst renderItem = item => (<li>{item}</li>);\n```\n```jsx\n<ul>{items.map(renderItem)}</ul>;\n```\nПосле удаления первого элемента (например, при помощи `items.shift()`) массива, элемент 0 в старом дереве будет сравниваться с элементом 1 в новом, 1 с 2, 2 с 3 и так далее. Все элементы окажутся различными и будут обновлены.\n\nЭтого можно избежать при помощи ключей, устанавливающихся в атрибут `key`.\n```jsx\nconst renderItem = item => (<li key={item}>{item}</li>);\n```\nПри наличии ключей `React` пытается сопоставить элементы по этим ключам. Если элементы по ключам совпадают, то они остаются без изменений.\n\nДля *правильной работы* этого *алгоритма* важно, чтобы *ключи* были *уникальными*. Поэтому в ключ нужно *передавать уникальное значение*, *присущее элементу* (например, `id`). Если уникальные значения отсутствуют, лучше всего их разово сгенерировать и использовать (например, при помощи `uuid()`. В примере выше уникальным значением является сам `item` (число). \n\n`index` элемента в массиве использовать не рекомендуется. При его использовании в примере выше произойдёт аналогичная ситуация (под индексом 0 в старом массиве лежит 0, а в новом 1). Использовать индекс можно только в том случае, когда порядок элементов не меняется (например, когда элементы могут добавляться только в конец и оттуда же).\n\n*Ключи* должны быть *стабильными*, *предсказуемыми* и *уникальными*. *Нестабильные ключи* (например, `key={Math.random()}`) вызовут необязательное пересоздание экземпляров компонента и DOM-узлов, что приводит к потере состояния дочерних компонентов и падению производительности.\n\n## Алгоритм Fiber\n"
  },
  {
    "path": "Testing.md",
    "content": "- [Зачем тестировать приложение](#зачем-тестировать-приложение)\n- [Тестирование и его разновидности](#тестирование-и-его-разновидности)\n  - [Ручное и автоматизированное тестирование](#ручное-и-автоматизированное-тестирование)\n  - [Пирамида тестирования](#пирамида-тестирования)\n- [Подходы к написанию тестов](#подходы-к-написанию-тестов)\n  - [TDD](#tdd)\n  - [ATDD](#atdd)\n  - [BDD](#bdd)\n  - [Сравнение TDD, ATDD и BDD](#сравнение-tdd-atdd-и-bdd)\n  - [DDT](#ddt)\n  - [KDT](#kdt)\n- [Тестовые объекты](#тестовые-объекты)\n  - [Пустышка](#пустышка)\n  - [Заглушка](#заглушка)\n  - [Макет](#макет)\n- [Паттерны тестирования](#паттерны-тестирования)\n  - [Объект-страница](#объект-страница)\n  - [Шпион](#шпион)\n  - [Испытательная Платформа](#испытательная-платформа)\n  - [Фикстура](#фикстура)\n\n<!--   - [Тестирование чёрного и белого ящиков](#тестирование-чёрного-и-белого-ящиков)\n  - [Функциональное и нефункциональное тестирование](#функциональное-и-нефункциональное-тестирование) -->\n<!--   - [Регрессионное тестирование](#регрессионное-тестирование) -->\n\n## Зачем тестировать приложение\n\nУ *каждого программиста* (неважно, профессионал он или любитель) *бывают* случаи, когда его *приложение ломается*. Очень *здорово*, когда *проблема* ему хорошо *знакома* и *решить* её можно достаточно *быстро*, но иногда даже *маленькое, незаметное изменение* в *коде* может *привести* к *часам*, а иногда и дням безудерджного *дебага*. \n\nВ такие моменты *хочется знать*, *какие части приложения точно работают*, а какие нет. Существует *множество путей решения* этой проблемы. \n\nНапример, *системы контроля версий* (такие, как *Git*) позволяют *просмотреть* лишь те *изменения*, которые были *внесены после* выхода в свет *последней рабочей версии приложения*. Это позвояет *не проверять всё приложение целиком* и *искать ошибку среди последних изменений*.\n\nЕсли же система котроля версия не используется или изменений после последней рабочей версии было слишком много, то найти ошибку всё ещё затруднительно.\n\nНа *помощь* приходит *тестирование*. \n\nКачественное тестирование очень важно для приложения. Хороший тестировщик продумывает все возможные и невозможные исходы и проверяет каждый из них. Без такого тестирования невозможно гарантировать корректность работы прилолжения, а значит оно может сломаться в любом месте и в любой момент.\n\nВ идеале это работает так, что некоторый готовый блок кода проверяется тестами и в случае успешного прохождения он больше не трогается. На практике же часто приходится затрагивать один и тот же код, при этом именно тесты могут показать, что ничего не сломалось после внесения изменений.\n\nЧем больше становится приложение и команда, которая над ним работает, тем больше вероятность возникновения ошибок и сложность их исправления. Тесты помогут избежать попадения этих ошибок в production, а также локализовать эти ошибки, что явно поможет их устранить.\n\nЕщё одно применение тестов: описание требований к приложению. Заказчик описывает, чего он ожидает от некоторой функциональности, программист запускает тесты ( тестировщик тестирует готовый продукт) и таким образом показывает, что все требования заказчика были соблюдены.\n\n# Тестирование и его разновидности\n- [Ручное и автоматизированное тестирование](#ручное-и-автоматизированное-тестирование)\n- [Пирамида тестирования](#пирамида-тестирования)\n\n**Обеспечение качества** (Quality assurance, QA) — способ предотвращения ошибок и дефектов в производимых продуктах, а также предотвращения проблем при доставке продуктов или услуг клиенту.\n\n**Тестируемая система** (System under test, SUT) — система, которая тестируется на корректность работы.\n\n## Ручное и автоматизированное тестирование\n\n**Ручное тестирование** — прямое взаимодействие QA-инженера с приложением.  \nQA ищет неисправности и недостатки, а затем информирует о них программиста.\n\n### Плюсы ручного тестирования\n* *Отчёт тестировщика* — первый отзыв потенциального пользователя, позволяющий увидеть приложение его глазами.\n* *Обратная связь* (feedback) по **UI**. Протестировать UI приложения и выявить его недостатки пока позволяет только ручное тестирование.\n* *Отсутствие затрат на написание тестов*. Это важно при быстром внедрении новой функциональности. На малых проектах написание и поддержку тестов может оказаться достаточно затратным.\n\n### Минусы ручного тестирования\n* *Человеческий фактор*. Часть ошибок может упускаться, некоторые результаты субъективны.\n* *Трудозатраты и продолжительность*. Ручное тестирование обычно занимает много времени. Часто требуется тесное взаимодействие QA с программистом во время тестирования для разъяснения многих вопросов.\n* *Отсутствие возможности моделирования большой нагрузки*. При ручном тестировании невозможно смоделировать большое количество пользователей.\n\n**Автоматизированное тестирование** — написание кода для тестов.  \nОжидаемый сценарий описывается кодом, затем при запуске тестов он сравнивается с реальным и программа указывает расхождения.\n\n### Плюсы автоматизированного тестирования\n* *Отсутствие человеческого фактора*. Хорошо написанный тест не может забыть что-то проверить, он каждый раз последовательно проверяет действия.\n* *Скорость выполнения*. Код для сценария пишется один раз, а его запуск обычно занимает несколько секунд.\n* *Переиспользуемость*. Код автотестов может быть использован неоднократно, особенно при внедрении новой функциональности.\n* *Возможность моделирования большой нагрузки*. Приближает тесты к реальной ситуации использования приложения.\n\n### Минусы автоматизированного тестирования\n* *Отсутствие тестирования глазами пользователя* (UX). Не всё можно покрыть автотестами.\n* *Отсутствие обратной связи*. Автоматизированные тесты не предоставляют обратную связь о качестве продукта, они лишь выполняют запрограммированные сценарии.\n* *Затраты на написание тестов и их поддержку*. Автоматизированные тесты трубуют написания большого количества кода. При изменении тестируемых частей приложения, нужно изменять и их тесты (иначе они станут бесполезными). Это может быть губительно для маленьких проектов и проектов, где всё меняется довольно быстро и непредсказуемо.\n\n<!-- * Надежность. Автоматизированные тесты могут упасть по многим причинам, например, при большой загруженности тестовой машины или при проблемах с сетью. -->\n\n<!-- \n\n## Тестирование чёрного и белого ящиков\n\n### Чёрный ящик\n\n**Тестирование чёрного ящика** (black-box testing), **тестирование на основе спецификации** (specification-based testing) — метод тестирования ПО, при котором проверка функциональности приложения происходит, не вдаваясь во внутреннюю структуру приложения. При определённых входных данных должен быть определённый результат. \n\nСпецифических знаний о коде приложения, внутренней структуре проекта и языках программирования в целом не требуется. Тестировщие должен знать, что должно делать приложение (из спецификации) и что оно делает на самом деле (проверяет сам), но может не знать, как оно это реализует. \n\nМетод может быть применен практически на всех уровнях тестирования: Unit, Integration, System, Acceptence.  \n\n### Белый ящик\n\n**Тестирование белого ящика** (white-box testing), **структурное тестирование** (structural testring) — метод тестирования, при котором тестируются внутренние структуры приложения.\n\nТестирование белого ящика — метод тестирования приложения на уровне исходного кода.\n\nМетод применяется на следующих уровнях тестирования: Unit, Integration, System.\n\nТестирование белого ящика позволяет выявить множество проблем в приложении, но может пропустить нереализованные части спецификации.\n\n## Функциональное и нефункциональное тестирование\n\n**Функциональное тестирование** — процесс обеспечения качества (QA process) и подтип тестирования чёрного ящика. \n\nФункциональное тестирование проводится для проверки соответствия системы или её компонентов функциональным требованиям, описывающих функции системы и её компонентов.\n\nФункциональное тестирование обычно описывает, что система делает.\n\n**Нефункциональное тестирование** — тестирование системы (приложения) на предмет её нефункциональных требований: способа работы, а не конкретного поведения. \n\n### Примеры нефункциональных тестов\n* Стресс-тестирование (Stress testing)\n* Тестирование безопастности (Security testing)\n* Тестирование производительности (Performance testing)\n* Тестирование масштабируемости (Scalability testing)\n* Тестирование выносливости (Endurance testing)\n* Тестирование восстановления (Recovery testing)\n* Тестирование документации (Documentation testing)\n\nМногие из этих понятий пересекаются или даже взаимозаменяются в зависимости от приложения. -->\n<!-- \n## Регрессионное тестирование\n\n**Регрессионное тестирование** (regression testing) — повторно запускающиеся функциональные и нефункциональные тесты, предназначенные для проверки того, что ранее написанный и протестированный код не сломался после новых изменений. -->\n\n<!-- ## Уровни тестирования\n* Модульное, Компонентное тестирование (Unit, Component Testing)\n* Интеграционное тестирование (Integration Testing)\n* Системное тестирование (System Testing)\n* Приёмочное тестирование (Acceptance Testing)\n\n## Модульное тестирование\n -->\n\n\n## Пирамида тестирования\n\nМайк Кон, автор книги \"Scrum. Гибкая разработка ПО\", представляет тестирование приложения в виде пирамиды.  \n\nЧем выше по пирамиде, тем дольше и затратнее делать тесты.\n\nПо этой причине автор расставил следующие приоритеты: 80% — модульные тесты, 15% — интеграционные и API-тесты, 5% — UI-тесты (могут быть как автоматизированными, так и ручными).\n\n![Пирамида тестирования](./assets/testing-pyramid.png)\n\n# Подходы к написанию тестов\n- [TDD](#tdd)\n- [ATDD](#atdd)\n- [BDD](#bdd)\n- [Сравнение TDD, ATDD и BDD](#сравнение-tdd-atdd-и-bdd)\n- [DDT](#ddt)\n- [KDT](#kdt)\n\n## TDD\n\n**Test-Driven Development** (TDD) — разработка через тестирование; подход к разработке и тестированию, при котором сначала создаются тесты, которым должен удовлетворять код, затем его реализация.  \n\n*TDD* имеет *итеративный процесс*. Сперва пишется тест на новый, ещё не реализованный функционал, а затем пишется минимальное количество кода (ничего лишнего) для его реализации. При успешном прохождении теста, можно задуматься о качестве кода и сделать его рефакторинг.\n\n### Преимущества TDD\n- Полное покрытие кода тестами.\n- Заранее задумываемся об использовании кода.\n- Хорошие тесты являются неплохим примером использования кода (что-то вроде документации).\n\n### Пример TDD\nНапример, напишем тест для функции, которая должна возводить двойку в степень.\n```js\nconst runTests = () => {\n  assert.equal(pow2(0), 1, '2^0 = 1');\n  assert.equal(pow2(1), 2, '2^1 = 2');\n  assert.equal(pow2(2), 4, '2^2 = 4');\n};\n```\nТеперь на основании теста пишется сама функция.\n```js\nconst pow2 = (power) => {\n  let result = 1;\n  for (let i = 0; i < power; i++) {\n    result *= 2;\n  }\n  return result;\n}\n```\nФункция удовлетворяет тестам выше, добавим новые тесты.\n```js\nassert.equal(pow2(-1), 0.5, '2^(-1) = 0.5');\nassert.equal(pow2(-2), 0.25, '2^(-2) = 0.25');\nassert.equal(pow2(-3), 0.125, '2^(-3) = 0.125');\n```\nДописываем нужный функционал.\n```js\nconst pow2 = (power) => {\n  let result = 1;\n  if (power > 0) {\n    for (let i = 0; i < power; i++) {\n      result *= 2;\n    }\n  } else {\n    for (let i = 0; i > power; i--) {\n      result /= 2;\n    }\n  }\n  return result;\n}\n```\nДалее снова пишем тесты.\n\n## ATDD\n\n**Приемочное тестирование** (Acceptance Testing) — тестирование, направленное на проверку соответствия системы требованиям.\n\n**Acceptance TDD** (ATDD) - разработчка через приёмочные тесты. Подход близок к TDD, но отличается тем, что привлекает тестировщиков, программистов и сторону заказчика к совместному написанию критериев принятия тестов до начала написания кода. \n\nСперва придумываются критерий выполненной работы и критерий того, что она выполнена правильно. Эти критерии описываются доступным для понимания нетехническому человеку языком.\n\nATDD вносит ястность, что все участники проекта точно понимают, что необходимо сделать и реализовать.\n\n## BDD\n\n**Behavior-Driven Development** (BDD) — разработка, основанная на поведении; расширение подхода TDD, где особое внимание уделяется поведению системы в терминах бизнеса. Такие тесты обычно иллюстрируют и тестируют сценарии, интересные заказчику системы. Поэтому для BDD-тестов используются фреймворки (Chai, Mocha, Jest) с синтаксисом, понятным не только программисту, но и представителю заказчика. Обычно этот синтаксис похож на английский язык.\n\n```js\nconst createArticle = title => ({ title, date: new Date() });\n\ndescribe('createArticle', () => {\n  const article = createArticle('Weather');\n  it('should return an object', () => article.to.be.an('object'));\n  it('the object should have a \"date\" and \"title\" properties', () => {\n    expect(article).to.have.property('date'));\n    expect(article).to.have.property('title'));\n  });\n});\n```\n\n## Сравнение TDD, ATDD и BDD\n\nTDD-тесты пишутся программистами для программистов. Они не указывают, что конкретно нужно тестировать и как именно должны выглядеть и называться тесты.\n\nBDD вдохновлено архитектурой DDD (Domain-Driven Design) и фокусируется на предметной области приложения, в то время как TDD фокусируется на сам код, реализацию.\n\nATDD фокусируется на отображении требований в приёмочных тестах и использует эти тесты для разработки. Вопрос, определяющий ATDD-тесты: \"Система делает то, что от неё требуется?\".\n\nBDD ориентирован на клиента (customer-focused), в то время как ATDD больше ориентирован на разработчика (developer-focused) и часто использует те же технологии, что и TDD.\n\nBDD-тесты могут быть написаны и поняты не только программистом, но и техническими менеджерами или тестировщиками, что позволяет убрать языковой барьер между всеми ними.\n\n## DDT\n\n**Data-Driven Testing (DDT)** — тестирование, управляемое данными; подход к архитектуре автоматизированных тестов, при котором тестовые данные хранятся отдельно от тестов (в файле или базе данных).\n\n### Алгоритм DDT\n* Часть тестовых данных извлекается из хранилища.\n* Выполняется скрипт, в котором вызывается обычный тест с извлечёнными тестовыми данными.\n* Сравниваются полученные (actual) результаты с ожидаемыми (expected).\n* Алгоритм повторяется со следующим набором входных данных.\n\n### Пример DDT\n\nПерепишем функцию `pow2` из раздела TDD.\n```js\nconst runTests = () => {\n  assert.equal(pow2(0), 1, '2^0 = 1');\n  assert.equal(pow2(1), 2, '2^1 = 2');\n  assert.equal(pow2(2), 4, '2^2 = 4');\n  /* ... */\n};\n```\nВозводить двойку в степень можно до бесконечности (на самом деле не совсем, поскольку в JavaScript `MAX_SAFE_INTEGER = 2^53 - 1`), но писать все эти степени руками не хочется.  \n\nЧто можно сделать?  \nМожно взять файл, содержащий степени двоек и подгружать данные из его.\n```js\n/* pow-data.txt */\n1 2 4 8 16 32 64 128 256 512 1024 2048 4096 ...\n```\nПишем функционал, загружающий данные из файла и возвращающий их в виде массива.\n```js\nconst fs = require('fs');\n\nconst readPowData = async (filename) => {\n  const powData = await fs.readFile(filename, 'utf8'); // async, await доступны в fs с Node 11+\n  return powData.split(' ');\n};\n```\nПишем функционал для тестов таким образом, чтобы можно было запускать их с массивом данных.\n```js\nconst runTest = (power, expectedValue) => assert.equal(pow2(power, expectedValue), `2^${power} = ${expectedValue}`);\n\nconst runTests = (powDataArray) => {\n  powDataArray.forEach((item, index) => {\n    runTest(index, item);\n  });\n};\n```\nСчитываем данные из файла и запустить тесты.\n```js\nlet powDataArray = await readPowData('pow-data.txt');\nrunTests(powDataArray);\n```\nЗагружаем данные из другого файла или же из другого источника, снова запускаем тесты.\n```js\npowDataArray = await readPowData('pow-data-2.txt');\nrunTests(powDataArray);\n\npowDataArray = await getPowDataFromDB();\nrunTests(powDataArray);\n```\n\n## KDT\n\n**Keyword-Driven Testing (KDT)** — тестирование, управляемое ключевыми словами; подход, использующий ключевые слова, описывающие набор действий, необходимых для выполнения определённого шага тестового сценария.\n\nДля использования подхода нужно определить набор ключевых слов и сопоставить им действия (функции).\n\nВ KDT используется что-то вроде таблиц, чтобы ключевые слова могли иметь параметры, поэтому подход иногда называют **Table-Driven Testing (TDT)**.\n\n### Алгоритм KDT\n* Считываем ключевые слова вместе с их параметрами из таблицы.\n* Последовательно вызываем связанные с ключевыми словами функции.\n\n### Пример KDT\n\nПусть наш тестовый сценарий требует входа в пользовательский аккаунт, отправки двух сообщений на разные адреса и выход из аккаунта.  \n\nОпределяем ключевые слова: `Login`, `Send Email`, `Logout`.  \n\nСопоставим им функции.\n```js\n/* keyword \"Login\" */\nconst login = (username, password) => { /* ... */ };\n\n/* keyword \"Send Email\" */\nconst sendEmail = (receiver, message) => { /* ... */ };\n\n/* keyword \"Logout\" */\nconst logout = () => { /* ... */ };\n```\n\nТеперь можно описать тестовый сценарий, например, следующим файлом (одна строка - одно ключевое слово).\n```js\n/* user-test.txt */\nLogin         | user                  | pass123\nSend Email    | another-user@mail.com | Hello!\nSend Email    | my-friend@mail.com    | Hi!\nLogout\n```\n\nОбработаем этот файл.\n```js\nconst fs = require('fs');\n\nconst removeExtraSpaces = str => str.replace(/ +\\| +/g, '|');\n\nconst readTestData = async (filename) => {\n  const testData = await fs.readFile(filename, 'utf8');\n \n  // убираем лишние пробелы в файле\n  const formattedTestData = removeExtraSpaces(testData);\n\n  // возвращаем массив строк файла\n  return formattedTestData.split('\\n'); \n};\n```\nЧтобы выполнять действие в зависимости от ключевого слова, можно использовать простую функцию с конструкцией `switch`.\n```js\nconst makeAction = (keyword, params) => {\n  switch (keyword) {\n    case 'Login': return login(...params);\n    case 'Send Email': return sendEmail(...params);\n    case 'Logout': return logout();\n  };\n};\n```\nОсталось только считать строки из файла и выполнить то, что описано в каждой.\n```js\n  let rows = await readTestData('user-test.txt');\n  rows.forEach((row) => {\n    // выделяем ключевое слово и параметры в строке\n    const [keyword, ...params] = row.split('|');\n    // выполняем действие\n    makeAction(keyword, params);\n  });\n```\nТут можно сделать вспомогательную функцию.\n```js\nconst executeRows = (rows) => {\n  rows.forEach((row) => {\n    const [keyword, ...params] = row.split('|');\n    makeAction(keyword, params);\n  });\n};\n```\nТеперь можно выполнять любые последовательности, состоящие из определённых нами ключевых слов.\n```js\n  let rows = await readTestData('user-test.txt');\n  executeRows(rows);\n\n  rows = await readTestData('another-test.txt');\n  executeRows(rows);\n\n  rows = ['Login|admin|admin', 'Logout'];\n  executeRows(rows);\n```\n\n# Тестовые объекты\n- [Пустышка](#пустышка)\n- [Заглушка](#заглушка)\n- [Макет](#макет)\n\nДля достижения изолированности какого-то блока тестируемого кода внешние зависимости при его тестировании заменяются **тестовыми объектами**.\n\n<!-- Тестовые объекты увеличивают переиспользуемость тестов и улучшают их поддержку. -->\n\n## Пустышка\n\n**Пустышка (Dummy)** — простейший тестовый объект, реализующий интерфейс, минимально совместимый с интерфейсом реального объекта. \n\nКод Dummy не содержит никакой логики.  \n\nDummy используется в тех случаях, когда обращение к реальному объекту в данном тесте не имеет значения или отсутствует.\n\nНапример, нам в контексте теста не нужна проверка авторизации, поэтому мы заменяем реальную функцию `checkAuth()` на тестовую, которая всегда возвращает истиное значение.\n```js\nconst checkAuth = () => true;\n\ncheckAuth(); // true\n```\nВ реальности же функция `checkAuth` могла иметь следующий интерфейс `(token: string) => boolean`.\n\n## Заглушка\n\n**Заглушка (Stub)** — тестовый объект, частично реализующий логику реального объекта (валидные данные на входе - валидные данные на выходе).\n\nStub обычно содержит тривиальную логику, имитирующую работу нескольких методов реального объекта.\n\n<!-- функция или метод класса, которая подменяет реализацию оригинальной функции и не выполняет никакого осмысленного действия, возвращает пустой результат или тестовые данные. -->\n\nНапример, имитируем создание пользователя в базе данных: принимаем данные пользователя и сразу же возвращаем полученные данные вместе с сгенерированным id (должен был сгенерироваться в бд).\n```js\nconst generateId = () => parseInt(Math.random() * 1000);\n\nconst createUser = data => Promise.resolve({\n  ...data,\n  _id: generateId(),\n});\n```\n\nПерепишем пример для Dummy так, чтобы он смог стать Stub (принимает валидные данные и отдаёт тоже).\n```js\nconst checkAuth = token => !!token;\n\ncheckAuth('qq'); // true\ncheckAuth(''); // false\n```\n\n## Макет \n\n**Макет (Mockup)** — модуль или класс, представляющий собой конкретную фиктивную (dummy) реализацию определенного интерфейса. \n\nМакет, как правило, предназначен для подмены оригинального объекта системы исключительно для тестирования взаимодействия и изолированности тестируемого компонента.\n\nМетоды макета чаще всего из себя представляют заглушки.\n\n### Пример макета\n\nПусть имеется интерфейс, описывающий методы работы с базой данных.\n```ts\nimport { ICreateUser } from '/* ... */';\nimport { IUser } from '/* ... */';\n\ninterface IUserRepository {\n  createUser: (data: ICreateUser) => IUser;\n  getUser: (id: string) => IUser;\n}\n```\n\nВместо того, чтобы использовать реальную базу данных, мы заменяем её фиктивной реализацией: реализуем интерфейс при помощи заглушек. \n\nТогда макет:\n```ts\n/* users-mock.ts */\nimport { ICreateUser } from '/* ... */';\nimport { IUser } from '/* ... */';\nimport { generateId } from '/* ... */';\n\nconst users = [];\n\n/* заглушка */\nconst createUser = (data: ICreateUser): IUser => {\n  const newUser = {\n    ...data,\n    _id: generateId(),\n  };\n  users.push(newUser);\n  Promise.resolve(newUser);\n}\n\n/* заглушка */\nconst getUser = (id: string): IUser => Promise.resolve(users.find(user => user._id));\n\nexport default () => {\n  createUser,\n  getUser,\n};\n```\n\n# Паттерны тестирования\n- [Объект-страница](#объект-страница)\n- [Шпион](#шпион)\n- [Испытательная Платформа](#испытательная-платформа)\n- [Фикстура](#фикстура)\n\n## Объект-страница\n\n**Объект-страница** (Page Object Model, POM) — модуль (класс), представляющий интерфейс отдельной страницы тестируемой системы. \n\nПри использовании POM тесты оперируют с абстрактными объектами страниц и не завязаны на конкретную реализацию пользовательского интерфейса (его дизайн и вёрстку).\n\nТесты используют методы POM каждый раз, когда требуется взаимодействие с UI страницы. \n\n*Преимущества Page Object*\n- Изменение дизайна и вёрстки страниц может потребовать изменения только во внутренней реализации объекта страницы, но оно не затрагивает тестовые сценарии, оперирующие данной страницей. \n- Абстрактность интерфейса позволяет переиспользовать тесты для различных девайсов.\nразличных устройств и разрешений экранов, что очень удобно.\n\n## Шпион\n**Шпион** (Spy) — объект-обёртка (по типу прокси), слушающий вызовы и сохраняющий информацию об этих вызовах (аргументы, количество вызовов, контекст) оригинального объекта системы. \n\nСохраненные шпионом данные используются в тестах.\n\n## Испытательная Платформа\n\n**Испытательная Платформа** (TestBed)  — это специально воссозданная тестовая среда, платформа для тестирования (например, комплекс Макетов, Заглушек и Шпионов).\n\n*Испытательная Платформа* применяется для комплексного тестирования отдельных связок системы или её компонентов. \n\n## Фикстура\n**Фикстура** (Fixture)  —  механизм, позволяющий привести объект или всю систему в определенное состояние и зафиксировать это состояние для тестов. \n\nПод фикстурой чаще всего понимают тестовые данные необходимые для корректного запуска тестов, а также механизмы загрузки/выгрузки этих данных в хранилище. \n\nОсновное назначение фикстуры: привести данные системы к определенному (фиксированному) состоянию, которое будет точно известно во время выполнения тестов.\n"
  },
  {
    "path": "TypeScript.md",
    "content": "- [О TypeScript](#о-typescript)\n- [Типы данных (Data Types)](#типы-данных)\n- [Класс (Class)](#класс-class)\n- [Интерфейс (Interface)](#интерфейс-interface)\n- [Тип и интерфейс(Type vs interface)](#тип-и-интерфейс-type-vs-interface)\n- [Дженерики (Generics)](#дженерики-generics)\n\n# О TypeScript\n\n**TypeScript** - это язык программирования, расширяющий возможности языка JavaScript. \n\nTypeScript привносит несколько полезных возможностей:\n1) Наделяет JavaScript возможностью явного статического объявления типов данных.\n2) Дополняет возможности JavaScript инструментами ООП-разработки (появляются интерфейсы, модификаторы доступа, дженерики и так далее), таким образом TypeScript может считаться полноценным ООП-языком, позволяющим реализовывать большинство теоритических практик парадигмы, а значит может послужить альтернативой некоторым другим традиционным объектно-ориентированным языкам.\n3) В отличии от JavaScript - TypeScript имеет свою гибко настраиваемую систему модулей, которая использует технологию ES6-модули (`import`, `export`). Заметим, что сам JavaScript вообще не имеет модульной системы. Нативный JavaScript может импортируется только в тэгах `<script>`, вставленных в HTML. При этом в NodeJS встроена более старах модульная система CommonJS-модулей (`require`, `module.export`), поддержка ES6-модулей начала появляться лишь недавно и только для последних версий.\n\n**TypeScript** обратно совместим с JavaScript, поскольку TypeScript компилируется в JavaScript перед запуском программы. Таким образом, компилятор запускает TypeScript, производится статическая проверка типов, выдаются ошибки приведения типов, а если ошибок нет, то происходит компиляция в JavaScript и далее работа происходит обычный запуск скрипта на языке JavaScript.\n\n## Разница между TypeScript и JavaScript\n\nTypeScript - компилируемый язык, который содержит все позможности языка JavaScript и расширяет их, а затем компилируется обратно в JavaScript.\n\nJavaScript имеет неявную динамическую слабую типизацию. TypeScript позволяет делать её явной и статической в тех местах приложения, где это нужно. По умолчанию все переменные наделяются типом `any`, который позволяет иметь им любое значение. Таким образом, наличие TypeScript не обязывает разработчика указывать типы везде и всегда - он сам выбирает, где это нужно и насколько сильно. Также это позволяет плавно переписать любой JavaScript проект на TypeScript и не иметь 10000 ошибок в консоли при его подключении.\n\n\n## Типы данных\n\n- [Примитивные типы данных](#примитивные-типы-данных)\n\n### Примитивные типы данных\nПоскольку TypeScript добавляет лишь инструменты типизации и после всех проверок компилируется в JavaScript, он не может вносить то, что нельзя было бы перевести в JavaScript.\n\nПримитивные типы совпадают с теми, которые есть в JavaScript. \n\n- `boolean` — **логическое значение** (`true`, `false`).\n- `number` — **числовое значение** (`-1`, `3.9`).\n- `string` — **строковое значение** (`\"notes\"`, `'123'`).\n- `null` — *специальное значение* **null**.\n- `undefined` — *специальное значение* **undefined**.\n- `symbol` — **символ**.\n```ts\nconst symbol = Symbol('key');\nconst obj = {\n    [symbol]: 'value'\n};\nconsole.log(obj[sym]); // \"value\"\nconsole.log(Symbol('key') === symbol); // false\nconsole.log(obj[Symbol('key')]) // undefined\n```\n### Остальные типы\n- `Array` — **массив** (`number[]`).\n```ts\nlet foo: number[];\nfoo = [1, 2, 3];\nlet bar: Array<string>;\nbar = ['n', 'o', 't', 'e', 's'];\n```\n- `any` — **произвольный тип** (используется по умолчанию, если тип не указан).\n```ts\nlet foo: any;\nfoo = 1;\nfoo = '';\n```\n- `void` — **отсутствие конкретного значения** (обычно возвращаемый тип функции).\n```ts\nconst fn = (param: string): void => {\n  console.log(param);\n  // return отсутствует\n}\n```\n- `never` — **значение**, которое **никогда не наступит** (обычно функции, возвращающие ошибку)\n```ts\nconst throwError = (message: string): never {\n  throw new Error(message);\n};\n```\n- `Tuple` — **кортеж** (`[string, number]`).\n```ts\nlet foo: [string, number, boolean];\nfoo = ['notes', 17, true];\n/* порядок важен */\nfoo = [17, 'notes', true]; // error\n```\n- `Enum` — **перечисление** (более дружелюбные имена для множества числовых значений).\n```ts\nenum Visibility { Visible, Hidden }\nconst state: Visibility = Visibility.Visible; // 0\n```\n\n### `object` vs `Object` vs `{}` vs `Record<K, V>`\n- `object` — **непримитивный тип** (non-primitive); *любой тип*, *кроме [примитивных](#примитивные-типы)*.\n```ts\nlet foo: object;\nfoo = { prop: 'value' };\nfoo = ['value'];\nfoo = () => console.log('notes');\n```\n- `Object` — любой **JavaScript-объект** (соответствует интерфейсу `Object`, имеющий методы `toString()`, `valueOf()`, `hasOwnProperty()` и другие).\n```ts\nlet obj: Object;\nobj = {};\n```\n```ts\ninterface Object {\n  toString(): string;\n  hasOwnProperty(v: string): boolean;\n  /* ... */\n}\n```\n\nФактически, `object` и `Object` очень похожи, поскольку все непримитивные типы в JavaScript происходят от объектов, так в чём же отличие?\n\nПо сути, разница лишь в хайлайтинге:\n```ts\nconst foo: Object = function (){}; // ок\nconst bar: Object = function (){}; // ок\n\nfoo. // покажет доступные методы\nbar. // не покажет ничего\n\n// но при этом\nfoo.toString() // нет ошибки\nbar.toString() // нет ошибки\n```\nПроведя больше тестов, я выяснил, что разница в поведении между ними касается лишь типа `Symbol`.\n```ts\nconst func1: Object = function (){}; // ок\nconst func2: Object = function (){}; // ок\nfunc1() // ошибка TS: `This expression is not callable. Type 'Object' has no call signatures`\nfunc2() // ошибка TS: `This expression is not callable. Type '{}' has no call signatures`\n\nconst arr1: Object = []; // ок\nconst arr2: object = []; // ок\narr1.fill(2); // `Property 'fill' does not exist on type 'Object'`\narr2.fill(2); // `Property 'fill' does not exist on type 'object'`\n\nconst symbol1: Object = Symbol(''); // ок\nconst symbol2: object = Symbol(''); // ошибка TS: `Type 'typeof symbol2' is not assignable to type 'object'`\n\nconst boolean1: Object = new Boolean(true); // ок\nconst boolean2: object = new Boolean(true); // ок\n\nconst promise1: Object = Promise.resolve(true); // ок\nconst promise2: object = Promise.resolve(true); // ок\npromise1.then(() => {}); // `Property 'then' does not exist on type 'Object'`\npromise2.then(() => {}); // `Property 'then' does not exist on type 'object'`\n```\n\n\n- `{}` — **пустой тип**, **пустой объект**.\nОбращение к его свойствам приведёт к ошибке, но остаётся возможность использовать все методы `Object`.\n```ts\nconst foo = {};\nif (true) {\n  foo.a = 'notes'; // Error: Property 'a' does not exist on type '{}'\n}\nconsole.log(foo.toString()); // '[object Object]'\n```\n\nСтоит отметить, что тип `{}` задаётся автоматически, если присвоить пустой объект на этапе создания переменной вне зависимости от того, это `let` или `const`.\n```typescript\nconst a = {};\na.foo = 1; // ошибка TS `Property 'foo' does not exist on type '{}'`\nlet b = {};\nb.bar = 2; // ошибка TS `Property 'bar' does not exist on type '{}'`\n\n// можно задать тип явно\nconst a: {} = {}\n\n// можно сбить пользователя с толку\nconst env: {} = { // ошибки при объявлении нет\n  OS: 'win32',\n}\nenv.OS /* ошибка TS: `Property 'OS' does not exist on type '{}'`,\nнет подсказок, какие свойства есть в объекте, но при этом значение `win32` возвращается\n*/\n\n// попытка обмануть компилятор тоже ни к чему не приведёт\nenv['OS'] /* ошибка TS: `Element implicitly has an 'any' type because expression of type '\"OS\"' can't be used to index type '{}'.\nProperty 'OS' does not exist on type '{}'.` */\n\n// единсвенный способ избежать ошибки, отключить проверку следующей строки при помощи директивы `@ts-ignore`\n// @ts-ignore\nenv.OS // 'win32' без ошибки TS\n\n// тот же самый трюк с функцией, возвращающей `{}`\nconst getUserData = (): {} => ({ email: '17.max.starling@gmail.com' });\nconst user = getUserData();\nuser.email; /* вернёт '17.max.starling@gmail.com', но подсказки о том, что свойство `email` сущесвует, не будет,\nа также будет ошибка TS `Property 'email' does not exist on type '{}'` */\n```\n\n\n### Детальный разбор `void`, сравнение с типом `undefined`\n\n```typescript\nfunction a(): void {} // без ошибок\nfunction b(): void {\n    return undefined; // всё ещё без ошибок (!)\n}\nfunction c(): void {\n    return (-1 + 1); // ошибка: `Type 'number' is not assignable to type 'void'`\n}\nfunction d(): void {\n    return void (-1 + 1); // нет ошибки, поскольку оператор `void` выполняет выражение, но возвращает `undefined\n}\n```\nНе путайте тип `void` из TypeScript и оператор `void` из JavaScript :)\n\nСтоит отметить, что тип `void` на первый взгляд не сильно отличается от `undefined` в плане ошибок TS.\n```ts\nvoid function a(): undefined {} // ок\nfunction a(): undefined {} // ок\nfunction b(): undefined { return undefined; } // ок\nfunction с(): undefined { return void true; } // ок\n```\n\nТак в чём же разница?\n\nРазница есть, но она больше семантическая. Ещё раз, `void` означает, что значение не будет возвращено, а `undefined` означает, что будет возвращён `undefined`.\nЭто важно в опреденеии некоторых функций, например, `Array.prototype.forEach`:\n```ts\ndeclare function forEach<T>(array: T[], callback: (item: T) => undefined): void;\nlet numbers: number[] = [];\nforEach([0, 1, 2], item => numbers.push(item)); // ошибка `Type 'number' is not assignable to type 'undefined'`\n```\nПоскольку `Array.prototype.push` возвращает число, получаем ошибку, поскольку ожидался `undefined`.\nНо если использовать `void`, то такой проблемы не будет, поскольку мы обещаем, что в реализации функции `forEach` не будет использовано возвращаемое значение callback-а.\n\nТаким образом, судя по примеру выше, нельзя с точностью утверждать, что функция, которая возвращает `void`, действительно возвращает `undefined` - возвращаемое знаение может быть любым `any`.\n\n```ts\ndeclare function forEach<T>(array: T[], callback: (item: T) => void): void;\nlet numbers: number[] = [];\nforEach([0, 1, 2], item => numbers.push(item)); // ок\n```\n\n\n### Детальные разбор `never`\n```typescript\nfunction a(): never {\n    throw new Error();\n}\n\nfunction b(): never {\n    if (Math.random() > 0.5) {\n         throw new Error(); // ошибка `A function returning 'never' cannot have a reachable end point.`\n    }\n}\n\nfunction b(): never | void {\n    if (Math.random() > 0.5) {\n         throw new Error(); // нет ошибки\n    }\n}\n```\n\n### Детальный разбор `enum`\nПеречисления - это один из немногих типов в TypeScript, который имеет представление в JavaScript в виде объекта, который можно использовать в ходе выполнения программы.\n\n```ts\nenum Visibility { Visible, Hidden }\n\nconsole.log(Visibility);\n/* {\n  \"0\": \"Visible\",\n  \"1\": \"Hidden\",\n  \"Visible\": 0,\n  \"Hidden\": 1\n}  */\nconsole.log(Visibility[0]) // \"Visible\"\nconsole.log(Visibility.Hidden) // 1 \n```\nПолучили объект, в котором строки соответствуют числовым индексам и наоборот.\n\nВыше показано, что происходит со значениями перечислений по умолчанию, теперь зададим значения явно.\n\n```typescript\nenum Color {\n    RED = 'red',\n    YELLOW = 'yellow',\n    GREEN = 'green'\n}\nconsole.log(Color);\n/* {\n  \"RED\": \"red\",\n  \"YELLOW\": \"yellow\",\n  \"GREEN\": \"green\"\n}  */\n\nconsole.log(Color.YELLOW) // 'yellow'\nconsole.log(Color['yellow']) // `undefined` и ошибка: `Property 'yellow' does not exist on type 'typeof Color'. Did you mean 'YELLOW'?`\n```\n\nПеречислениями также можно манипулировать при помощи `Object`-методов. Это может быть полезно при переборе всех допустимых значений, например, при написании валидации.\n```ts\nObject.values(Color) // [\"red\", \"yellow\", \"green\"]\nObject.keys(Color) // [\"RED\", \"YELLOW\", \"GREEN\"]\nObject.entries(Color) // [[\"RED\", \"red\"], [\"YELLOW\", \"yellow\"], [\"GREEN\", \"green\"]] \n```\n\n# Класс (Class)\n\n\n<!-- \n# Поддержка ООП в TypeScriot\n\nTypeScript предоставляет полноценную поддержку классов из ООП, JavaScript предоставляет лишь частичную поддержку. -->\n\n**Классом** (англ. `class`) называют конструктор (строитель, генератор, создатель) объектов. В теле класса содержится вся информация, которую будет содержать объект после создания.\n\n- [Объявление класса](#объявление-класса)\n- [Создание объектов с помощью класса](#создание-объектов-с-помощью-класса)\n- [Задание свойств в теле класса](#задание-свойств-в-теле-класса)\n- [Передача параметров класса через конструктор и ключевое слово `this`](#передача-параметров-класса-через-конструктор-и-ключевое-слово-this)\n- [Валидация параметров класса](#валидация-параметров-класса)\n- [Задание методов класса](#задание-методов-класса)\n- [Потеря и привязка контекста `this` в классе](#потеря-и-привязка-контекста-this-в-классе)\n\n## Объявление класса\n*Имена классам* даются с *большой буквы*.\n```ts\n/* объявление двух классов Bird и Person */\nclass Bird {}\nclass Person {}\n```\n## Создание объектов с помощью класса\nДля создания объекта через класс или функцию-конструктор используется оператор `new`. \n```ts\n/* создание двух объектов при помощи класса Bird */\nconst dove = new Bird();\nconst magpie = new Bird();\n/* создание объекта при помощи класса Person */\nconst guest = new Person();\n```\n\n## Задание свойств в теле класса\n```ts\n/* задание свойств `name` и `age` в теле класса Person */\nclass Person {\n  name: string = \"He/She\"; \n  age: number = 0;\n}\nconst p = new Person();\nconsole.log(p); // Person { name: \"He/She\", age: 0 }\n```\nЕсли *не задать тип* и *значение свойству*, то будет *выдано предупреждение*:\n```ts\n/* задание свойств `name` и `age` в теле класса Person */\nclass Person {\n  name: string = \"He/She\";\n  age; // TS: Member 'name' implicitly has an 'any' type.\n}\nconst p = new Person();\nconsole.log(p); // Person { name: \"He/She\" }\n```\n<!-- Как видно на примере выше, свойство `age` не задано, поэтому оно не отображается в объекте `p`.\nНо если задать значение `undefined` явно, то свойство попадёт в объект\n\nсо значением `undefined` не отображается в объекте -->\n\n## Передача параметров класса через конструктор и ключевое слово `this`\n\nКласс может принимать параметры и использовать их в качестве аргументов внутри конструктора при создании объекта. Как и параметры функции, параметры класса могут быть обязательными и не обязательными (объявляются с `?`).\n\nЧтобы *присвоить значение полю класса*, нужно использовать *ключевое слово* `this`, которое представляет собой *контекст*, то есть *всё, что касается создания или использования текущего объекта*.\n\n```ts\n/* задание свойств `name` и `age` в теле класса Person */\nclass Person {\n  name: string;\n  age: number;\n  constructor(name: string, age?: number) {\n    this.name = name;\n    this.age = age || 0;\n  }\n}\n/* в примере ниже будет ошибка, так как параметр `name` ялвяется обязательным\nconst someone = new Person();  // TS: Expected 1-2 arguments, but got 0. An argument for 'name' was not provided.\n\n/* инициализация класса с передачей обязательного параметра */\nconst max = new Person(\"Max\");\nconsole.log(max); // Person { name: \"Max\", age: 0 }\n\n/* инициализация класса с передачей обязательного и необязательного параметров */\nconst dan = new Person(\"Dan\", 21);\nconsole.log(dan); // Person { name: \"Dan\", age: 21 }\n```\n## Валидация параметров класса\n\nМожно *налагать некоторые условия* на *параметры класса* и *проверять выполнение* этих *условий при *создании объекта* в *конструкторе*, то есть *валидировать параметры класса*. При *несоблюдении заданных условий* будет *выдаваться ошибка*.\n```ts\n/* задание свойств `name` и `age` в теле класса Person */\nclass Person {\n  name: string;\n  age: number;\n  constructor(name: string, age: number) {\n    /* валидация по параметру `name` */\n    if (name.length < 2) {\n      throw new Error(\"Name must be at least 2 characters long\");\n    }\n    /* валидация по параметру `age` */\n    if (age < 18) {\n      throw new Error(\"Person must be 18 years of age or older\");\n    }\n    this.name = name;\n    this.age = age || 0;\n  }\n}\n/* в примере ниже будет ошибка из-за непрошедшего валидацию поля `name` */\nconst g = new Person(\"G\", 27);  // [ERR]: Name must be at least 2 characters long\n/* в примере ниже будет ошибка из-за непрошедшего валидацию поля `age` */\nconst yo = new Person(\"Yo\", 10);  // [ERR]: Person must be 18 years of age or older\n/* в примере ниже ошибок нет */\nconst ns = new Person(\"NS\", 39);\n```\n## Задание методов класса\n\nМетод в классе можно объявить тремя способами, которые практически ничем не отличаются.\n```ts\nclass Animal {\n  name: string;\n  constructor(name?: string) {\n    this.name = name || \"It\";\n  }\n\n  /* объявление метода как свойства, значением которого является функция `function` (`Function Expression`) */\n  walk = function() {\n    console.log(this);\n  }\n\n  /* объявление метода как свойства, значением которого является стрелочная функция (`Arrow Function Expression`) */\n  fly = () => {\n    console.log(this);\n  }\n  \n  /* объявление метода как метода класса */\n  swim() {\n    console.log(this);\n  }\n}\nconst duck = new Animal(\"Duck\");\nduck.swim(); // Animal { name: \"Duck\" }\nduck.walk(); // Animal { name: \"Duck\" }\nduck.fly(); // Animal { name: \"Duck\" }\n```\nКак видно на примере выше, если использовать обращение к методу через `.` и его вызов `()` в одном выражении, то все три объявления работают одинаково.\n\n## Потеря и привязка контекста `this` в классе\nПопробуем теперь *присвоить каждый метод класса* в *отдельные переменные* и *вызвать получившиеся функции* спустя *некоторое время*.\nЧаще всего *такое необходимо* при *передаче метода класса* как *фунцкии обратного вызова* (`callback`) куда-либо. \n```ts\nclass Animal {\n  name: string;\n  constructor(name?: string) {\n    this.name = name || \"It\";\n  }\n  walk = function() {\n    console.log(this);\n  }\n  fly = () => {\n    console.log(this);\n  }\n  swim() {\n    console.log(this);\n  }\n}\nconst duck = new Animal(\"Duck\");\n\n/* потеря контекста */\nconst swim = duck.swim;\nswim(); // undefined\n\n/* контекст не потерялся */\nconst walk = duck.walk;\nwalk(); // Animal { name: \"Duck\" }\n\n/* контекст утерян */\nconst fly = duck.fly;\nfly(); // undefined\n```\nВ данном примере *разрывается связь классом* и его *методом*, связь между `.` и `()`, что *приводит* к *потере контекста*. \n\nИз примера выше видно, что *потеряли контекст методы* `swim()` и `walk = function() {}`, поскольку *нестрелочные функции* могут иметь *свой контекст*. \n\nВ то же время *стрелочная функция не может иметь контекст* и *присваивать его* ей тоже *нельзя*. Вместо этого *контекст задаётся ей в момент объявления* с *уровня выше*. В данном случае для `fly = () => {}` *контекст* взят из класса `Animal`, поэтому её *связь с классом будет сохраняться в любом случае*.\n\nСущесвует *несколько способов решения проблемы*\n1) *Никогда не разрывать связь между обращением к методу* класса через `.` и его *вызовом* через `()` при присвоении в другую переменную.\n```ts\nconst duck = new Animal(\"Duck\");\n\nconst swim = () => duck.swim();\nconst walk = () => duck.walk();\nconst fly = () => duck.fly();\n\nswim(); // Animal { name: \"Duck\" }\nwalk(); // Animal { name: \"Duck\" }\nfly(); // // Animal { name: \"Duck\" }\n```\n2) *Явно привязать контекст* в *конструкторе класса* через `bind`\n```ts\nclass Animal {\n  name: string;\n  constructor(name?: string) {\n    this.name = name || \"It\";\n    /* явная привязка контекста через `bind` */\n    this.walk = this.walk.bind(this);\n    this.swim = this.swim.bind(this);\n  }\n  walk = function() {\n    console.log(this);\n  }\n  fly = () => {\n    console.log(this);\n  }\n  swim() {\n    console.log(this);\n  }\n}\nconst duck = new Animal(\"Duck\");\n\nconst swim = duck.swim;\nconst walk = duck.walk;\nconst fly = duck.fly;\n\nswim(); // Animal { name: \"Duck\" }\nwalk(); // Animal { name: \"Duck\" }\nfly(); // Animal { name: \"Duck\" }\n```\n3) Всегда *использовать только стрелочные функции* в *качестве методов класса* в тех случаях, когда *есть риск потери контекста*.\n\nДоказательство того, что *стрелочной функции нельзя привязать контекст*:\n```ts\nclass Animal {\n  name: string;\n  constructor(name?: string) {\n    this.name = name || \"It\";\n    /* явная привязка контекста пустого объекта через `bind` всем трём методам */\n    this.walk = this.walk.bind({});\n    this.swim = this.swim.bind({});\n    this.fly = this.fly.bind({});\n  }\n  walk = function() {\n    console.log(this);\n  }\n  fly = () => {\n    console.log(this);\n  }\n  swim() {\n    console.log(this);\n  }\n}\nconst duck = new Animal(\"Duck\");\n\nconst swim = duck.swim;\nconst walk = duck.walk;\nconst fly = duck.fly;\n\nswim(); // {}\nwalk(); // {}\nfly(); // Animal { name: \"Duck\" }\n```\n \n\n## Интерфейс (Interface)\n\n**Интерфейс** (`Interface`) является абстрактным описанием того, что должен включать в себя объект, но *не содержит* никакой *реализации*, то есть *не содержит методов и полей свойств* - *только используемые* в них *типы*.\n\nСравните\n```ts\nclass Person {\n    name: string = \"He/she\"; \n    eat(food: string): void {\n        console.log(this.name, \"eats\", this.food);\n    }\n}\n```\n```ts\ninterface IPerson {\n    name: string;\n    eat(food: string): void;\n}\n```\n\nТо есть интерфейс представляет собой лишь схему \n\n*Интерфейс* в *TypeScript* является *виртуальной структурой*: он *существует только* в *контексте языка*. Компилятор при помощи интерфейсов и прочих способов типизации проводит проверку типов, а затем переводит код в JavaScript, куда интерфейсы не попадают.\n\nИнтерфейсы, как и классы, именуют с большой буквы. Часто можно встретить заглавную `I` в начале, чтобы разрешить конфликт имён классов и интерфейсов.\n\n```ts\ninterface IAuthor {\n  id: string;\n  username: string;\n}\n\ninterface IArticle {\n  id: string;\n  title: string;\n  description?: string;\n  getAuthor: () => Author;\n}\n```\n\nИспользование интерфейсов похоже на утиную типизацию.\n```ts\ninterface IDuck {\n  quack(): void;\n}\n\nconst obj: IDuck = {\n  /* может квакать, значит утка */\n  quack(): void {\n    console.log('quack!');\n  },\n};\n```\n\n### Абстрактные классы и интерфейсы\n\n**Абстрактный класс** (Abstract class) содержит некоторые *абстрактные методы*, которые *должны быть реализованы его наследниками*. *Помимо абстрактных методов*, в нём могут также содержаться и *обычные методы*. Они характеризуют поведение по умолчанию и их реализовывать не обязательно.\n```ts\nabstract class Duck {\n  abstract eat(): void; // утки в разных странах могут есть разную еду\n  makeSound(): void {\n    console.log('quack!'); // но все они издают похожий звук\n  }\n}\n\nclass Mallard extends Duck {\n  eat() { /* ... */ }\n  // дикая утка наследует метод quack от абстрактного родительского класса\n}\n```\n\n*Интерфейс*, в отличие от любого класса, вообще *не может содержать реализации*. Поэтому от него *не наследуют* (extends), а его *реализуют* (implements).\n```ts\ninterface IDuck {\n  eat(): void;\n  makeSound(): void;\n}\n\nclass Mallard implements IDuck {\n  eat() { /* ... */ }\n  makeSound() { /* ... */ }\n};\n```\n\n### Наследование интерфейса от класса\n\nВ TypeScript есть возможность наследовать интерфейс от класса. \n```ts\nclass Fish {\n  private age(): string;\n  swim(): void;\n}\ninterface IFlyingFish extends Fish {\n  fly: () => void;\n};\n```\n\nТакая возможность связана с тем, что в TypeScript (как и в JavaScript) можно создать объект без класса.\n```ts\nclass Human {\n  sex: string;\n  constructor(sex: string) {\n    this.sex = sex;\n  }\n\n  run():void {\n    console.log('run');\n  }\n}\n/* объекты human1 и human2 реализуют один и тот же интерфейс */\nconst human1 = new Human('male');\nconst human2 = {\n  sex: 'male',\n  run():void {\n    console.log('run');\n  }\n};\n```\nВ этом и есть смысл: можно взять интерфейс класса и использовать его.\n```ts\ninterface IHuman extends Human {}\n\nlet human3: IHuman;\nhuman3 = { ...human2 };\n```\nБолее того, мы можем расширить этот интерфейс. И таким образом заменить наследование композицией (*Composite Reuse Principle*), реализуя расширенный интерфейс вместо переопределения методов родительского класса.\n```ts\nclass Bird { /* ... */ };\n\ninterface IFlyingBird extends Bird {\n  fly: () => void;\n}\n\nclass FlyingBird implements IFlyingBird { /* ... */ }\n```\nМожно также найти применение наследованию интерфейса от класса при использовании Generics.\n```ts\nclass Translator<From, To> { /* ... */ }\ninterface EngRusTranslator extends Translator<Russian, English> {}\n\nconst translate = (translator: EngRusTranslator) => { /* ... */ }\n```\n\nИнтерфейсы наследуют всё, включая приватные и защищённые члены базового класса.\n\nЕсли базовый класс содержит приватные или защищённые свойства и методы, то наследующий от него интерфейс может быть реализован только базовым классом или его наследником.\n\n# Тип и интерфейс(Type vs interface)\n\nИ тип (Type), и интерфейс (Interface) описывают объекты, так что в простых случаях будут работать одинаково.\n```ts\ntype User = { id: string; name: string }\n```\n```ts\ninterface User { id: string; name: string }\n```\n\nРазличия:\n* Интерфейсы поддерживают наследование (англ. `inheretance`) через `extends`, позволяющее создавать новый тип, дополняя уже существующий:\n```ts\ninterface Customer extends User { company: string }\n```\n* Типы поддерживают пересечение (англ. `intersection`) с помощью `&` (`AND`), позволяющее комбинировать несколько типов в один, создавая новый тип:\n```ts\ntype Customer = User & { company: string }\n```\n* Типы также поддерживают объединение (англ. `union`) с помощью `|` (`OR`):\n```ts\ntype A = { foo: string }\ntype B = { bar: string }\ntype C = A | B // либо тип А, либо тип Б\n```\n* Интерфейсы поддерживают **слияние деклараций** (англ. `Declaration Merging`), типы - не поддерживают (будет ошибка):\n```ts\ninterface User { id: string }\ninterface User { name: string }\nconst user: User = {\n  id: 1,\n  name: \"John\",\n};\n```\n```ts\ntype User = { id: string }\ntype User = { name: string } // ❌ Error: Duplicate identifier 'User'\n```\n* Типы могут работать с примитивами, интерфейсы - не могут:\n```ts\ntype Pet = 'cat' | 'dog'\ntype Pets = `{Pet}s` // 'cats' | 'dogs'\ntype ID = string | number\n```\n* Интерфейсы подходят для имплементации классов (англ. `implementation`) через `implements`:\n```ts\ninterface Person {\n  name: string;\n  greet(): void;\n}\n\nclass User implements Person {\n  name: string;\n  greet() { console.log('Hi!'); }\n}\n```\n\n# Дженерики (Generics)\n<!-- \n/*\n- Допустим, у нас в системе есть пользователи, уникальные идентификаторы\nкоторых являются числами\n*/\n\n{\n  type Person = {\n    id: number\n    name: string\n  }\n\n  const max: Person = {\n    id: 123,\n    name: 'Max'\n  }\n}\n\n/*\n- В какой-то момент времени появляется требование использовать строковые значения\nвместо числовых, но при этом старых пользователей изменить уже нельзя.\n\nЭтого можно достигнуть, создав ещё один тип и объявлять эти типы через \"или\"\n(либо NumberPerson, либо StringPerson).\n*/\n{\n  type NumberPerson = {\n    id: number\n    name: string\n  }\n\n  type StringPerson = {\n    id: string\n    name: string\n  }\n\n  const max: NumberPerson | StringPerson = {\n    id: 123,\n    name: 'Max',\n  }\n\n  const nastya: NumberPerson | StringPerson = {\n    id: 'fafafa',\n    name: 'Nastya',\n  }\n}\n\n\n/* Таким образом мы имеем два типа, которые отличаются всего лишь\nтипом одного параметра. Это может работать, но получается много лишнего кода.\nПредставь, что каждый из типов содержал бы 100 строк кода и притом различия\nбыли бы по-прежнему в одном параметре.\n\nЧтобы не дублировать код и сделать возможным полиморфизм\n(то есть обобщение нескольких типов в один)\nпридумали Generics. \n*/\n\n{\n  // В <> задаём параметр, который можем затем использовать при указании типа свойствам\n  type GenericPerson<ID_TYPE> ={\n    id: ID_TYPE\n    name: string\n  }\n\n  const max: GenericPerson<number> = {\n    id: 123,\n    name: 'Max',\n  }\n\n  const nastya: GenericPerson<string> = {\n    id: 'fafafa',\n    name: 'Nastya',\n  }\n}\n\n/* Это то, как Generics работают с объектами.\nПосмотрим теперь на функции. */\n\n/* просто перенос кода из примера выше, чтобы потом не дублировать */\ntype GenericPerson<ID_TYPE> = {\n  id: ID_TYPE\n  name: string\n}\n\nconst nastya: GenericPerson<string> = {\n  id: 'fafafa',\n  name: 'Nastya',\n}\n\nconst max: GenericPerson<number> = {\n  id: 123,\n  name: 'Max',\n}\n\n{\n  const kissPerson = function <PERSON_ID_TYPE>(person: GenericPerson<PERSON_ID_TYPE>) {\n    console.log('kiss', person.name);\n  }\n\n  // Настя у нас со строковым айди, поэтому мы передаём строку\n  kissPerson<string>(nastya);\n\n  // а вот Максим как старый пользователь с числовым айди, поэтому мы передаём число\n  kissPerson<number>(max);\n}\n\n/* Если бы мы задали тип PERSON_ID_TYPE в параметрах функции напрямую, то поцеловать на ночь можно было\nбы лишь один тип людей - либо строковых, либо числовых, поэтому параметр и выносится на уровень выше,\nто есть на уровень вызова функции. Пример: */\n{\n  const kissPerson = function (person: GenericPerson<string>) {\n    console.log('kiss', person.name);\n  }\n  // Настю поцеловать можно\n  kissPerson(nastya);\n  // а Максима нельзя\n  kissPerson(max);\n}\n\n/* Но я всё же люблю хэппиэнды, поэтому покажу, как решить эту проблему */\n{\n  const kissPerson = function (person: GenericPerson<string | number>) {\n    console.log('kiss', person.name);\n  }\n  // Настю поцеловать можно\n  kissPerson(nastya);\n  // и Максима можно тоже\n  kissPerson(max);\n}\n/* а если вообще без Generics, то (person: NumberPerson | StringPerson) */\n\n/* Рассмотрим пример c двумя разными параметрами на примере функции чата между двумя людьми */\n{\n  const makeChat = function <TYPE_A, TYPE_B>(personA: GenericPerson<TYPE_A>, personB: GenericPerson<TYPE_B>) {\n    console.log(`${personA.name}: - Привет, ${personB.name}!`);\n    console.log(`${personB.name}: - И тебе привет, ${personA.name}!`);\n  }\n  // Настя пишет первой Максиму\n  makeChat<string, number>(nastya, max);\n  // Максим пишет первым Насте\n  makeChat<number, string>(max, nastya);\n}\n\n/* Пример c использованием Generics в теле функции. Представим, что есть некая Лера.\nИ она хочет взять кого-то выпить с собой.\nДопустим, что Лера хочет определить себя как личность уже в теле функции. */\n{\n  const leraGoDrinkWith = function <LERA_ID_TYPE, PERSON_ID_TYPE>(\n    leraId: LERA_ID_TYPE,\n    person: GenericPerson<PERSON_ID_TYPE>\n  ) {\n    const lera: GenericPerson<LERA_ID_TYPE> = {\n      id: leraId,\n      name: 'Lera',\n    };\n\n    console.log(`\n    Девушка по имени ${lera.name} с id \"${lera.id}\" говорит:\n      - Привет, ${person.name}! Пойдём со мной гулять.\n    `);\n    // console.log(`${personB.name}: - И тебе привет, ${personA.name}!`);\n  }\n  // Лера с id 27 зовёт Настю гулять\n  leraGoDrinkWith<number, string>(27, nastya);\n}\n\n -->\n\n# Вернуть тип в зависимости от параметра\n* Определяем типы:\n```ts\ntype User = {\n  name: string;\n  age: number;\n};\n\ntype Admin = {\n  name: string;\n  permissions: string[];\n};\n```\n* Создаём маппинг\n```ts\ntype RoleMap = {\n  user: User;\n  admin: Admin;\n};\n```\n* Создаём функцию и вызываем\n```ts\nfunction getData<T extends keyof RoleMap>(role: T): RoleMap[T] {}\nconst user = getData(\"user\"); // вернёт тип User\nconst admin = getData(\"admin\"); // вернёт тип Admin\n```\nЕщё пример\n```ts\ntype ResponseMap = {\n  success: { status: \"ok\"; data: any };\n  loading: { status: \"loading\" };\n  error: { status: \"error\"; message: string };\n};\n\nfunction getResponse<T extends keyof ResponseMap>(status: T): ResponseMap[T] {}\n\nconst successResponse = getResponse(\"success\"); // вернется тип { status: \"ok\"; data: any }\nconst errorResponse = getResponse(\"error\");     // вернется тип { status: \"error\"; message: string }\n```\n"
  },
  {
    "path": "_config.yml",
    "content": "theme: jekyll-theme-hacker"
  },
  {
    "path": "in-progress/Algorithms-Structures.md",
    "content": "# Трудоёмкость алгоритмов\n\n# Алгоритмы сортировки\n\n## Сортировка пузырьком (bubble sort)\n\n*Cравниваются соседние элементы* и *меняются местами*, если *следующий* элемент *меньше предыдущего*.  \nТребуется *несколько проходов по данным*.\n\n```js\nconst bubbleSort = (array) => {\n  const length = array.length;\n  let tmp = 0;\n  for (let i = 0; i < length; i += 1){\n    for (let j = length - 1; j >= i + 1; j -= 1) {\n      if (array[j] < array[j-1]) {\n        tmp = array[j];\n        array[j] = array[j-1];\n        array[j-1] = tmp;\n      }\n    }\n  }\n}\n```\n\nhttps://function-x.ru/cpp_algoritmy_sortirovki.html  \nhttps://habr.com/ru/post/204600/  \nhttps://en.wikipedia.org/wiki/Timsort  \n\n# Структуры данных\n\n## Список с пропусками\n\n**Список с пропусками** (Skip List) — вероятностная структура данных, основанная на нескольких параллельных отсортированных связных списках. Позволяет производить поиск и вставку сложности `O(log n)` в среднем в упорядоченной последовательности `n` элементов, что сравнимо с двоичным деревом.\n\n"
  },
  {
    "path": "in-progress/C++.md",
    "content": "\n## Указатели\n\n**Указатель** (pointer) — переменная, значением которой является адрес ячейки памяти, то есть указатель ссылкается на начала какого-то блока данных из области памяти.\n\n### Размер указателя\n\n**Размер указателя** (pointer size) зависит от версии приложения\n* *4 байта* — 32-битная версия.\n* *8 байт* — 64-битная версия.\n\nРазмер указателя можно узнать при помощи `sizeof`.\n```cpp\nint* pointer = new int(1);\ncout << sizeof(pointer) << endl;\n```\n\n## Абстрактные классы\n\n**Виртуальная функция** (virtual function) - функция, которая определена базовым классом и переопределена производным классом.  \n\n*Виртуальаня функция* отличается от обычной функции тем, что для обычной функции связывание вызова функции с её определением происходит на этапе компиляции, а для виртуальных функций связывание происходит во время выполнения программы.\n```cpp\nclass Bird {\n  public:\n    virtual void fly(); // виртуальная функция\n}\n```\n\n*Виртуальная функция* вызывается только через указатель или ссылку на базовый класс.\n\n**Чистая виртуальная функция** (pure virtual function), **абстрактная функция** (abstract function) — виртуальная функция, которая не имеет реализации: имеет только объявление.\n\n```cpp\nclass Bird {\n  public:\n    virtual void fly(); // виртуальная функция\n    virtual void eat() = 0; // чистая виртуальная функция\n}\n```\n\n**Абстрактный класс** — любой класс, содержащий хотя бы одну чистую виртуальную функцию.  \n\nМы не можем создать экземпляр (instance) абстрактного класса.\n```cpp\nconst bird = new Bird(); // Compiler Error: cannot declare variable 'bird' to be of abstract type 'Bird' because the following virtual functions are pure within 'Bird': ...\n```\n\nДочерние классы классы не должны реализовывать все виртуальные функции родителя, но должны реализовать все чистые функции.\n```cpp\nclass Chicken: Bird {\n  public:\n    void eat() {\n      cout<< \"eat grain\";\n    }\n}\n```\n"
  },
  {
    "path": "in-progress/Colors.md",
    "content": "* `rgba(255, 255, 255, 1)` - red-green-blue-alpha\n* `hsl(360, 100%, 100%)` - hue-saturation-lightness (оттенок-насыщенность-светлость)\n* `#rrggbb` - hex rgb \n"
  },
  {
    "path": "in-progress/English.md",
    "content": "- [Времена (`Tenses`)](#времена-tenses)\n- [Модальные глаголы (`Modal verbs`)](#модальные-глаголы-modal-verbs)\n- [Вспомогательные глаголы (`Auxiliary verbs`)](#вспомогательные-глаголы-auxiliary-verbs)\n- [Предложения](#предложения)\n- [Условные выражения (`Conditionals`)](#условные-выражения-conditionals)\n- [Сожаление (конструкция с `wish`)](#сожаление-конструкция-с-wish)\n- [Сожаление (конструкция с `If only`)](#сожаление-конструкция-с-if-only)\n- [Предпочтение (конструкция с `would rather`)](#предпочтение-конструкция-с-would-rather)\n- [Конструкции с `used to`, `be used to`, `get used to`](#конструкции-с-used-to-be-used-to-get-used-to) \n- [Порядок прилагательных (`Order of adjectives`)](#порядок-прилагательных-order-of-adjectives)\n- [Сослагательное наклонение (`Subjunctive`)](#сослагательное-наклонение-subjunctive)\n\n# Времена (`Tenses`)\n\n- [Настоящее совершённое время (Present Perfect)](настоящее-совершённое-время-present-perfect)\n\n## Настоящее совершённое время (Present Perfect)\n\nВремя **Present Perfect** (настоящее совершённое) обозначает действие, которое завершилось к настоящему моменту.\n\nДействия времени Present Perfect привязаны к настроящему времени своим результатом. \n\nВ русском языке Present Perfect обычно переводится в прошедшем времени.\n\n### Образование Present Perfect\n\nОбразуется при помощи вспомогательного глагола `to have` в настоящем времени и третьей формы глагола (V3).\n* `has` – 3 лицо, ед. число (`he`, `she`, `it`).\n* `have` – все остальные формы\n\n* (+) `I + have + V3.` и `He + has + V3.`\n* (–) `I + have + not + V3.` и `He + hasn't + V3.`\n* (?) `Have + you + V3?` и `Has + he + V3?` \n\n### Случаи употребления Present Perfect\n* Если важен сам *факт произошедшего действия*, а *не время*. При этом чаще всего просматриваются *результат в настоящем* (можно сделать вывод), *какое-то достижение* или *изменения со временем*.\n```css\n– I have lost my keys. (so I can't gen into my house) /* Я потерял ключи (поэтому не могу попасть домой) */\n– I have been to London. /* Я был в Лондоне */\n– Man has walked on the Moon. /* Человек побывал на луне */\n– We have begun to understand each other better. /* Мы начали лучше понимать друг друга */\n```\n<!-- * *Действия, которые завершились, но упомянутый период времени ещё не окончен* (`all day`, `today`, `this week`, `this year`).\n```css\n– I have read the book ~this week~. // Я читал книгу на этой неделе\n``` -->\n* *Жизненный опыт* (`ever`, `never`, `before`).\n```css\n– Have you ~ever~ eaten pizza ~before~? /* Ты когда-нибудь ел пиццу раньше? */\n– I have ~never~ done this. /* Я никогда не делал этого раньше */\n– Have you heard this ~before~? /* Ты слышал подобное раньше? */\n```\n* *Действия, которые начались в прошлом, и продолжаются сейчас* (`for`, `since`, `all day`, `How long ...?`). *Работает с* теми *глаголами*, которые обычно *не используют* в *форме Continuous* (например, *глаголы состояния* `have`, `know`, `like`) - такие *глаголы не отражают процесс* (англ. `non-action verbs`).\n```css\n– I have lived in Australia ~for a week~. /* Я живу в Австралии неделю */\n- They have had this house ~for the last 5 years~. /* Они владеют этим домом последние пять лет */\n– She has known him ~since~ childhood. /* Она знала его с детства */\n```\n* *Недавно (только что) завершённые действия* (`just`, `already`, `yet`, `recently`).\n```css\n– Have you finished your task ~yet~? /* Ты уже закончил свою задачу? */\n– I have ~already~ finished my task. /* Я уже закончил мою задачу */\n– I have ~just~ woken up. /* Я только что проснулся */\n```\n* Когда мы *говорим* или *спрашиваем* о том, *как часто* (*как много раз*) что-то *было сделано* к *этому моменту* (`several times`, `twice`).\n```css\n– I have watched this video ~several times~. /* Я (уже) несколько раз смотрел это видео */\n– I have been to the doctor ~twice~. /* Я (уже) дважды был у врача */\n```\n\n### Present Perfect vs Past Simple\n* Past Simple используется, если важно время выполнения действия и период времени окончен (на него указывают такие слова, как: `last year`, `yesterday`, `3 weeks ago`). \n```css\n– I went to London ~last year~. /* Я был в Лондоне 5 лет назад */\n– I saw her ~yesterday~. /* Я видел её вчера */\n```\n* Если период времени не окончен или время выполнения действия неважно, используется Present Perfect (`today`, `this week`, `this year`).\n```css\n– I have been to London ~this year~. /* Я был в Париже в этом году */\n– I have seen her ~today~. /* Я видел её сегодня */\n```\n\n## Present Perfect Continuous\n\n* *Действия, которые начались в прошлом, и продолжаются сейчас* (`for`, `since`, `all day`, `How long ...?`). *Работает с* теми *глаголами*, которые *отражают действие* или *процесс* (англ. `action verbs`), то есть с *большинством глаголов*.\n```css\n– They've been eating meat ~all their life~. /* Они едят мясо всю свою жизнь */\n- I have been learning English ~since childhood~. /* Я учу английский с детства */\n- ~How long~ have you been hiding it? /* Как долго ты скрываешь это? */\n```\n* *Длительные действия* (процесс), которые уже *завершились*, но имеют *результат в момент речи*.\n```js\n- I have been cooking dinner. /* Я готовил ужин (акцент на длительности действия) */\n```\n* *Повторяемое действие* (*повторившийся жизненный опыт*). Особенно с *временными выражениями* (`all day`, `recently`, `this week`, `this year`), когда *период времени ещё не закончен*.\n```js\n- Have you been reading my diary again? /* Ты снова читал мой дневник ? */\n- It's been raining this week. /* Всю эту неделю идёт дождь */\n- I have been reading the book ~this week~. /* Я читал книгу эту неделю */\n```\n\nТаким образом, если речь идёт о *незавершённых действиях*, то `Present Perfect Simple` используется с `non-action verbs`, а `Present Perfect Continuous` - с `action verbs`. Причём, *некоторые глаголы* (например, глагол `have`) могут *относиться* как *одной*, так и к *другой группе* в *зависимости* от *контекста*: `have lessons` - заниматься (`action verb`), `have something` - иметь что-то (`non-action verb`).\n```js\n- I have had my own car for 2 years. // У меня уже два года как есть собственная машина\n- I have been having piano lessons since I 10 years old // Я учусь игре на фортепиано с тех пор, как мне стукнуло 10\n```\n\nЕсли *Present Perfect* ставит *акцент* на *совершении* (*завершении*) *действия* (англ. `emphasizes the completion of an action`), то есть *действие обычно* уже *совершено*, то *Present Perfect Continuous* ставит *акцент* на *длительности действия* (англ. `emphasizes the duration of an action`)\n```js\n- I have cooked dinner. /* Я приготовил ужин (акцент на том, что ужин уже сделан) */\n- I have been cooking dinner. /* Я готовил ужин (акцент на длительности действия, ужин может быть ещё не готов, но он готовился некоторое время) */\n```\n\nС глаголами `work` и `live` можно использовать в одинаковом смысле как *Present Perfect*, так и *Present Perfect Continuous*.\n\n\n# Модальные глаголы (`Modal verbs`)\n\n**Модальные глаголы** (англ. `modal verbs`) обозначают *возможность, способность, вероятность* или *необходимость совершения действия*, *выраженного смысловым глаголом*.\n\n## Отличительные свойства модальных глаголов\n* Модальные глаголы самостоятельно не употребляются – только в сочетании со смысловым глаголом, которым выступает инфинитив.\n```css\n– I ~must do~ it. /* Я должен это сделать */\n```\n* После них не используется окончание `s` в 3 лице, ед. числе.\n```css\n– He walks. /* (без модального) Он ходит */\n– He ~can~ walk. /* Он может ходить */\n```\n* В вопросительной и отрицательной формах не используются вспомогательные глаголы `to do` (`do`, `does`, etc).\n```css\n– ~Should~ he go there? /* Следует ли ему идти туда? */\n– He ~needn't~ do it.\n– ~May~ I come in? /* Можно мне войти? */\n```\n* Не имеют временных форм (кроме `can` – `could`, `may` – `might`, `have to` и `be to`) и страдательного загола.\n* После модальных глаголов (кроме `ought to`, `have to`, `be to`) следует голый (bare) инфинитив (без частицы `to`).\n\n# Вспомогательные глаголы (`Auxiliary verbs`)\n\n**Вспомогательный глагол** (англ. `auxiliary verb`) — это глагол, основной *функцией* которого является *передача дополнительной грамматической* и *семантической информации* в *сочетании* со *смысловым* (основным) *глаголом*.\n\n*Вспомогательные глаголы* в большинстве языке обычно *используются* для *образования временных* и *залоговых форм*.\n\nЧаще всего вспомогательным является обычный глагол, который просто употребляется не в основном своём значении. Если глагол используется как вспомгательный, то он лишается своего лексического значения, то есть больше не означает то, что он обычно значит, а просто указывает на какое-то время или какую-то конструкцию.\nОбычно такими глаголами в различных разговорных языках выступают глаголы \"быть\" и \"иметь\".\n```\nЯ человек.\n```\n\nВ русском языке вспомогательным глаголом является глагол \"быть\", с помощью которого можно образовать будущее время.\n```\n- Я буду заниматься усерднее. // \"заниматься\" - основной глагол, \"буду\" - вспомогательный глагол, указывающий на будущее время\n```\n\nВ английском языке вспомогательными глаголами служат `to have` (иметь), `to do` (делать) и `to be` (быть) и их *различные формы* (`have/had`, `do/does/did`, `am/is/are/will/was/were`).\n```\n// времена\n- I am a programmer. // Я программист (am/is/are - вспомогательный глагол, указывающий на Present Simple в именном сказуемом\n- What did you do? // Чем ты занимаешь? (do/does - вспомогательный глагол, указывающий на Present Simple)\n- Have you tried swimming? Вы (когда-либо) пробовали плавать? (have - вспомогательный глагол, указывающий на Present Perfect)\n\n// пассивный залог\n- The job is done. // Работа сделана (кем-то) - пассивный залог, настоящее время, am/is/are + третья форма глагола\n- The job will be done // Работа будет сделана (кем-то) - пассивный залог, будущее время, will be + третья форма глагола\n- The job was done // Работа была сделана (кем-то) - пассивный залог, прошедшее время, was/were + третья форма глагола\n```\n\n## Зачем нужны вспомогательные глаголы\n\nВспомогательные глаголы используются для того, чтобы:\n1) Избежать повторения основного глагола (в данном случае вспомогательны глагол служат чем-то вроде переменной, местоимения)\n```\n- I like dogs, but she doesn't // Я люблю собак, а она не любит (их)\nПЛОХО: I like dogs, but she doesn't like dogs\n```\n2) Кратко ответить на поставленный вопрос.\n```\n- Do you like it?\n- Yes, I do.\n- No, I don't.\n\n- Were you a doctor?\n- Yes, I was.\n- No, I wasn't.\n```\n3) Показать, что ваше мнение (опыт) отличается от мнения (опыта) говорящего.\n```js\n- I don't like reading. // Я не люблю читать\n- I do. Reading books is great. // Читать книги здорово\n```\n4) Показать, что ваше мнение (опыт) *совпадают* с мнением (опытом) говорящего. Для этого используется *конструкция* `so + auxiliary verb + subject` c *положительным глаголом* (англ. `positive verb`) и конструкция `neither (nor) + auxiliary verb + subject` для отрицательных глаголов (англ. `negative verb`). Это так же работает и с модальными глаголами.\n```js\n- I'm a programmer. // Я программист\n- So am I. // Я тоже\n\n- I'm not a doctor. // Я не врач\n- Neither am I. // Я тоже\n\n- I have done. // Я закончил\n- So have I. // И я тоже закончил\n\n- I have not tried this yet. // Я ещё не попробовал это\n- Nor have I. // Так и я тоже\n\n// с модальным глаголом\n- I can fly. // Я могу летать\n- So can I. // Я тоже могу\n```\n5) Чтобы *уделить особое внимание совершённому действию*, *сделать акцент* (англ. `emphasis`) на *сделанном*, *выразить свою уверенность* в своём *выборе действия*. С глаголами, которые *не могут быть вспомогательными* (то есть любыми, кроме `to be`, `to have`, `to do`), эта конструкция строится следующим образом: `subject + do + verb` в простом настоящем времени и `subject + did + verb` в простом прошедшем времени. Чаще всего испольуется для *самозащиты* при *предъявлении претензий*, чтобы *выразить несогласие* с утверждением.\n```js\n- You didn't tell him! // Ты так и не расскзал ему!\n- I did tell him! // (Не правда!) Я рассказал ему! (did tell, самозащита в ответ на упрёк)\nТАКЖЕ ГРАММАТИЧЕСК ПРАВИЛЬНО, НО БЕЗ АКЦЕНТА НА ДЕЙСТВИЕ: I told him!\n\n- You never call them back! // Ты никогда им не перезваниваешь!\n- I do call them back but not very often! // Я им перезваниваю! Просто не очень часто. (do call back)\n\n- If you do call 911, even by mistake, DO NOT hang up the phone. Please stay calm and answer all questions. // Если вы позвонили в 911, даже по ошибке, НЕ ВЕШАЙТЕ трубку! Пожалуйста, оставайтесь терпеливыми и ответьте на все вопросы. (do call)\n```\n6) Чтобы выразить *удивление*, *участие* в *разговоре* с кем-то. Это считается *хорошим тоном*.\n```js\n- I went to the doctor yesterday.\n- Did you? // Серьёзно? В самом деле? Действительно?\n\n- I like you. // Ты мне нравишься.\n- Did you? It is wonderful! I like you too! // Серьёзно? Это же замечательно! Ты мне тоже нравишься!\n\n- I will buy a car. // Я куплю машину\n- Will you? That's a great idea! - В самом деле? (Правда?) Это отличная идея!\n```\n7) Чтобы *уточнить (проверить)* какую-то *информацию*, к *утвердительному предложению* можно *добавить вспомогательную конструкцию*, которая *сделает* из него *вопрос* и которую на *русский язык* можно *перевести* `\"не так ли?\"`. На такой *тип вопроса* можно *ответить либо \"да\"*, *либо \"нет\"* - *других вариантов ответа не подразумевается*. *Конструкция* такого предложения следующая: `auxiliary verb + subject` для *отрицательных глаголов* в *утвердитеьной части предложения* и `auxiliary verb + not subject` для *положительных глаголов*.\n```js\n- It's so beautiful here, isn't it? // Это так красиво, не так ли? (it is ..., is not it?)\n- Yes, it is. // можно просто ответить Yes.\n- No, it isn't. // можно просто ответить No.\n\n- You was there tonight, wasn't you? // Ты была там вчера ночью, не так ли? (you was ..., was not you?)\n- No, I wasn't.\n- Yes, I was.\n\n- She didn't do her homework, did she? // Она не сделала своё домашнее задание, не так ли? (she didn't do ..., did she?)\n- Yes, she didn't.\n- No, she did.\n\n- You won't tell them, will you? // Ты им не расскажешь, не так ли? (you will not tell ..., will you?)\n```\n\nПри этом вспомогательный глагол полностью или частично утрачивает своё основное лексическое значение. Вспомогательные глаголы участвуют в образовании различных видо-временных и залоговых форм.\n\n# Предложения\n\nПредложения бывают простыми с составными. Составное предложение состоит из нескольких частей, представляющими собой простые предложения.\n\nВ зависимости от связи между своими частями сложные предложения могут быть сложносочинёнными и сложноподчинёнными.\n\nВ сложноподчинённом предложении одна часть зависит от другой\n```\nЯ люблю читать книги, потому что они открывают тебе новые миры. // Первая часть - основная, вторая - зависимая от неё\nЕсли ты\n```\nВ сложносочинённом предложении части независимы друг от друга:\n```\nТы любишь бегать по музеям, а я люблю по ним гулять.\n```\n\nПростые предложения бывают полными и неполными.\n\nПолное предложение состоит из подлежащего и сказуемого. Дополнительно в предложении могут присутствовать вспомогательные \n\nСказуемое чаще всего выражено каким-то действием (глаголом), а подлежащее - субъектом, совершающим данное действие.\n```\nЯ люблю природу // Я - подлежащее, люблю - сказуемое\nКролик убежал // Кролик - подлежащее, убежал - сказуемое\n```\nНо бывают и исключения, когда сказуемое выражено существительнм\n```\nОн всего лишь человек // Он - подлежащее, человек - сказуемое\n```\n\nВ русском языке последовательность слов особо не важна, чего нельзя сказать об английском языке. Причина в том, что в русском языке слова связываются друг с другом благодаря окончаниям и каждое слово обычно означает что-то одно. В английском языке окончания практически отсутствуют, а если они и есть, то используются для других целей (например, чтобы указать на определённое время, число), а одно слово может обозначать очень много чего в зависимости от контекста. Так, поменяв два слова в предложении местами, можно привнести совсем не тот смысл, который хотелось бы.\n\n## Утвердительное предложение\n\n## Вопросительное предложение\n\n\n**Вопросительное предложение** (англ. `interrogative sentence`) - это *предложение*, которое *выражает вопрос* (англ. `question`). \n\nРаспознать вопросительное предложение можно по следующим признакам:\n* *Структура вопросительного предложения* отличается от *утвердительного* тем, что *подлежащее* (англ. `subject`) *меняется местами* с *модальным* (англ `modal verb`) или *вспомогательным глаголом* (англ `auxiliary verb`).\n* В *конце предложения интонация повышается*, а *не идёт на спад* как в *утвердительном* предложении.\n* В конце предложени обычно ставится знак вопроса `?` (англ. `question mark`)\n\n*Пример утвердительного* и *вопросительного предложений* с *модальным глаголом* `can`.\n```js\n- You can do it yourself. // Ты можешь сделать это самостоятельно (утверждение)\n- Can you do it yourself? // Ты можешь сделать это самостоятельно?\n```\n\nПри построении вопросительного предложения в *настоящем простом времени* (англ. `Present Simple tense`) добавляется вспомогательный глагол `Do`\n```js\n- You like Radiohead. // Ты любишь Radiohead\n- Do you like Radiohead? // Ты любишь Radiohead?\n```\n\nПри построении вопросительного предложения в прошедшем простом времени (англ. `Past Simple tense`) добавляется вспомогательный глагол `Did`, который *отражает прошедшее время* (поэтому глаголу уже не нужно этого делать).\n```js\n- He visited Harry. // Он навестил Гарри.\n- Did he visit Harry? // Он навестил Гарри?\n```\n\nЕсли *после глагола идёт предлог*, то *предлог* принято *ставить в конец вопросительного предложения*!\n```js\n- She is talking about reading. // Она сейчас говорит о чтении\n- What is she talking about? // О чём она сейчас говорит? (предлог about ставится в конец)\nНЕПРАВИЛЬНЫЙ ВАРИАНТ: - About what is she talking?\n\n- I'm looking for a job. // Я сейчас ищу работу\n- What are you looking for? // Что ты ищешь? (предлог for ставится в конец)\n```\n\n### Прямой вопрос (direct question)\n\n### Вопрос к подлежащему (subject question)\n\nВ вопросе к подлежащему подлежащим выступает само вопросительное слово `What/Who/How many`. \n\nПостроение вопроса к подлежащему совпадает с *построением утвердительного предложения*, поэтосу структуры *вопроса* и *ответа* одинаковы: в ответе заменяется лишь вопросительное слово.\n\n```\n- Who wants to try? // Кто хочет попробовать? (\"кто\" выступает в роли подлежащего)\n- Max wants to try. // Макс хочет попробовать (структура ответа совпадает со структурой вопроса)\n\n- Who said that? // Кто это сказал? (\"кто\" выступает в роли подлежащего)\n- I said. // Я сказал.\n\n- How many fans have come? // Сколько фанатов пришло? (\"сколько\" выступает в роли подлежащего)\n- Twelve fans have come. // 10 фанатов пришло\n```\n\nСравнение с обычным прямым вопросом?\n```\n- What do you want to try? // Что ты хочешь попробовать? (\"ты\" выступает в роли подлежащего)\n- Everything. // Всё\n\n- What did you said? // Что ты сказал? (\"ты\" выступает в роли подлежащего)\n- I said nothing.\n```\n\n\nСтруктура этого вопроса самая простая.\n\n\n\n### Косвенный вопрос (indirect question)\n\n**Косвенный вопрос** (англ. `Indirect question`) используется тогда, когда мы хотим попросить о чём-то как можно вежливее.\n\n*Косвенный вопрос* обычно состоит из двух частей. Первая часть вежливо указывает на то, что будет задан вопрос и. Вторая часть раскрывает основной вопрос, причину обращения.\n\nКонструкции, позволяющие распознать косвенный вопрос:\n```js\n1) Could (Can) you tell me ...?\n2) Do you know ...?\n3) Do you remember ...?\n4) Do you have any idea ...?\n```\n\nВажно запомнить, что, поскольку первая часть косвенного вопроса уже указывает на вопросительное предложение, вторая часть с основным вопросом уже не строится как вопросительное предложение, а строится как утвердительное.\n```\n- Where does she live? // - Где она живёт? (прямой вопрос, direct question)\n- Could you tell me where she lives? // - Не подскажете, где она живёт? (косвенный вопрос)\nНЕПРАВИЛЬНО: Could you tell me where does she live? // второй раз на вопрос указывать не нужно - конструкция неправильная\n\n- What time does the ship leave? // - В какое время отплывает корабль? (прямой вопрос)\n- Do you know (remember) what time the ship leaves? // Вы случайно не знаете (не помните), в какое время отплывает корабль? (косвенный вопрос)\nНЕПРАВИЛЬНО: Do you remember what time does the ship leave? // вопрос задаётся дважды - неверная конструкция\n\n- Is Marry working tonight? // Работает ли Мэрри сегодня вечером? (прямой вопрос)\n- Do you have any idea if (whether) Marry is working tonight? // Вы случайно не знаете (Вы знаете), работает (работает ли) Мэрри сегодня вечером? (косвенный вопрос)\n```\n\nСуществует ещё несколько конструкций, порядок слов в которых совпадает с порядком слов в косвенном вопросительном предложении:\n```js\n1) I wonder ... .\n- I wonder why he left her alone. // Интересно, почему он оставил её наедине с собой.\n2) I can't remember ... .\n- I can't remember where I left my keys. // Я не могу вспомнить, где я оставил свои ключи.\n3) I'm not sure ... .\n- I'm not sure why I'm still here. // Я не уверен, почему я всё ещё здесь.\n```\n\nЕсли используется косвенный вопрос к подлежащему, то ошибиться невозможно: часть с основным вопросом остаётся как есть.\n```js\n- Who said that? // Ты помнишь, кто это сказал? (\"кто\" выступает в роли подлежащего)\n- Do you remember who said that? // Ты помнишь, кто это сказал?\n\n- How many fans have come? // Сколько фанатов пришло? (\"сколько\" выступает в роли подлежащего)\n- Do you know how many fans have come? // Ты не знаешь, сколько фанатов пришло?\n```\n\n\n\n<!-- \nВопросительные предложения по характеру вопроса делятся на несколько видов:\n* Сколько вам лет?\n* Ты ведь никому не расскажешь о нашем разговоре?\n* \n* Почему бы тебе не потанцевать со мной? -->\n\n## Условные выражения (`Conditionals`)\n\n**Условное предложение** (англ. `Conditional sentense`) - это сложное предложение, которое состоит из двух частей: **условия** (англ. `condition`, `if-clause`) и **следствия, результата** (англ. `result`, `other clause`) *выполнения* данного *условия*.\n\nНачнём с примеров в русском языке:\n- **Если** **бы** я много зарабатывал, (**то**) я **бы** объехал весь земной шар. (Но я не имею) | \n- **Если** я хорошо сдам экзамены, (**то**) я поступлю туда, куда давно хочу.\n- Тебе следует доверять мне, **если** ты считаешь меня своим другом.\n- Я спасу её, **если** мне хватит времени.\n\nИтак, в русском языке схема следующая:\n```jsx\nЕсли <условие>, (то) <результат>.\n<Результат>, если <условие>.\n```\n\nВ английском языке всё аналогично:\n```html\nIf <condition>, (then) <result>.\n<Result>, if <condition>.\n```\n\nМожно заметить, что *условие* и *следствие* могут относиться к различным временам: к прошедшему времени (например, сожаление о чём-то не случившемся), к настоящему времени и к будущему, причём времена в условии и следствии могут не совпадать.\n\nВ английском языке можно выделить 5 типов условных предложений:\n* Нулевое условное выражение (англ. `zero condition`)\n* Первое условное выражение (англ. `first condition`)\n* Второе условное выражение (англ. `second condition`)\n* Третье условное выражение (англ. `third condition`)\n* Условное выражение смешанного типа (англ. `mixed condition`)\n\nНулевой и первый типы относятся к типу реальных условных выражений (англ. `real conditions`). Добиться *заданного результата* можно при *соблюдении некоторых условий*. \n\nВторой и третий тип относятся к **нереальным**, **воображаемым** (англ. `unreal conditions`). Представляется какая-то ситуация и её исход в прошлом, настоящем или будущем.\n\n### Zero condition\n`Zero condition` используется для выражений, которые *всегда* являются *истинными*, либо в тех случаях, когда при *определённом условии всегда получается один* и тот же *результат*.\n\nИ *условие*, и *следствие* в `Zero condition` должны быть *настоящего времени*:\n```jsx\nIf <Present Simple/Continuous/Perfect>, then <Present Simple/Continuous/Perfect>\n```\n \nПримеры:\n```js\n- If you want to be strong you have to train hard every day. // Если ты хочешь быть сильным, тебе нужно много тренироваться каждый день\n- If you are not with us, then you are against us. // Если ты не с нами, то ты против нас\n- If you have never been to the sea, you have not lived. // Если ты не был на море, то ты не жил\n- If you love animals, animals love you back. // Если (Когда) ты любишь животных, животные любят тебя в ответ\n- I cannot sleep if I drink coffee, . // Я не могу спать, если (когда) я пью кофе.\n```\n\n### First condition\n`First condition` используется тогда, когда что-то может произойти в будущем при соблюдении заданных условий.\n\nПри использовании `Zero condition` *условие* имеет *любую форму настоящего времени*, а *следствие* - *любую форму будущего времени*:\n```jsx\nIf <Present Simple/Continuous/Perfect>, then <Future Simple/Continuous/Perfect/going to>\n```\n\nПримеры:\n```js\n- If I feel good, I will come to the party. // Если я буду хорошо себя чувствовать, то я приду на вечеринку\n- I am going to leave the university if I win this year's main event. // Я собираюсь покинуть школу, если я выиграю главный турнир этого года\n- If you suit us, we will call you back. // Если вы нам подходите, мы вам перезвоним\n- If you try, you will succeed. // Если ты будешь стараться, то у тебя получится\n```\n\n### Second condition\n\nВторое условное предложение используется в том случае, если чисто гипотетически представляется (моделируется) некоторая ситуация и предполагаются её последствия.\n\nПри использовании второго условного предложения *условие* имеет *прошедшее время* (обычно `Present Simple`, но иногда можно использовать `Present Continuous`), а *следствие* содержит глаголы `would/could/might`, выражающие *возможность в прошедшем времени* (*будущее в прошлом*):\n```jsx\nIf <Past Simple/Continuous>, (then) + [would/could/might] + (not)\n```\n\n```js\n- If I were at home now, I would probably watch my favorite movie. // Если бы я был сейчас дома, я бы, наверное, смотрел бы свой любимый фильм (но я сейчас не дома)\n- If the weather was better, we could walk in the park now // Если бы (сейчас) погода была лучше, мы бы могли сейчас гулять в парке (но погода сейчас не очень)\n- If I were you, I would invite her to dance // Если бы я (сейчас) был на твоём месте, я бы пригласил её потанцевать (но я сейчас не на твоём месте)\n```\n\n### Third condition\n\nДобиться *заданного результата можно было бы*, если бы были выполнены *некоторые условия в прошлом*, но они *не были выполнены* - *момент упущен*.\n\nВ *условии* используется *прошедшее совершённое время*, в *следствии* - `would` (бы), `have` и *третья форма глагола*.\n```jsx\nIf <Past Perfect>, (then) + would + (not) + have <Past Participle>\n```\n\n```js\n- He would not have broken his leg if he had followed the safety requirements. // Он бы не сломал ногу, если бы он (тогда) соблюдал технику безопасности (но он не соблюдал)\n- If I had stayed at home then everything would have been fine // Если бы я тогда остался дома, всё было бы (тогда) хорошо (но я не остался)\n```\n\n# Сожаление (конструкция с `wish`)\n\nСам глагол `wist` *отражает желание* (*пожелание*).  \nКонструкция: `wish something`.\n```js\n- I wish you only well // Я желаю тебе только хорошего\n- We wish you a Merry Christmas // Мы желаем вам счастливого Рождества\n```\n\nТем не менее, существует более сложная конструкция в виде сложноподчинённого выражения, которая выражает *сожаление* о несбыточном (о неслучившемся). Предложение в таком случае строится из двух частей.\n* Первая часть *выражает сожаление* о чём-то. *Переводится* на русский язык \"Я бы хотел\", \"Мне бы хотелось\". Эта часть использует *настоящее время* (`Present Simple`).\n* Вторая часть *содержит* сам *объект сожалений*. Эта часть *всегда* находится в *прошедшем времени* (`Past Simple/Perfect`). Если субъект *сожалеет о настоящем*, то *используется* `<Past Simple>`. Если субъект *сожалеет о прошлом*, то *используется* `<Past Perfect>`.\n\n```js\nI/We/They wish + I (he/...) + <Past Simple/Perfect>\nHe/She/It wishes + He (I/...) + <Past Simple/Perfect>\n```\n\n##### Примеры сожалений о настоящем\n```js\n- I wish I had a dog // Как бы мне хотелось, чтобы у меня (сейчас) была собака (но её нет)\n- I wish you were here // Я бы хотел, чтобы ты (сейчас) был(а) здесь (но ты не здесь); Как бы я хотел, чтобы ты была здесь\n```\n\n##### Примеры сожалений о прошлом\n```js\n- I wish we had had more time then // Хотелось бы мне, чтобы у нас тогда было больше времени тогда (но его не было); Жаль, что у вас тогда было мало времени\n- We wish I had passed that exam // Мне бы хотелось, чтобы я сдал тот экзамен (но я не сдал его)\n```\n\nЕсли *человек* нас *раздражает какими-то своими действиями* и *мы бы хотели* от *этого избавиться*, в *конструкции добавляется частица* `would`:\n```js\n- I/We/They wish + subject + would + invinitive\n```\n\nПримеры:\n```js\n- I wish he would stop playing video games at night // Я бы хотел, чтобы он перестал играть в видеоигры по ночам\n```\n\n# Сожаление (конструкция с `If only`)\n\nЕсли нужно выразить сожаление о случившихся или не произошедших действиях в прошлом, необходимо использовать Past Perfect или Past Perfect Continuous.\n\nТакие предложения, выражающие сожаление и относящиеся к прошедшему времени, на русский язык переводятся в форме «Как жаль, что...», «Как бы мне хотелось, чтобы...».\n\n\nЕсли *человек* нас *раздражает какими-то своими действиями* и *мы бы хотели* от *этого избавиться*, в *конструкции добавляется частица* `would`:\n```js\n- If only + subject + would + invinitive\n```\n\nПримеры:\n```js\n- I wish he would stop playing video games at night // Я бы хотел, чтобы он перестал играть в видеоигры по ночам\n```\n\n\n# Предпочтение (конструкция с `would rather`)\n\nКонструкция `would rather` (`'d rather`) *используется* в тех *случаях*, когда нужно *отдать предпочтение чему-либо* при *выборе*.\n\nПостроение предложения с данной конструкцией зависит от времени (настоящее/прошедшее/будущее) и от того, совпадают ли субъекты в обоих частях предложения.\n\nВ общем виде строение предложения с конструкцией `would rather` следующее:\n```js\nSubjectA + would rather + SubjectB + verb\n```\n\nНужно *запомнить*, что в *сочетании* с `rather` *инфинитив используется без частицы* `to`!\n\nЕсли мы хотим чему-то отдать предпочтение в настоящем или будущем времени, глагол `verb` нужно использовать с временной *формой* `<Simple>`. Причём, если `SubjectA` и `SubjectB` *совпадают*, то *используется* `<Present Simple>`, а если не совпадают, то `Past Simple`.\n```js\n- I + would rather + <Present Simple>\n- I + would rather + he + <Past Simple>\n```\n\n#### Примеры предпочтения в настоящем времени с одним и тем же субъектом\n```js\n- I would rather stay at home // Я бы хотел остаться дома (сейчас, сегодня)\n- He’d rather not do this // Он бы предпочёл не делать этого (он не хочет сейчас это делать)\n- I would rather not order sushi. I have no desire to eat fish today. // Я бы предпочёл не заказывать суши (а заказать что-то другое). У меня сегодня нет желания есть рыбу.\n- I’d rather not go to the party. I have a hedache. // Я бы предпочёл не пойти на вечеринку (а остаться дома). У меня болит голова.\n```\n\n#### Примеры предпочтения в настоящем времени с разными субъектами\n```js\n- I would rather you stayed at home today // Я бы хотел, чтобы ты остался сегодня дома\n- He’d rather they leave him alone // Он бы предпочёл, чтобы они оставили его наедине с собой (сейчас, сегодня)\n```\n\nЕсли мы хотим чему-то отдать предпочтение в прошеднем времени, глагол `verb` нужно использовать с временной *формой* `<Perfect>`. Причём, если `SubjectA` и `SubjectB` *совпадают*, то *используется* `<Present Perfect>`, а если не совпадают, то `Past Perfect`. На прошедшее время указывают определённые слова (например, `then`).\n```js\n- I + would rather + <Present Perfect> then\n- I + would rather + he + <Past Perfect> then\n```\n\n\n\n\n\nДля усиления конструкции можно использовать сочетание `would much rather` (я бы намного больше хотел).\n\n\n# Конструкции с `used to`, `be used to`, `get used to`\n\n## Конструкция `used to`\n\nКонструкция `used to + infinitive` используется для того, чтобы состаться на наши привычные (повторяющиеся) действия, состояния или ситуации в прошлом, которые сейчас уже не действительны. То есть данная конструкция подразумевает контраст между тем, что было раньше и тем, что есть сейчас. \n\nТакже верно, что конструкция `used to` может *ссылаться только* на *прошедшее время*. Её *можно перевести* на *русский* как `раньше + глагол прошедшего времени`.\n\nПредложение с конструкцией `used to` может состоять как из одной части, так и из двух. Вторая часть, отражающая констраст и начинающаяся обычно со слова \"но\" (англ. `but`) может иногда опускаться.\n\n*Не перепутайте конструкции* `used to + inifinitive` и `be used to + gerund` (о ней будет дальше)!\n\nНапример\n```cpp\n- I used to live with my family, but now I rent an apartment. // Раньше я жил с родителями, но сейчас я снимаю квартиру.\n- I used to date her, but we broke up. // Раньше я встречался с ней, но мы расстались\n- I used to wear jeans. // Раньше я носил джинсы (а сейчас не ношу).\n- I used to come here often when I was young. // Раньше я часто бывал здесь, когда был молод (а сейчас бываю редко).\n- People used to send letters to each other. // Раньше люди посылали друг другу письма (а сейчас они так не делают).\n- Before I started cycling, I used to go to work by public transport. // Прежде, чем я начал ездить на велосипеде, я добирался на работу на общественном трансопрте.\n```\n\n### Отрицательная форма `used to` - `didn't use to`\nЧаще всего используется отрицательная форма `didn't use to`.\n```cpp\n- It didn’t use to be so crowded in the shops as it is nowadays. // Раньше магазины не были так переполнены людьми, как в наши дни.\n- You didn’t use to smoke. // Раньше ты не курила (а сейчас куришь)\n```\nНо также существует и более *формальная версия* `used not to + infinitive`.\n```cpp\n- He used not to live as poorly as he does now. // Он не привык жить так бедно, как живёт сейчас\n- I used not to play football. // Раньше я не играл в футбол (а сейчас играю).\n```\n\n### Вопросительная форма `used to` - `did someone use to`\n```cpp\n- Did you use to work with her? // Ты раньше работал с ней?\n- Didn’t you used to dance? // Раньше вы не танцевали?\n```\n\n### Выразительная форма `did used to`\n```cpp\n- We never used to speak a lot with our neighbours, but we did used to say hello to them. // Мы никогда не говорили много с нашими соседями, но мы всегда говорили им \"привет\".\n```\n\n### Question tags\nДля создания наводящих вопросов с конструкцией `used to` используют вспомогательный глагол `did`.\n```cpp\n- You used to work at home, did you? // Раньше ты работал дома, верно?\n- You used to sleep a lot, didn't you? // Раньше ты много спал, не так ли?\n- She didn't use to live here, did she? // Раньше она не жила здесь, не так ли?\n```\n\n### Различие между `used to` и `would`\n\nИ `used to`, и `would` могут *использоваться* для *рассказа* о *привычках прошлого*. *Иногда* они *ипользуются вместе* в *предложении*, тогда обычно `used to` выходит вперёд, чтобы***задать сцену рассказа*.\n> When we were kids, we used to invent amazing games. We would imagine we were the government and we would make crazy laws that everyone had to obey.\n```cpp\n// Раньше, когда мы были деньми, мы изобретали удивительные игры. \n// Мы воображали, что мы руководим правительством и издаём сумашедшие законы, которым все должны подчиняться.\n```\n\n*В отличии* от `would` конструкция `used to` используется для *описания ситуации*, которая *уже не актуальна*.\n```cpp\n// Можно сказать:\n- I used to live in Minsk. // Я жил в Минске (а сейчас не живу)\n// Но нельзя сказать\n- I would live in Minsk. // Я бы жил в Минске. (теряется смысл, заключающийся в контрасте между прошлым и настоящим)\n```\n\n## Конструкции `be used to` и `get used to`\n\nBe used to means ‘be accustomed to’ or ‘be familiar with’. It can refer to the past, present or future. We follow be used to with a noun phrase, a pronoun or the -ing form of a verb:\n\nКонструкция `be used to + gerund` означает \"привыкнуть к чему-то\" (англ. `be accustomed to`) или \"быть знакомым с чем-то\" (англ. `be familiar with`). То есть для вас что-то было вновинку, но теперь вы кое-как привыкли к этому, познакомились, освоились.\n\nКонструкция `get used to + gerund` означает \"привыкать к чему-то\" (англ. `get accustomed to`) или \"знакомиться с чем-то\" (англ. `get familiar with`). То есть вы *находитесь в процессе привыкания* к *чему-то новому*, но ещё *не привыкли окончательно*.\n\n*Обе конструкции могут использоваться* в *любом времени* (настоящем, прошлом, будущем) и *только с герундием* (`-ing`) *после* частицы `to`.\n\nВообще говоря, *разница между* этими *двумя конструкциями выше невелика*: она *кроется* в *разнице между глаголами* `be` и `get`.\n\n```cpp\n- I find it hard to get used to going to bed early. // Я нахожу это сложным привыкнуть ложиться спать рано. (ещё не привык, но пытаюсь)\n- I'm not used to sleeping outdoors. I always sleep under a roof. // Я не привык спать на открытом воздухе. Я всегда сплю под крышей (даже не пытаюсь привыкать)\n- He is used to eating with his left hand. // Он привык есть левой рукой (уже привык)\n- She is getting used to sleeping alone. // Она привыкает спать одна (процесс привыкания)\n- I can't get used to eating only spicy food. // Я не могу привыкнуть есть только острую пищу (но пытаюсь, потому что нет выбора)\n- You will soon get used to it. // Ты скоро привыкнешь (начал делать что-то, но ещё не привык)\n```\n\nТакже существует более формальная версия для `get used to`: `become used to`:\n```cpp\n- You will soon become used to it. \n```\n\n### Различия между `used to` и `be (get) used to`:\n1) Разница в смысле: `used to` отражает контраст между настоящим и прошлым (привычки в прошлом, которые не имеют места в настоящем), `be (get) used to` ссылается на привыкание к чему-либо в указанный момент времени.\n2) Разница в применимости: `used to` используется только в прошлом, `be (get) used to` может использоваться в люом времени.\n3) Конструкция `used to` *употребляется только с инфинитивом*, конструкция `be (get) used to` - *только с герундием*. \n\n\n\n# Порядок прилагательных (`Order of adjectives`)\n1) `Determiners` - определители (например, `a/an/the`, `this/that/these/those`, `my/his/her/its/their`) и `Quantifiers` - квантификаторы, число/количество (например, `any`, `all`, `many`, `much`, `most`, `some`, `several`, `a few`, `a lot of`, `a little`, `a large amount of`, `none`; `the cardinal numbers`: `one`, `two`, `three` и так далее)\n2) `Opinion` - качество/мнение (например, `lovely`, `beautiful`, `wonderful`, `nice`, `cool`, `unusual`, `brilliant`, `awful`, `delicious`, `charming`, `dirty`)\n3) `Size` - размер (например, `big`, `little`, `small`, `tiny`, `large`, `huge`, `tall`, `short`)\n4) `Physical quality` - физическое качество (например, `thin`, `thick`, `soft`, `rough`, `dry`, `wet`, `cold`, `hot`)\n5) `Age` - возраст (например, `young`, `old`, `youthful`, `new`, `brand new`, `14-year-old`)\n6) `Shape` - форма (например, `round`, `square`, `rectangular`, `long`, `fat`)\n7) `Color` - цвет (например, `white`, `red`, `yellow`, `blue`, `dark`, `light`)\n8) `Origin` - источник происхождения, `Nationality` - национальность (например, `Dutch`, `Japanese`, `Russian`, `Turkish`, `Chinese`)\n9) `Material` - материал (например, `gold`, `rubber`, `metal`, `iron`, `tissue`, `plastic`, `glass`, `silver`, `wooden`, `silk`) \n10) `Purpose` - цель, область применения (например, `cleaning`, `cooking`, `erasing`, `washing`, `winter`, `baseball`)\n* Также могут быть использованы `Intensifiers` - *усилители* (например, `pretty`, `fairly`, `really`, `very`, `quite`, `so`, `extremely`).\nНапример, `so big`, `really beautiful`, `very long`, `extremely strong`). *Усилители* *стоят прямо перед* тем *прилагательным*, которое следует *усилить*.\n\nПримечание: в *разных источниках* `Age (5)` и `Shape (6)` могут стоять в *другом порядке*. Например, на сайте *Cambridge Dictionary*: https://dictionary.cambridge.org/grammar/british-grammar/adjectives-order.\nХотя во многих других источниках порядок другой:\nhttps://www.theguardian.com/commentisfree/2016/sep/13/sentence-order-adjectives-rule-elements-of-eloquence-dictionary#:~:text=The%20rule%20is%20that%20multiple,inviolable%2C%20even%20in%20informal%20speech.\n\nКак по мне, `old round table` звучит лучше, чем `round old table`, *Cambridge* не предоставил *ни одного примера*, где используется и возраст, и форма\n\nНапример,\n```\nYou have such a (1) lovely (2) little (3) old (5) rectangular (6) green (7) French (8) whittling (10) knife\n```\n\nВ своём *учебнике по английскому* я также нашёл *ещё один пункт между* `7) Color` и `8) Origin`: `7.5) Pattern` - шаблон, узор (например: полосатый, \"в полосочку\" - `striped`; клетчатый, \"в клеточку\" - `checkered`; пунктирный, \"в горошик\" - dotted).\n```\nan old (5) blue (7) checkered (8.5) shirt ( - старая клетчатая рубашка\n```\n\nНосители языка *стараются избегать использования усилителей* `very` и `really` в пользу *более* **сильных прилагательных** (англ. `strong adjectives`), которым *усилители не нужны*:\n* `very small` - очень маленький - `tiny` - крошечный\n* `very big` - очень большой - `huge` - огромный\n* `very good` - очень хороший - `excellent` - отличный\n* `very tasty` - очень вкусный - `delicious` - изумительно вкусный\n\nЕщё примеры\n```\nShe was a (1) beautiful (2), tall (3), thin (4), young (5), black-haired (7), Scottish (8) woman.\nI like that (1) really big (*3) old (5) black (7) daddy's (8) jacket.\nThere were three (1) imaginary big (3) round (6) yellow (7) plastic (9) balls.\nIt was a (1) beautiful (2) cold (4) evening.\nHe has lost his (1) awful (2) new (5) baseball (10) cap\nIt's my (1) favorite (2) winter (10) coat\n```\n\n\n\n# Сослагательное наклонение (`Subjunctive`)\n\nКонструкция:\n\n* `It's (high) time\n```js\nIt's (high) time + PAST SUBJUNCTIVE\n```\n\n<!-- it's (high) time + subject + past simple to say that something should be done now or in the future. -->\n\n\n"
  },
  {
    "path": "in-progress/Protocols.md",
    "content": "- [HTTP](#http)\n  - [О протоколе](#о-протоколе)\n  - [Связь между URI, URL и URN](#связь-между-uri-url-и-urn)\n  - [Формат сообщений](#формат-сообщений)\n  - [HTTP-методы](#http-методы)\n  - [Идемпотентность и безопасность HTTP-методов](#идемпотентность-и-безопасность-http-методов)\n  - [Разница между HTTP и HTTP/2](#разница-между-http-и-http2)\n  - [REST](#rest)\n\n# HTTP\n\n## О протоколе\n**HyperText Transfer Protocol** (HTTP, протокол передачи гипертекста) — *протокол прикладного уровня передачи данных*.\nВ *основу* данного протокола входит технология **Клиент-Сервер**.  \n*Клиенты* и *серверы обмениваются* *сообщениями* по схеме **запрос-ответ** (request–response): *клиент отправляет запрос*, *сервер возвращает ответ*.\n\n*HTTP* является *общим языком* и *набором правил* для *клиента* и *сервера*.\n\n*HTTP* определяет\n* *синтаксис* (формат данных и кодирование),\n* *семантику* (смысловое значение, связанное с синтаксисом),\n* *время* (скорость и последовательность).  \n\n*Каждый HTTP-запрос и ответ* считается отдельной **HTTP-транзакцией**.\n\n*HTTP* основан на **текстовых данных** — **сообщениях**, состоящих из *битов текста*.  \n*Каждое сообщение* состоит из *заголовка* (header) и *тела* (body).\n\n*HTTP* — протокол *прикладного* уровня: *уровень абстракции*, стандартизирующий взаимодействие хостов.  \nСам *HTTP не передаёт данные*, а обращается за помощью к протоколу TCP / IP, чтобы получить запрос и ответ.  \n\nОсновным объектом манипуляции в HTTP является ресурс, на который указывает URI в запросе клиента.  \n\nОдной из важных особенностей HTTP является то, что есть возможность задавать параметры в запросах и ответах: формат, кодировку и прочее.  \nНесмотря на то, что HTTP является текстовым протоколом, он может обмениваться двоичными данными благодаря возможности кодировки.\n\nДля идентификации ресурсов HTTP использует глобальные URI.  \n\n*HTTP не хранит своё состояние* в отличие от многих других протоколов.  \nЭто означает, что сохранение промежуточного состояния между парами запрос-ответ отсутствует и HTTP не осведомлён о предыдущих запросах и ответах.  \n\nКлиент, сервер и браузер могут самостоятельно хранить состояние, опираясь на данные последних запросов и ответов.  \nНапример, на стороне клиента могут храниться токены или куки.  \nСервер может хранить IP-адреса клиентов.  \nБраузер может отслеживать задержки ответов.  \n\nОбычные *HTTP-запрос* и *HTTP-ответ* *не зашифрованы и уязвимы* для различных типов атак.  \nЭту проблему решает HTTPS (HTTP Secure) — безопастная версия протокола, использующая протол TLS или SSL для шифрования.\n\n**SSL** - это **протокол безопасности**, который позволяет клиенту и серверу безопасно  \nобмениваться данными по сети, предотвращая подслушивание и фальсификацию, в то  \nвремя как сообщение передается по сети.\n\nДругие протоколы прикладного уровня: FTP, SMTP.\n\n## Связь между URI, URL и URN\n\n**Uniform Resource Identifier** (URI) — символьная строка, позволяющая идентифицировать какой-либо ресурс: документ, изображение, электронную почту и прочее.   \nПрежде всего, речь идёт о ресурсах сети Интернет и Всемирной паутины.    \nURI предоставляет простой и расширяемый способ идентификации ресурсов.  \n\n**Uniform Resource Locator** (URL) — URI, предоставляющий информацию о местонахождении ресурса.  \n\n**Uniform Resource Name** (URN) — URI, который только идентифицирует ресурс в определённом пространстве имён (в определённом контексте), но не указывает его местонахождение.  \nПример: urn:ISBN:0-395-36341-1 — URI, указывающий на ресурс (книгу) 0-395-36341-1 в пространстве имён ISBN.\n\nПоскольку URI не всегда указывает на то, как получить ресурс, в отличие от URL, а только идентифицирует его, это даёт возможность описывать с помощью RDF (Resource Description Framework) ресурсы, которые не могут быть получены через Интернет (например, личность, автомобиль, город и проч.).\n\n## Формат сообщений\n\nКак отмечалось ранее, клиент и сервер общаются сообщениями. \n\nКаждое сообщение содержит заголовки с метаданными и иногда тело с данными.\n\n### Заголовки сообщения\n\n**HTTP-заголовки** (HTTP Headers) обычно содержат **метаданные** (metadata), то есть *данные о данных*.  \n\n```http\nGET /url \nHeader-Name: header-value\nAnother-Header-Name: header-value\n```\n\n*Метаданные* включают в себя:\n- Тип запроса (request type): `GET`, `POST`, `PUT`, `DELETE` и другие.\n- Путь запроса (URL).\n- Код статуса запроса (status code): `100` - `5xx`.\n- Тип контента в запросе (content-type).\n- Данные с клиента (User-agent): браузер, ОС и прочее.\n- Куки (Cookie): текущая сессия.\n\n### Тело сообщения\n\n**Тело сообщения** (Message body) содержит *данные*. \n\nОно отделяется от заголовков пустой строкой.\n```http\nPOST /url \nContent-Type: application/json\n\nBody data\n```\nВ зависимости от HTTP-метода тело может отсутствовать.\n\n## HTTP-методы\n\n**HTTP-методы**, **HTTP-глаголы** (verbs) сообщают серверу, что требуется сделать с данными, хранящимися по указанному URL.  \n\n\nКогда *клиент делает запрос*, он *указывает тип запроса*, используя один из следующим методов:\n- [GET](#get)\n- [HEAD](#head)\n- [POST](#post)\n- [PUT](#put)\n- [DELETE](#delete) \n- [OPTIONS](#options)\n- [PATCH](#patch)\n\n### GET\n\n**Метод GET** используется для *чтения информации с сервера по данному URL*.\n\n*Метод GET* доступен *только для чтения*. По запросу `GET` данные на сервере не должны изменяться, сервер должен отправлять данные без изменений. \n```http\nGET /user/friends\n```\n\n*Метод GET* не имеет тела запроса* (Request Body). Все данные запроса GET содержатся в URL и видны всем, поэтому не стоит передавать секретную информацию в методе GET. \n\n*Статус-коды метода GET*\n* **200** (OK) — *запрашиваемый ресурс найден*.\n* **404** (Not found) — *запрашиваемый ресурс не найден*.\n\n### HEAD\n\n**Метод HEAD** идентичен *методу GET*, поскольку запрашивает у сервера такой же ответ, но без тела ответа (Response Body). \n```http\nHEAD /user/friends\n```\nПри помощи *метода HEAD* можно получить мета-данные, хранящиеся в заголовке ответа (Reponse Headers), и при этом напрасно не передавать данные с сервера.\n\nКак и *метод GET*, *метод HEAD* является *безопасным* и *идемпотентным*.\n\n### POST\n\n**Метод POST** обычно используется для *создания нового ресурса*.  \n```http\nPOST /users/create\n\nusername=max&sex=male\n```\n\nПри поздании нового ресурса подразумевается создание нового подчинённого ресурса (subordinate) для ресурса, заданного по URL. Например, в случае примера выше это создание нового пользователя для ресурса `users`, содержащего информацию о пользователях.\n\n*Статус-коды GET-запроса*\n* **201** (Created) — *подчинённый ресурс создан успешно для указанного ресурса*.\n* **404** (Not found) — *запрашиваемый ресурс не найден*.\n\n### PUT \n\n**Метод PUT** используется для обновления ресурса, идентифицируемого URL-адресом, с использованием информации в теле запроса. \n```http\nPUT /users/17\n\nusername=dana&sex=female\n```\n\nPUT также может быть использован для создания нового ресурса.  \n\nPUT запросы отвечают статусом 200 если запрос успешно обновлён и 404 если ресурс не найден.\n\n### DELETE\n\n**Метод DELETE** используется для удаления ресурса, указанного в URL.  \n\nНа запросы DELETE в ответ удаляется код состояния 200 (ОК), если он был успешно  \nудален, или 404 (НЕ НАЙДЕН), если удаляемый ресурс не может быть найден.\n\nhttps://medium.freecodecamp.org/how-the-web-works-part-iii-http-rest-e61bc50fa0a\n\n### OPTIONS\n\n## Идемпотентность и безопасность HTTP-методов\n\n**Идемпотентность** (Idempotence) — свойство объекта (или операции) при повторном применении операции к объекту (или при повторном вызове операции) давать тот же результат, что и при первом. \n\nТаким образом, запросы GET считаются безопасными операциями, потому что вызов его один  \nраз или вызов 20 раз будет иметь тот же эффект.\n\nКроме того, **запросы GET являются идемпотентными**.  \nЭто означает, что отправка нескольких запросов GET на один и тот же URL-адрес должна  \nвызывать тот же эффект, что и один запрос GET, поскольку запрос GET просто запрашивает  \nданные у сервера и фактически не изменяет какие-либо данные на сервере.\n\n**POST не является ни безопасным, ни идемпотентным**. Это связано с тем, что выполнение  \nдвух или более одинаковых запросов POST может привести к созданию двух новых идентичных  \nресурсов.\n\nЗапросы PUT не считаются безопасными операциями, поскольку они изменяют состояние на сервере.  \nОднако PUT идемпотентен, потому что несколько идентичных запросов PUT на обновление ресурса  \nдолжны иметь тот же эффект, что и первый.\n\nЗапросы DELETE являются идемпотентными, потому что если вы удаляете ресурс,  \nон удаляется, и даже если вы делаете несколько идентичных запросов DELETE,  \nрезультат остается тем же: удаленный ресурс.\n\nСкорее всего, вы просто получите сообщение об ошибке 404, если отправите запрос  \nDELETE более одного раза для одного и того же ресурса, потому что сервер не сможет  \nнайти его после его удаления.\n\n## Разница между HTTP и HTTP/2\n\n<!--\nhttps://stackoverflow.com/questions/58498116/why-is-it-said-that-http2-is-a-binary-protocol\n\nBinary is probably a confusing term - everything is ultimately binary at some point in computers!\n\nHTTP/2 has a highly structured format where HTTP messages are formatted into packets (called frames) and where each frame is assigned to a stream. HTTP/2 frames have a specific format, including a length which is declared at the beginning of each frame and various other fields in the frame header. In many ways it’s like a TCP packet. Reading an HTTP/2 frame can follow a defined process (the first 24 bits are the length of this packet, followed by 8 bits which define the frame type... etc.). After the frame header comes the payload (e.g. HTTP Headers, or the Body payload) and these will also be in a specific format that is known in advance. An HTTP/2 message can be sent in one or more frames.\n\nBy contrast HTTP/1.1 is an unstructured format made up of lines of text in ASCII encoding - so yes this is transmitted as binary ultimately, but it’s basically a stream of characters rather than being specifically broken into separate pieces/frames (other than lines). HTTP/1.1 messages (or at least the first HTTP Request/Response line and HTTP Headers) are parsed by reading in characters one at a time, until a new line character is reached. This is kind of messy as you don’t know in advance how long each line is so you must process it character by character. In HTTP/1.1 the HTTP Body’s length is handled slightly different as typically is known in advance as a content-length HTTP header will define this. An HTTP/1.1 message must be sent in its entirety as one continuous stream of data and the connection can not be used for anything else but transmitting that message until it is completed.\n\nThe advantage that HTTP/2 brings is that, by packaging messages into specific frames we can intermingle the messages: here’s a bit of request 1, here’s a bit of request 2, here’s some more of request 1... etc. In HTTP/1.1 this is not possible as the HTTP message is not wrapped into packets/frames tagged with an id as to which request this belongs to.\n\nI’ve a diagram here and an animated version here that help conceptualise this better. -->\n\n## REST\n\n**REST** (Representational State Transfer) — архитектурный стиль для разработки  \nприложений.\n\nЕго автор, Рой Филдинг, выступал за использование стандартных HTTP методов так,  \nчтобы придать запросам определённый смысл. А также за то, что между кодами ответов\nи самими ответами должен быть определённый смысл.\n\nОсновная идея заключается в том, что вы используете протокол «без сохранения  \nсостояния», «клиент-сервер», «кэшируемый» для выполнения вызовов между компьютерами  \n- и чаще всего этот протокол является HTTP. \n\nЭто просто причудливый способ сказать, что REST дает вам набор ограничений для  \nразработки приложения. Эти ограничения помогают сделать систему более производительной,  \nмасштабируемой, прозрачной и надежной.\n\n**Список ограничений**:\n* **Uniform interface (единый интерфейс)**.\n* **Stateless (отсутствие состояния)**. То есть все данные о состоянии, необходимые  \nдля обработки клиентского запроса, должны содержаться в самом запросе, и сервер должен  \nотправлять все необходимые данные состояния обратно клиенту через сам ответ. \n\nНаличие такой системы без сохранения состояния делает приложения намного более  \nмасштабируемыми, потому что ни одному серверу не нужно беспокоиться о том, чтобы  \nподдерживать одно и то же состояние сеанса в течение нескольких запросов.  \nВсе необходимое для получения данных о состоянии доступно в самом запросе и ответе.\n"
  },
  {
    "path": "in-progress/ReactNative.md",
    "content": "# Основы React Native\n\n**React Native** — фреймворк, TODO\n\n"
  },
  {
    "path": "in-progress/VSCode.md",
    "content": "## LF vs CRLF\n\n1) ctrl + P\n2) > settings\n3)\nFor LF (line feed):\n```json\n{\n   \"files.eol\": \"\\n\",\n}\n```\nFor CRLF (carrige return and line feed):\n```json\n{\n    \"files.eol\": \"\\r\\n\",\n}\n```\n![image](https://github.com/Max-Starling/Notes/assets/22237384/e4aebd55-0ac7-452c-bb04-4a4257022106)\n"
  },
  {
    "path": "in-progress/npm.md",
    "content": "- [Зависимости в `package.json`](#зависимости-в-packagejson)\n  - [Нормальные зависимости `dependencies`](#нормальные-зависимости-dependencies)\n  - [Зависимости разработчика `devDependencies`](#зависимости-разработчика-devdependencies)\n  - [Одноранговые зависимости `peerDependencies`](#одноранговые-зависимости-peerdependencies)\n    - [`peerDependenciesMeta`](#peerdependenciesmeta)\n - [Другие свойства `package.json`](#другие-свойства-packagejson)  \n    - [Переопределения `overrides`](#переопределения-overrides)\n    - [Скрипты `scripts`](#скрипты-scripts)\n      - [Передача параметров скрипту](#передача-параметров-скрипту)\n\n# Зависимости в `package.json`\n\n**Зависимостями** (англ. `devendencies`) в приложении выступают *сторонние пакеты* (модули), которые *декларируются* в `package.json`. Чаще всего зависимости скачиваются из [NPM Registry](https://www.npmjs.com/) \n\n## Нормальные зависимости `dependencies`\n\nСвойство `dependencies` (англ. `зависимости`) содержит *зависимости приложения*, то есть пакеты (модули), которые используются в приложении.\n\nКаждая зависимость представлена в виде пары *двух строковых значений*:\n1) *Уникальное название пакета*. Например, [`\"explicit\"`](https://www.npmjs.com/package/explicit), [`\"implicit\"`](https://www.npmjs.com/package/implicit).\n2) *Версия пакета*. *Версия пакета* может быть задана *явно* (то есть *тремя конкретными числами*, например, `2.3.21`) или *неявно* (например, в виде *промежутка*: `>= 2.0.0 < 3.0.0`, `>= 2.0.0 < 3.0.0`, `2.x`, `^2.3.21`)\n```json\n{\n  \"name\": \"my-project\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"explicit\": \"2.3.21\",\n    \"implicit\": \"^2.3.21\",\n    \"implicit-foo\": \"2.0.0 - 3.0.0\",\n    \"implicit-bar\": \">= 2.0.0 < 3.0.0\",\n    \"implicit-baz\": \"2.x\"\n  }\n}\n```\n\nДалее *детально разберём все возможные способы задания версии*.\n\n\n## Зависимости разработчика `devDependencies`\n\n\n## Одноранговые зависимости `peerDependencies`\n- [`peerDependenciesMeta`](#peerdependenciesmeta)\n\nЕсли в некотором пакете `foo` указаны `peer dependencies` (рус. `одноранговые зависимости`, иногда `одноуровневые` или `прямые`), то *при установке* пакета `foo` в ваше *приложение* *необходимо будет* также *установить* все *зависимости* из этого *списка*.\n\nТаким образом, если пакет `foo` имеет следующий `package.json`\n```js\n{\n  \"name\": \"foo\",\n  \"version\": \"2.1.1\",\n  \"peerDependencies\": {\n    \"bar\": \"^18.0.0\",\n    \"baz\": \"7.1.x\"\n  }\n} \n```\nто `package.json` вашего приложения после установки `foo` и его одноранговых зависимостей с помощью `npm install` должен выглядить примерно следующим образом\n```js\n{\n  \"name\": \"my-awesome-app\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"foo\": \"2.1.1\",\n    \"bar\": \"^18.0.0\",\n    \"baz\": \"7.1.x\"\n  }\n} \n```\nа *дерево зависимостей*, например, так\n```terminal\n├── ...\n├── foo@2.1.1\n├── bar@18.0.3\n└── baz@7.1.1\n```\nЭто будет *отображено* у вас в *консоли* и в несколько другом виде *отражено* в `package-lock.json`.\n\nЕсли же пакеты `bar` и `baz` *не будут установлены*, то `npm` *выдаст ошибку*.\n\nПример *реальной ошибки* со [*Stackoverflow*](https://stackoverflow.com/questions/35207380/how-to-install-npm-peer-dependencies-automatically):\n```terminal\nnpm install --save angular2\ntemp@1.0.0 /Users/doug/Projects/dougludlow/temp\n├── angular2@2.0.0-beta.3 \n├── UNMET PEER DEPENDENCY es6-promise@^3.0.2\n├── UNMET PEER DEPENDENCY es6-shim@^0.33.3\n├── UNMET PEER DEPENDENCY reflect-metadata@0.1.2\n├── UNMET PEER DEPENDENCY rxjs@5.0.0-beta.0\n└── UNMET PEER DEPENDENCY zone.js@0.5.11\n\nnpm WARN angular2@2.0.0-beta.3 requires a peer of es6-promise@^3.0.2 but none was installed.\nnpm WARN angular2@2.0.0-beta.3 requires a peer of es6-shim@^0.33.3 but none was installed.\nnpm WARN angular2@2.0.0-beta.3 requires a peer of reflect-metadata@0.1.2 but none was installed.\nnpm WARN angular2@2.0.0-beta.3 requires a peer of rxjs@5.0.0-beta.0 but none was installed.\nnpm WARN angular2@2.0.0-beta.3 requires a peer of zone.js@0.5.11 but none was installed.\n```\n\n*Автоматическая установка* `peer dependencies` была *удалена* из `NPM v3`, поскольку *создавала больше проблем*, чем *решала*. Тем не менее, она была *возвращена* в версии `NPM v7` - начиная с этой версии все `peer dependencies` снова *устанавливаются автоматически*. На версиях `NPM 4, 5, 6` необходимо устанавливать каждую `peer dependency` вручную при помощи `npm install package_name@version`.\n\n### `peerDependenciesMeta`\nДля `peer dependencies` есть свойство `peerDependenciesMeta`, хранящее метаданные об одноранговых зависимостях. Эти данные позволяют объяснить NPM, как именно используется та или иная `peer dependency`.\n\nВ основном, данные `peerDependenciesMeta` используют, чтобы пометить некоторую `peer dependency` вашего пакета как *необязательную*, *опциональную* (англ. `optional`)  для установки зависимость. Это позволит *избежать предупреждений* (ошибок) о неустановленных `peer dependencies`.\n\nНапример, ниже `peer dependency` `baz` помечена как опциональная зависимость для пакета `foo`, а значит, что если при установке пакета `foo` в ваше приложение не установить зависимость `baz`, то ошибки не будет.\n```js\n{\n  \"name\": \"foo\",\n  \"version\": \"2.1.1\",\n  \"peerDependencies\": {\n    \"bar\": \"^18.0.0\",\n    \"baz\": \"7.1.x\"\n  },\n  \"peerDependenciesMeta\": {\n    \"baz\": {\n      \"optional\": true\n    }\n  }\n}\n```\n\n\n# Другие свойства `package.json`\n\n## Переопределения `overrides`\n\nУказание свойства `overrides` (рус. `переопреедения`) позволяет *принудительно установить* (*перезаписывать*) *версии* некоторых *пакетов* в приложении.\n\nВозможны несколько случаев применения.\n\n<!-- Например, необходимо, чтобы в приложении использоваласть только одна версия пакета `foo` безоговорочно, тогда\n```json\n{\n  \"overrides\": {\n    \"foo\": \"1.0.0\"\n  }\n}\n```\n\nустановить во всём проекте единую версию конкретного пакета вне зависимости от того, какая \n\nOverrides provide a way to replace a package in your dependency tree with another version, or another package entirely. These changes can be scoped as specific or as vague as desired.\n\nTo make sure the package foo is always installed as version 1.0.0 no matter what version your dependencies rely on: -->\n\nhttps://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides\n\n## Свойство `scripts` (скрипты)\n\n### Передача параметров скрипту\n- [UNIX (Linux, Mac)](#unix-linux-mac)\n- [Windows](#windows)\n- [Кроссплатформенное решение](#кроссплатформенное-решение)\n\n#### UNIX (Linux, Mac)\n* *Передача строки*\n```JSON\n\"scripts\": {\n  \"start:prod\": \"NODE_ENV=production npm start\"\n}\n```\n```js\nprocess.env.NODE_ENV // 'production'\n```\n* *Передача числа*\n```JSON\n\"scripts\": {\n  \"start:prod\": \"PORT=5001 npm start\"\n}\n```\n```js\nparseInt(process.env.PORT, 10) // 5001\n```\n#### Windows\n```cmd\n\"scripts\": {\n  \"start:prod\": \"set NODE_ENV=production& npm start\"\n}\n```\n```js\nprocess.env.NODE_ENV // 'production'\n```\nЕсли поставить пробел перед `&`\n```cmd\n\"scripts\": {\n  \"start:prod\": \"set NODE_ENV=production & npm start\"\n}\n```\n```js\nprocess.env.NODE_ENV // 'production ' (появляется пробел на конце)\n```\n#### Кроссплатформенное решение\n* Передача *одной переменной*.\n```cmd\nnpm i --save-dev cross-env\n```\n```cmd\n\"scripts\": {\n  \"start:prod\": \"cross-env NODE_ENV=production npm start\"\n}\n```\n* Передача *нескольких переменных*.\n```cmd\n\"scripts\": {\n  \"start:prod\": \"cross-env NODE_ENV=production PORT=5001 npm start\"\n}\n```\n"
  },
  {
    "path": "tech/ApacheVelocity.md",
    "content": "\n## Установка значения переменной\n\n```js\n#set($size = 10)\n```\n```js\n#set($title = \"Notes\")\n```\n\n## Использование переменной\n```js\n{\n  \"size\": $size,\n  \"title\": \"$title\"\n}\n```\n\nНельзя использовать выражение в качестве значения как в примере ниже. Нужно выносить выражение в переменную и использовать её. Иначе в значение попадёт само выражение, а не его результат.\n```js\n{\n  \"size\": $size + 1,\n}\n/* результат 10 + 1, а не 11 */\n```\n\n## Инкремент\n```js\n#set($counter = 0)\n#set($counter = $counter + 1)\n```\n\n## Условный оператор\n\n## if..else\n```js\n#if($a > $b)          \n    #set($max = $a)\n#else\n    #set($max = $b)\n#end\n```\n\n\n## elseif\n```js\n#if($counter == 0)\n  #set($str = \"zero\")\n#elseif($counter == 1)\n  #set($str = \"one\")\n#else\n  #set($str = \"another number\")\n#end\n```\n\n## Краткая запись\n```js\n#if($a > $b)it's true!#{else}it's not!#end\n```\n## Цикл\n```js\n#foreach($entry in $array)\n  #set($prop = $entry.get(\"property\"))\n#end\n```\n\n## Массив\n\n### Объявление\n```js\n#set($array = [42, \"a string\", $counter])\n```\n### Обращение к элементу\n```js\n$array.get(1)\n$array[1]\n```\n### Проверка на пустоту\n```js\n#if ($myMap.isEmpty()) #end\n#if ($myMap.size() == 0) #end\n```\n### Добавление элементов\n```js\n$array.add(17)\n```\n"
  },
  {
    "path": "tech/Chai-Mocha.md",
    "content": "## Get started\n\n* Install packages  \n```npm\nnpm install --save-dev chai\nnpm install --save-dev mocha\n```\n* Set up the test script in the package.json\n  * --timeout <default it block timeout in ms> (number, default: 2000)\n  * --recursive <path regexp to test files> (string, dafault: './test/*.js')\n```JSON\n\"scripts\": {\n  \"test\": \"mocha --timeout 10000 --recursive './test/**/*.spec.js'\"\n}\n```\n* Create `your-filename-here.spec.js` file in the test folder\n* Start using chai сhai `expect` or chai `should`\n```js\nconst expect = require('chai').expect;\n// or\nconst chai = require('chai');\nchai.should();\n```\n\n### \"expect\" and \"should\" syntax\n```js\nconst getArticles = () => [/* ... */];\n\nexpect(getArticles()).to.be.an('array');\n\ngetArticles().should.be.an('array');\n```\n## Basic functions\n\n```js\nexpect(something).to.equal('value'); // something === 'value'\n\nexpect(something).to.be.a('type'); // typeof something === 'value, but\nexpect([]).to.be.an('array'); // success\n\nexpect(object).to.have.property('property'); // Object.keys(object).includes('property');\n\nconst keys = [/* ... */];\nexpect(object).to.have.all.keys(...keys); // keys.every(key => Object.keys(object).includes(key));\nexpect(object).to.have.any.keys(...keys); // keys.some(key => Object.keys(object).includes(key));\n```\n\n\n## Basic structure for unit tests\nSimple module:\n```js\n  module.exports = {\n    isInteger: number => number === parseInt(number, 10),\n    getRandomNaturalNumber: (max = 1000) => Math.floor(Math.random() * (max - 1)) + 1,\n  };\n```\nTests for the module:\n```js\nconst expect = require('chai').expect;\nconst numberFuncs = require('/*...*/');\n\ndescribe('numberFuncs', () => {\n  describe('isInteger', () => {\n    it('should always return a boolean', () => {\n      expect(numberFuncs.isInteger('article')).to.be.a('boolean');\n    });\n\n    it('should return true', () => {\n      expect(numberFuncs.isInteger(5)).to.equal(true);\n    });\n\n    it('should return false', () => {\n      expect(numberFuncs.isInteger(7.5)).to.equal(false);\n    });\n  });\n\n  describe('getRandomNaturalNumber', () => { \n    it('should return a natural number', () => {\n      const natural = numberFuncs.getRandomNaturalNumber();\n      expect(natural).to.be.an('number');\n      expect(natural).to.be.above(0);\n      expect(numberFuncs.isInteger(natural)).to.equal(true);\n    });\n  });\n});\n```\n\n## Basic structure for API tests\nSimple Express API:\n```js\nconst Express = require('express');\nconst app = Express();\n\nconst checkCredentials = (username, password) => { /* ... */ };\nconst createAuthToken = (username) => { /* ... */ };\n\napp.get('/login', async (req, res) => {\n  const { username, password } = req.params;\n  const user = await checkCredentials(username, password);\n  if (user) {\n    const token = await createAuthToken(username);\n    res.status(200).send({ ...user, token });\n  } else {\n    res.status(400).send();\n  }\n});\n\nconst ARTICLES = [/* ... */];\nconst getAuthTokenFromHeaders = (headers) => { /* ... */ };\nconst checkAuthToken = (token) => { /* ... */ };\n\napp.get('/articles', async (req, res) => {\n  const authToken = getAuthTokenFromHeaders(req.headers);\n  const isAuthorized = await checkAuthToken(authToken);\n  if (isAuthorized)) {\n    res.status(200).send(ARTICLES);\n  } else {\n    res.status(400).send();\n  }\n});\n```\nTests for the API above:\n```js\nconst axios = require('axios');\nconst expect = require('chai').expect;\n\nconst API = axios.create({ baseURL: 'http://localhost:3002' });\nconst POST = ({ endpoint, body }) => API.post(endpoint, body));\nconst GET = ({ endpoint, query, authToken }) => API.get(\n  endpoint,\n  { params: query },\n  { headers: { 'Authorization': `Bearer ${authToken}` },\n});\n\nlet authToken = '';\n\ndescribe('POST /login', () => {\n  const credentials = { username: 'admin', password: 'admin' };\n\n  it('should return status 200 and object containing auth token', async () => {\n    const response = await POST({ endpoint: '/login', body: credentials });\n    expect(response).to.be.an('object');\n    const { status, data } = response;\n    expect(status).to.equal(200);\n    expect(data).to.be.an('object');\n    const { token } = data;\n    expect(token).to.be.a('string');\n    authToken = token;\n  });\n});\n\ndescribe('GET /articles', () => {\n  it('should return an array of objects containing title, text and username', async () => {\n    const response = await GET({ endpoint: '/articles', authToken });\n    expect(response).to.be.an('object');\n    const { status, data } = response;\n    expect(status).to.equal(200);\n    expect(data).to.be.an('array');\n    const article = data[0];\n    expect(article).to.be.an('object');\n    expect(article).to.have.all.keys('title', 'text', 'username');\n  });\n});\n\n\n```\ntry - catch\nbefore, beforeAll, after, afterAll\nthis (timeouts, skip)\n"
  },
  {
    "path": "tech/ESlint-TSlint-Prettier.md",
    "content": "## О линтерах\n\n**Линтер** (англ. `linter`) в программировании — это программа (инструмент), которая анализирует код приложения с целью выявления потенциальных ошибок, проблем, уязвимостей, нарушений код стайла и так далее до реального выполнения кода.\n\nЛинтеры позволяют улучшить качество кода вашего приложения.\n\nНастраивая линтер в своём проекте, вы сами задаёте для него правила с учётом уже встроенных рекомендаций, после чего каждый разработчик на проекте автоматически подстаивается под них.\n\nВсе мы разные, у каждого из нас свой опыт, своя точка зрения. Кому-то нравятся пробелы, кому-то - табы. Кто-то любит точки с запятой, закрывающие запятые, фигурные скобки, а кто-то топит за минимализм символов. Линтер позволяет установить соглашение о едином стиле между разработчиками.\n\n\n### Как использовать линтер\nЧаще всего линтеры встраивают в качестве расширения в IDE (например, [расширение Eslint в VSCode](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)), в этом случае ошибки будут подсвечиваться прямо у вас в коде.\n\nЕсли же вы приверженец терминала, то для Eslint также предлагает CLI решение https://eslint.org/docs/latest/use/command-line-interface\n```py\n# запуск линтинга одного файла\neslint app.js\n# запуск линтинга в папке\neslint src/*\n```\n\n### Алгоритм линтера\n\n* Производится статический анализ кода (то есть код анализируется без реального выполнения).\n* Проанализированный код парсится, затем строится абстракное синтаксическое дерево (англ. `abstract syntax tree`, `AST`).\n* Для каждой вершины построенного дерева прогоняются все сконфигурированные на проекте правила линтера.\n* Результат отображается на экране (например, в консоли или в IDE) \n\n### Забавная ассоциация для наилучшего запоминания\n\n[Англ.](https://www.merriam-webster.com/dictionary/linters) `lint`, `linters` переводится как ворс - пушок, который удаляют с семян хлопка во время его обработки. \n\n[Англ.](https://www.merriam-webster.com/dictionary/linters) `linter` - это машина, которая удаляет ворсинки с хлопка во время его обработки.\n\nТо есть хлопковый линтер \"причёсывает хлопок\", избавляясь от ворсинок, в то время, как линтер в программировании \"причёсывает\" наш код, устраняя его неровности (недостатки).\n\n## О форматтерах\n**Форматтер** (англ. `formatter`) в программировании — это программа (инструмент), которая форматирует код. \n\nТак же, как и линтер, форматтер анализирует код и строит абстрактное синтаксическое дерево (англ. `AST`)\n\n## Сравнение линтера и форматера \n\nЛинтеры анализируют\n\nИз гугла:\n> Linting is distinct from Formatting because linting analyzes how the code runs and detects errors whereas formatting only restructures how code appears.\n\nС сайта Prettier:\n> Linters usually contain not only code quality rules, but also stylistic rules. Most stylistic rules are unnecessary when using Prettier, but worse – they might conflict with Prettier! Use Prettier for code formatting concerns, and linters for code-quality concerns, as outlined in Prettier vs. Linters.\n\n## Зачем нужны линтеры и форматеры\n\nЛинтеры и форматеры позволяют задать общий стиль всему проекту, а поэтому когда он уже настроен\n* Не нужно тратить время и энергию на извечные споры в комментариях Pull Request-ам, комментариев в целом становится меньше, а значит Pull Requuest-ы мержатся быстрее и чаще. Более того, если постоянно не отвлекаться на такие мелочи как стиль кода при ревью, можно сфокусироваться на действительно важной составляющей кода - бизнес-логике и потенциальных ошибках.\n* Новым людям намного проще влиться в проект и команду, когда правила явно заданы и хорошо описаны. Так они чувствуют себя увереннее и комфортнее, а большинство вопросов отпадают сами, что экономит время каждого члена проекта.\n* Когда линтинг и форматинг происходят автоматически, можно тратить меньше времени на красивое оформление, а значит и писать код быстрее (и качествее, потому что все правила в голове не удержишь).\n\n## Мои предпочтения в настройках линтера с пояснением\n"
  },
  {
    "path": "tech/ErrorHandling.md",
    "content": "# Обработка ошибок в Node.js\n\nОбработка ошибок в приложении на Node.js — одна из важнейших частей разработки, ведь она позволяет предотвратить аварийные ситуации и обеспечивает стабильную работу приложения.\n\nДалее будут представлены несколько подходов для обработки ошибок на Node.js:\n\n## Использование try-catch и async/await\nДля обработки ошибок в асинхронных функциях с `async..await` можно использовать блоки `try..catch`:\n```js\nasync function fetchData() {\n  try {\n    const data = await call();\n    return data;\n  } catch (error) {\n    console.error('Error fething data:', error);\n    throw new Error('Не удалось получить данные');\n  }\n}\n```\nВажно логировать ошибки и выдавать пользователю информативные сообщения, но при этом не раскрывать внутренние детали приложения.\n\n## Централизованная обработка ошибок (Middleware в Express)\nВ Express можно настроить *централизованный обработчик ошибок* с помощью специального middleware:\n```js\nconst express = require('express');\nconst app = express();\n\n// Пример маршрута\napp.get('/data', async (req, res, next) => {\n  try {\n    const data = await call();\n    res.send(data);\n  } catch (error) {\n    next(error); // Передаем ошибку в следующий middleware\n  }\n});\n\n// Централизованный обработчик ошибок\napp.use((err, req, res, next) => {\n  console.error(err.stack);\n  res.status(500).json({ message: 'Internal server error' });\n});\n\napp.listen(3000, () => {\n  console.log('Server is listening at 3000');\n});\n```\n\nЗдесь используется функция `next()`, которая *передает ошибку* в *централизованный обработчик ошибок*.\nЭто позволяет *отделить логику обработки ошибок от основной логики маршрутов*.\n\n## Использование пользовательских классов ошибок\nСоздание пользовательских классов ошибок позволяет делать сообщения более специфичными и легко различать типы ошибок:\n\n```js\nclass NotFoundError extends Error {\n  constructor(message) {\n    super(message);\n    this.name = 'NotFoundError';\n    this.statusCode = 404;\n  }\n}\n\n// Использование пользовательской ошибки\napp.get('/user/:id', async (req, res, next) => {\n  try {\n    const user = await getUserById(req.params.id);\n    if (!user) {\n      throw new NotFoundError('User not found');\n    }\n    res.json(user);\n  } catch (error) {\n    next(error);\n  }\n});\n```\nВ зависимости от нужд приложения можно создавать сколь угодно много классов ошибок, например, для валидации `ValidationError`, аутентификации `AuthError`, таймаутов `TimeoutError` и т.д.\n\nЭто помогает централизованно обрабатывать разные типы ошибок в одном месте, предоставляя соответствующие HTTP-коды и сообщения.\nЗатем эти ошибки можно централизованно отлавливать\n```js\ntry {\n\n} catch (err) {\n  if (err instanceof NotFoundError) { /* ... */ }\n  else if (err instanceof ValidationError) { /* ... */ }\n  else if (err instanceof AuthError { /* .. */}\n}\n```\n\n## Обработка необработанных ошибок и необработанных отклоненных промисов\nДля обработки ошибок, которые не были перехвачены в коде, можно использовать *глобальные обработчики* `uncaughtException` и `unhandledRejection`. В них можно завершить приложение, логировать ошибки и так далее.\n```js\nprocess.on('uncaughtException', (error) => {\n  console.error('Uncaught exception:', error);\n  // Обычно на этом этапе приложение завершает работу, чтобы избежать непредсказуемых состояний\n  process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n  console.error('НUnhandled promise rejection:', reason);\n  // Здесь также можно выполнить какую-то логику, например, логирование, алерты и т.д.\n});\n```\nВажно помнить, что после `uncaughtException` лучше завершить работу приложения, чтобы избежать неконсистентного состояния.\n\n## Логирование ошибок\nЛогирование играет важную роль в обработке ошибок, и его следует реализовать с помощью специализированных библиотек, таких как winston или pino:\n```js\nconst winston = require('winston');\n\nconst logger = winston.createLogger({\n  level: 'error',\n  format: winston.format.json(),\n  transports: [\n    new winston.transports.File({ filename: 'error.log' })\n  ]\n});\n\n// Пример использования логгера\napp.use((err, req, res, next) => {\n  logger.error(err.message);\n  res.status(500).json({ message: 'Ошибка сервера' });\n});\n```\nЛогирование ошибок помогает отслеживать проблемы в продакшене.\nТакже можно настроить отправку уведомлений, если возникла критическая ошибка.\n\n## Валидация входящих данных\n\nИспользуйте библиотеки, такие как Joi или express-validator, чтобы валидировать данные до выполнения бизнес-логики:\n\n```js\nconst { body, validationResult } = require('express-validator');\n\napp.post('/user', [\n  body('email').isEmail(),\n  body('password').isLength({ min: 6 })\n], (req, res, next) => {\n  const errors = validationResult(req);\n  if (!errors.isEmpty()) {\n    return res.status(400).json({ errors: errors.array() });\n  }\n  next();\n});\n```\nЭто позволяет избежать выполнения лишнего кода, если входные данные невалидны.\nПозволяет централизованно обрабатывать ошибки валидации.\nЗаключение\nНаилучшие практики обработки ошибок в Node.js включают использование try-catch, централизованную обработку ошибок с middleware, создание пользовательских ошибок, логирование и валидацию входящих данных. Эти подходы помогают сделать код более надежным, улучшить обслуживание и быстрее находить и исправлять проблемы.\n"
  },
  {
    "path": "tech/Files.md",
    "content": "\n It is available in standard text format and used for saving and transporting the data. JSON helps to transmit data in web applications where the data is sent from server to client and can be viewed on the web page. YAML is used for scripting configuration files and can be incorporated with added programming languages. It is popular for data serialization language, human-readable language, and adaptive. The difference and comparison of JSON and YAML are described in the article.\n\n# JSON\n\nJSON is abbreviated as JavaScript Object Notation which is easy to understand and self-describing.\n\n# BSON\n\n# YAML\n\n# TOML\n"
  },
  {
    "path": "tech/Firebase.md",
    "content": "# Firebase Deployment\n\n## Get Started\n* Install Firebase tools:\n```yarn\nnpm i -g firebase-tools\n```\n* Init hosting project:\n```yarn\nfirebase init hosting\n```\n* Select a default Firebase project for the directory.\n\n## Basic commands\n* Show all your firebase projects:\n```js\n  firebase list\n```\n* Serve locally\n```js\n  firebase serve --only functions, hosting\n```\n* Deploy\n```js\n  firebase deploy\n```\n* Deploy by using token\n```yml\nfirebase deploy --token \"$FIREBASE_TOKEN\"\n```\n\n## Deploy React App (frontend)\n\n* Create a new project with the [Firebase console](https://console.firebase.google.com).\n* Choose a project name.\n* Click \"Add Firebase into web app\"\n* Choose an app name.\n* Select the \"Set up  Firebase Hosting\" option and go next.\n* Open web app settings -> Configuration and copy object like this:\n```js\nconst firebaseConfig = {\n  apiKey: \"...\",\n  authDomain: \"...\",\n  databaseURL: \"...\",\n  projectId: \"...\",\n  storageBucket: \"...\",\n  messagingSenderId: \"...\",\n  appId: \"...\"\n};\n```\n* Now open index.html file and post this two scripts at the end of the body tag:\n```HTML\n<body>\n  <!-- /* ... */ -->\n  <!-- The core Firebase JS SDK is always required and must be listed first -->\n  <script src=\"https://www.gstatic.com/firebasejs/6.0.4/firebase-app.js\"></script>\n  <script>\n    // Your web app's Firebase configuration\n    const firebaseConfig = { /* your config here */ };\n    // Initialize Firebase\n    firebase.initializeApp(firebaseConfig);\n  </script>\n </body>\n```\n* Create firebase.json in the root project folder and set up Firebase hosting:  \n  * public - path to the build folder (the only one required attribure for hosting)\n  * ignore - files to ignore on deploy\n  * rewrites - serve index.html for requests to files or directories that do not exist\n```json\n{\n  \"hosting\": {\n    \"public\": \"dist\",\n    \"ignore\": [\n      \"firebase.json\",\n      \"**/.*\",\n      \"**/node_modules/**\"\n    ],\n    \"rewrites\": [\n      {\n        \"source\": \"**\",\n        \"destination\": \"/index.html\"\n      }\n    ]\n  }\n}\n```\n* (optional) Create .firebaserc in the root project folder and set up default:\n```json\n{\n  \"projects\": {\n    \"default\": \"your_project_name\"\n  }\n}\n```\n* Create a build:\n```NPM\nnpm run build\n```\n* Deploy your app:\n```npm\nfirebase deploy\n```\n\n## Deploy Node.js App (backend)\n\n## Bitbucket Integration\n* Get deployment token:\n```yml\nfirebase login:ci\n```\n* Visit shown URL to confirm your request.\n* Copy generated token.\n* Create *bitbucket-pipelines.yml* file in the root directory:\n```yml\npipelines:\n  default:\n    - step:\n        name: Client: install modules and run build\n        image: node:10.15.3\n        deployment: test\n        script:\n          - cd ./client\n          - npm install\n          - npm run build\n        artifacts: # cache for next steps\n          - client/dist/**\n    - step:\n        name: Client: Deploy to firebase\n        script:\n          - pipe: atlassian/firebase-deploy:0.2.4\n            variables:\n              FIREBASE_TOKEN: \"FIREBASE_TOKEN\" # paste generated token\n              PROJECT_ID: \"PROJECT_ID\" # you can find projectId in firebase config\n              DEBUG: 'true'\n```\n\n\n# Set up `node-gyp` for Windows (to make `grpc` work)\n\nFirebase may require GRPC module and GRPC requires [node-gyp](https://github.com/nodejs/node-gyp).\nThere is a guide how I managed to install it after reading tons of stackoverflows resolving issues one after another.\n\nDownload and open `Visual Studio Installer` (https://visualstudio.microsoft.com/downloads/).\nIntall `Visual Studio Build Tools 2022`. Select there:\n* `Desktop development with C++`\n\n![image](https://github.com/Max-Starling/Notes/assets/22237384/edb4764e-7e59-412d-bce5-477962bbed1c)\n\n* Some `SDK`\n\n![image](https://github.com/Max-Starling/Notes/assets/22237384/f2e17f8b-72e3-48bf-a9de-456db9d52b01)\n\n* `VC++`\n\n![image](https://github.com/Max-Starling/Notes/assets/22237384/f1dc472f-6870-4300-91de-0d8eae83f0a8)\n\nThen install, restart PC.\n\n```\nnpm config edit\n\nmsbuild_path=C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\MSBuild\\Current\\Bin\\MSBuild.exe\nmsbuild-path=C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\MSBuild\\Current\\Bin\\MSBuild.exe\nmsvs_version=2022\nmsvs-version=2022\npython=C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\n```\n\n```\nSETX VCINSTALLDIR 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\VC'\nSETX VSINSTALLDIR 'C:\\\\Program Files (x86)\\\\Microsoft Visual Studio\\\\2022\\\\BuildTools'\nrefreshenv\n```\n![image](https://github.com/Max-Starling/Notes/assets/22237384/5dbe6296-09c7-4cf8-aa03-d514e247201a)\n![image](https://github.com/Max-Starling/Notes/assets/22237384/7a892ee6-b4cc-46ea-bd32-bb34dce4c238)\n\n```\nnpm i -g node-gyp\n```\n\nCreate `binding.gyp` text tile\n```json\n{\n  \"targets\": [\n    {\n      \"target_name\": \"binding\",\n      \"sources\": [ \"./binding.cc\" ]\n    }\n  ]\n}\n```\nCreate empty `binding.cc` text file.\n\n```\nnode-gyp configure\nnode-gyp build\nnode-gyp rebuild\n```\nnow you can do `npm install` without `node-gyp` errors\n\n"
  },
  {
    "path": "tech/Heroku.md",
    "content": "# Heroku Deployment\n\n## Get Started\n* Install Heroku CLI:\n```yarn\nnpm i -g heroku\n```\n* Login with:\n```yarn\nheroku login\n```\n* Create a new empty heroku app:\n```yarn\nheroku create\n```\n* Copy *app_id* and set up git remote with it:\n```yarn\nheroku git:remote -a <app_id>\n```\n* Make changes and commit them:\n```git\n  git add -A\n  git commit -m \"<commit_message>\"\n```\n* Now you can deploy the app via:\n```git\ngit push heroku master\n```\n\n## Basic commands\n* Command list:\n```js\n  heroku help\n```\n* Open app in a browser:\n```js\n  heroku open\n```\n"
  },
  {
    "path": "tech/Jest.md",
    "content": "# Concurrency params\n\n`--maxConcurrency=<num>`\nPrevents Jest from executing more than the specified amount of tests at the same time. Only affects tests that use test.concurrent.\n\n`--maxWorkers=<num>|<string>`\nAlias: -w. Specifies the maximum number of workers the worker-pool will spawn for running tests. In single run mode, this defaults to the number of the cores available on your machine minus one for the main thread. In watch mode, this defaults to half of the available cores on your machine to ensure Jest is unobtrusive and does not grind your machine to a halt. It may be useful to adjust this in resource limited environments like CIs but the defaults should be adequate for most use-cases.\n\nFor environments with variable CPUs available, you can use percentage based configuration: --maxWorkers=50%\n\n`--runInBand`\nAlias: -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging.\n\n<!-- https://freecontent.manning.com/the-value-of-concurrency-in-tests/ -->\n"
  },
  {
    "path": "tech/Klaviyo.md",
    "content": "# Klavio\n- [Переменные и их методы](#переменные-и-их-методы)\n- [Циклы](#циклы)\n- [Условные операторы](#условные-операторы)\n- [Массивы и их методы](#массивы-и-их-методы)\n- [Форматирование](#форматирование)\n\n\nKlavio использует Django (Python) в качестве *шаблонизатора* (англ. `template engine`).\n\n## Переменные и их методы\n- [Обращение к переменной](#обращение-к-переменной)\n- [Использование встроенных методов](#использование-встроенных-методов)\n- [Переменные со знаком `$` в названии](#переменные-со-знаком--в-названии)\n- [Передача переменной значения по умолчанию](#передача-переменной-значения-по-умолчанию)\n\n### Обращение к переменной\n\nОбращение к переменной `foo`:\n```hbs\n{{ foo }}\n```\nЕсли использовать переменную в тексте:\n```hbs\n<span> Hello, {{ name }}! </span>\n```\n\n### Использование встроенных методов\nНапример, для применения метода с названием `method` с переменной `foo` используется следующий синтаксис:\n```hbs\n{{ foo|method }}\n```\nЕсли метод может принимать параметры, то вызов метода `method` с параметром `param` имеет вид:\n```hbs\n{{ foo|method:param }}\n```\nЕсли нужно применить к переменной `bar` последовательно два метода `method1` и `method2`, то\n```hbs\n{{ foo|method1:param1|method2:param2 }}\n```\n\n### Переменные со знаком `$` в названии\nЕсли переменная из контекста имеет знак `$` в своём названии (например, `$value`), то к ней нельзя обратиться как к `{{ $value }}`, поскольку это приведёт к синтаксической ошибке.\nТакие переменные нужно находить с помощью метода `lookup`, передавая ему в качестве параметра название переменной строкой:\n```hbs\n{{ event|lookup:'$value' }}\n```\n\n### Передача переменной значения по умолчанию\n```hbs\n<span> Hello, {{ name|default:'Sir' }}! </span>\n```\n\n## Циклы\nВ Django существует только один вид циклов: `for..in`. Циклы `foreach`, `while`, `for`, `do..while` отсутствуют.\n### Цикл `for`\n```django\n<ul>\n{% for unit in unit_list %}\n    <li>{{ unit.name }} {{ unit.price }}</li>\n{% endfor %}\n</ul>\n```\n\n## Условные операторы\nВ Django существует только один вид условных операторов: `if..else`. Тернарный оператор `a ? b : c` отсуствует.\n\n### Оператор if..else\n```klaviyo\n{% if unit_list and unit_list|length > 1 %}\n    Number of units: {{ unit_list|length }}\n{% else %}\n    There are no units.\n{% endif %}\n```\n<!-- elif unit_list|length == 1\n    There is only one unit left. -->\n\n\n## Массивы и их методы\n* `length` возвращает длину массива с названием `array` (также подходит для строк)\n```hbs\n{{ array|length }}\n```\n* `length_is` возвращает логическое значение `true/false`, если длина массива `array` совпадает с указанной (например, `3`)\n```hbs\n{{ array|length_is:\"3\" }}\n```\n\n## Форматирование\n### Форматирование числа с плавающей точкой\n* Приведение *числа с плавающей точкой* `float` к *точности* (англ. `precision`) до *двух знаков после запятой*  \n(`7.3193 --> 7.32`, `6 --> 6.00`, `1.1 --> 1.10`, `3.0000000001 --> 3.00`):\n```hbs\n{{ event.float|floatformat:2 }}\n```\n\n### Форматирование даты\n* Перевод даты `date` из формата `ISO` (например, `2022-02-16T21:57:21.323Z`) в *американский формат* `MM/DD/YYYY`:\n```hbs\n{{ event.date|format_date_string|date:'m/d/Y' }}\n```\n\n"
  },
  {
    "path": "tech/Kubernetes.md",
    "content": "\n# Компоненты\n\n## Кластер `Cluster`, узлы `Nodes`, модули `Pods`, `Workload`\nДля начала работы с Kubernetes необходимо создать *Кластер*.\n\n**Кластер** (англ. `Cluster`) представляет собой *множество* *Узлов*.\n\nКаждый **Узел** *представляет собой рабочую машину* (англ. `worker machine`), являющуюся *частью Кластера* и *запускающую контейнерные приложения* (англ. `containerized applications`). *Узлы* в *Кластере* *не являются частью публичного интернета*, то есть они *не общедоступны*.\n\nНа рабочих *Узлах размещаются* **Модули** (англ. `Pods`), являющиеся *компонентами приложения*, компонентами Workload.  \n**Workload** (рус. `рабочая нагрузка`) - это приложение, запущенное в Kubernetes.\n\n## Контрольный план\n\n**Контрольный план** (англ. `Control plane`)\n> The container orchestration layer that exposes the API and interfaces to define, deploy, and manage the lifecycle of containers.\n\n\n\n\n## Ingress\n"
  },
  {
    "path": "tech/Microservices.md",
    "content": "\n\n# Принципы построения микросервисов\n\nПри разделении приложения на микросервисы важно следовать определённым принципам, чтобы система оставалась поддерживаемой, масштабируемой и эффективной. Вот ключевые принципы, которые следует учитывать:\n\nСледуя этим принципам, вы можете разбить своё приложение на микросервисы таким образом, чтобы максимально использовать преимущества микросервисной архитектуры, минимизируя при этом потенциальные недостатки, такие как повышенная сложность и операционные накладные расходы.\n\n## Принцип единственной ответственности\n\nКаждый микросервис должен фокусироваться на конкретной бизнес-возможности или функции.\nЭто повышает поддерживаемость и делает сервисы легче для понимания и управления.\n\n## Ограниченные контексты (Domain-Driven Design)\n\nИспользуйте предметно-ориентированное проектирование для идентификации отдельных областей (ограниченных контекстов) внутри вашей доменной модели.\nСоотнесите микросервисы с этими контекстами, чтобы обеспечить инкапсуляцию связанных данных и поведения.\n\n## Слабая связанность и высокая когезия\n\n**Слабая связанность** (англ. `low coupling`) - cервисы должны иметь минимальные зависимости друг от друга, чтобы снизить влияние изменений.\n\n**Высокая когезия** (англ. `high cohesion`) - группируйте связанные функции внутри одного сервиса, чтобы сохранить их целостность.\n\n\n## Ориентация на бизнес-возможности (англ. `Business Capability Orientation`)\n\nОрганизуйте микросервисы вокруг бизнес-возможностей, а не технических слоёв (например, пользовательский интерфейс, бизнес-логика, доступ к данным).\nЭто обеспечивает предоставление сервисами полной функциональности от начала до конца.\n\n## Независимая развертываемость\nПроектируйте сервисы так, чтобы их можно было разрабатывать, тестировать, развертывать и масштабировать независимо.\n\nЭто снижает риски развертывания и позволяет быстрее проводить итерации.\n\n## Владение данными и их хранение:\n\nКаждый микросервис должен владеть своей базой данных и управлять своими данными, чтобы предотвратить тесную связанность через общие базы данных.\nЭто способствует инкапсуляции данных и автономности сервисов.\n\n## Явные контракты сервисов (API):\n\nОпределите чёткие и стабильные интерфейсы для взаимодействия между сервисами.\nИспользуйте хорошо определённые API для управления коммуникацией и обеспечения обратной совместимости при обновлении сервисов.\n\n## Асинхронная коммуникация:\n\nПредпочитайте асинхронное обмен сообщениями для межсервисного взаимодействия, чтобы улучшить масштабируемость и устойчивость.\nЭто помогает разъединить сервисы и более эффективно обрабатывать изменяющиеся нагрузки.\n\n## Избегайте преждевременной оптимизации (правильный размер сервисов):\n\nИзбегайте слишком детального разделения сервисов с самого начала; начните с более крупных сервисов и при необходимости рефакторьте.\nЧрезмерно гранулированные сервисы могут привести к повышенной сложности и накладным расходам.\n\n## Организационное соответствие (закон Конуэя):\n\nСоотнесите границы микросервисов со структурой команд, чтобы усилить владение и ответственность.\nКросс-функциональные команды могут управлять сервисами от разработки до развертывания.\n\n## Устойчивость и изоляция отказов:\n\nПроектируйте сервисы так, чтобы они могли справляться с отказами без влияния на всю систему.\nРеализуйте прерыватели цепи, повторные попытки и тайм-ауты для управления сбоями в межсервисной коммуникации.\n\n## Независимость технологий и платформ:\n\nПозвольте сервисам использовать наиболее подходящие технологии для их специфических потребностей.\nЭта гибкость может оптимизировать производительность и использовать специализированные инструменты.\n\n## Соображения масштабируемости:\n\nИдентифицируйте сервисы, которые требуют независимого масштабирования на основе нагрузки.\nРазделите сервисы для оптимизации использования ресурсов и производительности.\n\n## Мониторинг и логирование:\n\nРеализуйте централизованный мониторинг и логирование для отслеживания состояния и производительности сервисов.\nОбеспечьте видимость межсервисной коммуникации для устранения неполадок и анализа.\n\n## Границы безопасности:\n\nРассматривайте каждый сервис как границу безопасности.\nОбеспечьте аутентификацию и авторизацию на уровне сервиса для повышения безопасности.\nСоответствие нормативным требованиям и чувствительность данных:\n\nУчитывайте юридические и нормативные требования при определении границ сервисов.\nИзолируйте сервисы, обрабатывающие конфиденциальные данные, чтобы упростить управление соответствием.\n\n## Эволюционный дизайн:\n\nБудьте готовы, что границы сервисов могут изменяться со временем, а это значит, что придётся рефакторить и развивать архитектуру по мере углубления понимания домена.\n"
  },
  {
    "path": "tech/MongoDB.md",
    "content": "- [О MongoDB](#о-mongodb)\n- [Основные понятия](#основные-понятия)\n  - [Документ](#документ)\n  - [Коллекция](#коллекция)\n- [Операции над данными](#операции-над-данными)\n  - [Агрегации](#агрегации)\n  - [Работа с типами данных](#работа-с-типами-данных)\n- [Обновление данных в массиве](#обновление-данных-в-массиве)\n  - [Оператор $push](#оператор-push)\n  - [Оператор $pull](#оператор-pull)\n- [Выборка элементов](#выборка-элементов)\n  - [Поиск объекта в массиве объектов (`$elemMatch`)](#поиск-объекта-в-массиве-объектов-elemmatch)\n\n# О MongoDB\n\n**MongoDB** - это *документно-ориентированная* база данных (англ. document-oriented database), что означает, что *каждый объект* (реальный или абстрактный) *представляется* в виде *отдельного документа*, *хранящегося* в *базе данных* в *формате JSON*.\n\n*MongoDB* относят к *типу NoSQL баз данных* или *нереляционных баз данных*, поскольку *MongoDB* не *использует синтаксис языка SQL* и *реляционную модель данных*. \n\n# Основные понятия\n- [Документ](#документ)\n- [Коллекция](#коллекция)\n\n\n## Документ\n\nЛюбой объект в MongoDB представляются в виде **документа** (англ. document). \n\n*Каждый документ представляет* собой *множество* пар **\"ключ-значение\"** (англ. key-value). \n\nКлючи также называют **полями** (англ. fields). \n\nКаждый документ обязательно хранит поле `_id`, по которому можно уникально идентифицировать любой документ. Это поле имеет тип `ObjectId` и генерируется автоматически при добавлении документа, если его не задать.\n\n*Пример документа*, *хранящего данные* некоторого *сообщения*, отправленного в некоторой *системе* для *обмена сообщениями*:\n```js\n{\n  \"_id\": ObjectId(\"60b3a171e3168e00b9f28e33\"),\n  \"text\": \"Go for it.\",\n  \"sender\": ObjectId(\"5f694dcf36a0f350f8ea75f3\"),\n  \"recipients\": [\n    ObjectId(\"5f694dce36a0f350f8ea75cf\"\n  ]\n}\n```\n\nАналагом документа в SQL является **строка таблицы** (англ. row), а одному *полю* соответствует **столбец таблицы** (англ. column).\n\n## Коллекция\nНесколько объектов, обладающих схожими свойствами, объединяют в **коллекцию** (англ. collection).\n\nПример коллекции `users`:\n```js\n[{\n  \"_id\": ObjectId(\"3ae0017l0ff1234567b0be7e\"),\n  \"username\": \"Max-Starling\",\n  \"following\": [\n    ObjectId(\"5f694d9936a0f350f8ea6e83\")\n  ],\n  \"followers\": [\n    ObjectId(\"5f694d9936a0f350f8ea6e83\"),\n    ObjectId(\"4a694d7196e3f350f4ea3e16\")\n  ],\n  \"settings\": {\n    \"mode\": \"dark\"\n  },\n  \"createdAt\": \"2017-07-17T01:07:01.787+00:00\",\n},\n{\n  \"_id\": ObjectId(\"4a694d7196e3f350f4ea3e16\"),\n  \"username\": \"Manfredi\",\n  \"following\": [\n    ObjectId(\"3ae0017l0ff1234567b0be7e\")\n  ],\n  \"followers\": [],\n  \"settings\": {\n    \"mode\": \"light\",\n    \"fontSize\": 12,\n  },\n  \"createdAt\": \"2018-08-18T00:04:01.123+00:00\",\n}]\n```\n\n# Операции над данными\n- [Агрегации](#агрегации)\n- [Доступные операции в методе aggregate](#доступные-операции-в-методе-aggregate)\n- [Работа с типами данных](#работа-с-типами-данных)\n\n\n## Агрегации\n- [Определения и разновидности агрегаций](#определения-и-разновидности-агрегаций)\n- [Доступные операции в методе aggregate](#доступные-операции-в-методе-aggregate)\n\n### Определения и разновидности агрегаций\n\n**Агрегация** (от лат. `aggregatio` - *присоединение*, англ. `agregation`) в *общем случае* означает *процесс объединения* некоторых *объектов* в одну *группу* ( (англ. `a cluster of things`, `a collection of items`).\n\n<!-- В рамках MongoDB агрегация представляет собой составную поисковую операцию из нескольких последовательных операций.  -->\n\n<!-- Одной из таких операций является операция группировки, которая уменьшает количество -->\n\nВ терминалогии баз данных **агрегациями** или **агрегационными функциями** (англ. `aggregate function`) называют *операции*, которые *преобразуют* некоторую *заданную группу значений* (a set of values) в *одно значение* (single value).\n\nНапример, такими агрегационными функциями являются: \n* *Сумма* (`sum`).\n* *Подсчёт количества элементов* (`count`).\n* *Вычисление среднего значения* (`avg`).\n* *Нахождение минимума* (`min`).\n* *Нахождение максимума* (`max`).\n\n\nК *агрегациям* также *относят операцию группировки* (`group`).\n\nВ *JavaScript* нечто *подобное* можно *сделать* с *массивом* при помощи *метода* `Array.reduce()`:\n```js\nconst values = [1, 2, 3];\n\nconst sum = values.reduce((sum, value) => sum + value, 0);\nconsole.log(sum); // 6\n\nconst count = values.reduce((count, value) => count + 1, 0);\nconsole.log(count); // 3\n\nconst avg = values.reduce((sum, value) => sum + (value / count), 0);\nconsole.log(avg); // 2\n\nconst min = values.reduce((sum, value) => Math.min(sum, value));\nconsole.log(min); // 1\n\nconst max = values.reduce((sum, value) => Math.max(sum, value));\nconsole.log(max); // 3\n```\n\nТак, например, при помощи агрегации можно выбрать документы из коллекции, сгруппировать их по некоторому критерию и оставить только необходимые поля.\n\nВ MongoDB агрегирование чаще всего осуществляется за счёт метода `collection.aggregate(pipeline)`, где `pipeline` - массив, каждый объект которого описывает некоторую операцию над данными.\n\n<!-- Например, нам нужно найти определённые документы в коллекции, сгруппировать их по определённому принципу. -->\n\n### Доступные операции в методе aggregate\n#### Операция $match\n#### Операция $group\n#### Операция $project\n\n### Пример агрегации с группировкой и подсчётом\n\nВ примере ниже мы ищем сотрудников компании с зарплатой больше 1000, группируем их по их роду деятельности и считаем количество сотрудников в каждой группе.\n```js\nconst pipeline = [\n  { $match: { salary: { $gte: 1000 } },\n  {\n    $group: {\n      _id: '$profession',\n      number: { $sum: 1 },\n    },\n  },\n  {\n    $project: {\n      _id: 0,\n      profession: '$_id',\n      number: 1,\n    },\n  },\n]);\n];\n\n/* workers: [\n  { _id: 1, salary: 500, profession: 'cleaner' },\n  { _id: 2, salary: 1200, profession: 'developer' },\n  { _id: 3, salary: 1500, profession: 'developer' },\n  { _id: 3, salary: 1000, profession: 'tester' },\n  { _id: 4, salary: 900, profession: 'tester' },\n] */\n\nconst workersCollection = db.collection('workers');\n\nconst bigSalaryInfo = await workersCollection.aggregate(pipeline);\n\n/* bigSalaryInfo: [\n  { profession: 'developer', number: 2 },\n  { profession: 'tester', number: 1 },\n] */\n```\n\n\n\n## Работа с типами данных\n- [Получение документов по заданному типу данных](#получение-документов-по-заданному-типу-данных)\n- [Приведение типа некоторого поля документов коллекции](#приведение-типа-некоторого-поля-у-документов-коллекции)\n\nДопустимые типы данных:\n```js\nconst MONGO_TYPES = {\n  bool: 'bool',\n  date: 'date',\n  decimal: 'decimal',\n  double: 'double',\n  int: 'int',\n  long: 'long',\n  objectId: 'objectId',\n  string: 'string'\n}\n```\n\n### Получение документов по заданному типу данных\n \n```js\nconst animalsCollection = db.collection('animals');\nconst document animalsCollection.find({\n  age: { $type: 'int' },\n})\n```\n\n### Приведение типа некоторого поля у документов коллекции\n\nДопустимые методы приведения типов:\n```js\nconst MONGO_CAST_METHODS= {\n  bool: '$toBool',\n  date: '$toDate',\n  decimal: '$toDecimal',\n  double: '$toDouble',\n  int: '$toInt',\n  long: '$toLong',\n  objectId: '$toObjectId',\n  string: '$toString',\n};\n```\nКод ниже *обновляет только* те *документы коллекции* `bills`, *значение поля* `amount` которых имеет *строковый тип*, при этом *производится приведение* этого *значения* к *числовому типу* `double`.\n```js\nconst billsCollection = db.collection('bills');\n\n/* bills: [\n  { _id: ObjectId(...), \"amount': \"123.50\" },\n  { _id: ObjectId(...), \"amount': 17 }\n] */\n\nawait billsCollection.update(\n  {\n    \"amount\": { $type: 'string' },\n  },\n  [{\n    $set: {\n      \"amount\": { $toDouble: \"$amount\" },\n    },\n  }],\n  { multi: true },\n);\n\n/* bills: [\n  { _id: ObjectId(...), \"amount': 123.5 },\n  { _id: ObjectId(...), \"amount': 17 }\n] */\n```\n\nМетод ниже *находит все документы колекции* `collection`, *значение поля* `fieldName` которых *имеет строковый тип*, и *приводит* их к *типу* `date`.\n```js\nconst castStringFieldToDate = (collection, fieldName) => {\n  return collection.update(\n    {\n      `${fieldName}`: { $type: 'string' },\n    },\n    [\n      {\n        $set: {\n          `${fieldName}`: {\n            $toDate: `$${fieldName}`,\n          },\n        },\n      },\n    ],\n    { multi: true },\n  );\n}\n\n/* ... */\n\nconst usersCollection = db.collection('users');\n/* users: [{ _id: ObjectId(...), \"joinDate': \"2020-09-22T01:05:18.486Z\" }] */\nawait castStringFieldToDouble(usersCollection, 'joinDate')\n/* users: [{ _id: ObjectId(...), \"joinDate': { $date: \"2020-09-22T01:05:18.486Z\" }}] */\n```\n\n# Обновление данных в массиве\n\n## Оператор $push\n\n*Оператор* `$push` *добавляет один элемент* `<newArrayItem>` в *массив* `<arrayFieldName>`.\n```js\n{ $push: { <arrayFieldName>: <newArrayItem> } }\n```\n\n```js\ndb.favoritePages.insertOne({\n  _id: 17,\n  pages: [3, 21, 73]\n})\n// favoritePages: [{ _id: 17, pages: [3, 21, 73] }]\ndb.favoritePages.updateOne(\n   { _id: 17 },\n   { $push: { pages: 101 } }\n)\n// favoritePages: [{ _id: 17, pages: [3, 21, 73, 101] }]\n```\nДля *добавления нескольких элементов* `<item1>`, `<item2>` в *массив* `<arrayFieldName>`\n\n```js\n{ $push: { <arrayFieldName>: { $each: [<item1>, <item2>] } } }\n```\n\n```js\ndb.favoritePages.updateOne(\n   { _id: 17 },\n   { $push: { pages: { $each: [277, 314] } } }\n)\n// favoritePages: [{ _id: 17, pages: [3, 21, 73, 101, 277, 314] }]\n```\n\n## Оператор $pull\n\n# Выборка элементов\n\n## Поиск объекта в массиве объектов (`$elemMatch`)\n\nДопустим, у нас в схеме коллекции `workspace` содержится массив пользователей.\n```js\n/* Workspace Schema */\n{\n  uuid: String;\n  name: String;\n  users: [{\n    uuid: String;\n    name: String;\n  }]\n}\n```\nЕсли мы хотим найти воркспейс, содержащий определённого пользователя, следует использовать оператор `$elemMatch`.\n\nОператор `$elemMatch` находит документы, которые содержат в поле-массиве хотя бы один элемент, который удовлетворяет всем указанным параметрам вложенного query-запроса, передаваемого в качестве значения для `$elemMatch`.\n\nДопустим, в коллекции имеется следующий документ.\n```js\n{\n  id: 'w17',\n  name: 'lab',\n  users: [{\n    id: 'u19',\n    name: 'Kat',\n  }, {\n    id: 'u22',\n    name: 'Tom',\n  }]\n}\n```\nОба query-запроса ниже включат документ выше в выборку:\n```js\n{ users: { $elemMatch: { id: 'u19' } } }\n{ users: { $elemMatch: { name: 'Tom' } } }\n```\n"
  },
  {
    "path": "tech/Parcing-Preprocessing-StaticChecking.md",
    "content": "\n## Препроцессинг\n\n**Препроцессинг** (англ. `Preprocessing`) подразумевает приведение кода одного формата к другому формату.\nНапример, удалить все лишние проблемы, или все комментарии. Это может быть сделано при помощи резулярных выражений, строить синтаксическое дерево в данном случае не требуется.\n\n## Парсинг\n\n**Парсинг** (англ. `Parsing`) подразумевает лесический анализ кода и построение абстрактного синтаксического дерева.\nВ этом случае код (представляющий из себя набор/поток символов) разбивается на токены отделимые блоки.\n\nНапример, код `while (count > 0)` может быть разбит на токены:\n* `keyword(while)`\n* `leftParenthesis`\n* `variable(count)`\n* `operator(>)`\n* `number(0)`\n* `rightPerenthesis`\n\n### Статический парсинг\n\n### Динамический парсинг \n\n## Статическая проверка кода\n\nСуществует два этапа: компиляция (интерпретация) кода и его непосредственное выполнение.\n\nСтатическая проверка кода подразумевает статический анализ кода, то есть проверку кода на этапа компиляции. В этом случае код реально не выполняется.\n\nТаким образом, строится синтаксическое дерево и проверку кода на ошибки (компиляции) производится на основании результата поостроения.\n"
  },
  {
    "path": "tech/PostCSS.md",
    "content": "# PostCSS Setup\n## NPM\n```\nnpm install postcss-cli stylelint stylelint-config-standard postcss-cssnext precss postcss-cssnext cssnano --save-dev\nnpm install stylelint -g\n```\n## package.json\n```json\n{\n  \"scripts\": {\n    \"styles\": \"postcss input.postcss --config postcss.config.js --output output.min.css --watch\"\n  },\n}\n```\n## postcss.config.js\n```js\nmodule.exports = () => ({\n  plugins: {\n    'stylelint': {},\n    'precss': {},\n    'postcss-cssnext': {},\n    'cssnano': {\n      'autoprefixer': false\n    }\n  }\n});\n```\n## .stylelintrc\n```json\n{\n  \"extends\": \"stylelint-config-standard\",\n  \"rules\": {\n    \"selector-max-class\": 2\n  }\n}\n```\n## VSCode Settings (settings.json)\n```json\n{\n  \"emmet.includeLanguages\":{\n    \"postcss\": \"css\"\n  },\n  \"emmet.syntaxProfiles\": {\n    \"postcss\": \"css\"\n  },\n  \"css.validate\": false,\n  \"less.validate\": false,\n  \"scss.validate\": false,\n  \"postcss.validate\": false,\n  \"files.associations\": {\n    \"*.postcss\": \"postcss\",\n  },\n  \"[postcss]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  }\n}\n```\n\n## VSCode extensions\n* postcss-sugarss-language\n* Prettier - Code formatter (format document with Alt + Shift + F)\n* stylelint\n\n## PostCSS Setup (Webpack)\n```js\n{\n  test: /\\.css$/,\n  use: [\n    'style-loader',\n    'css-loader',\n     {\n       loader: 'postcss-loader',\n       options: {\n         ident: 'postcss',\n         plugins: [\n           require('stylelint')(),\n           /* ... */,\n         ],\n       },\n     },\n  ],\n}\n{\n  loader: 'postcss-loader',\n  options: {\n    ident: 'postcss',\n    plugins: [\n      require('autoprefixer')(),\n      require('stylelint')(),\n      /* ... */\n    ]\n  }\n}\n```\n\n## CSS Modules Setup (Webpack)\n```js\n{\n  test: /\\.css$/,\n  use: [\n    'style-loader',\n    {\n      loader: 'css-loader',\n      options: {\n        modules: true,\n        importLoaders: 1,\n        localIdentName: '[name]__[local]___[hash:base64:4]',\n      },\n     },\n     {\n       loader: 'postcss-loader',\n       options: { /* ... */ },\n     },\n  ],\n}\n```\n"
  },
  {
    "path": "tech/Postman.md",
    "content": "## Понятия в Postman\n\nЕсть рабочее пространство (англ. `Workspace`), по умолчанию используется `My workspace`.\nВ рабочем пространстве можно создавать коллекции (англ. `Collections`) логически сгруппированных запросов.\n\nКаждый запрос (`Request`) представляет собой сохранённую информацию об HTTP-запросе:\n* классические метод, URL, параметры, заголовки, тело запроса\n* но также тесты, Pre-request script и настройки запроса.\n\nСохраняя информацию о запросе в коллекцию, вы можете в любой момент переиспользовать её в качестве темплейта для новых запросов или же переодически вызывать один и тот же запрос в один клик в любое время.\n\nУ колекции также есть свои настройки, которые применяются ко всем запросам в ней:\n* переменные окружения\n* pre-request-script\n* тесты и так далее\n![image](https://github.com/Max-Starling/Notes/assets/22237384/9b118c4e-8cfa-47ae-b23d-7964764fe698).\n\nНиже представлен небольшой схематичный пример, как можно было бы использовать свое личное рабочее пространство:\n* My workspace\n  * Users dashboard Prod\n    * Get users - GET https://users-dashboard.com/api/users\n    * Get user by id - GET http://users-dashboard.com/api/users/:id\n  * Users dashboard Local\n    * Get users - GET http://localhost:3001/api/users\n    * Get user by id - GET http://localhost:3001/api/users/:id\n  * Calculator\n    * Calculate - POST http://localhost:3002/api/calculate\n\nно можно также для каждого проекта создавать новое рабочее пространство для наилучшей изолированности (вдруг вы собираетесь давать доступ кому-то к ворксейсам):\n* Users dashboard\n  * Production\n    * Get users - GET https://users-dashboard.com/api/users\n    * Get user by id - GET http://users-dashboard.com/api/users/:id\n  * Localhost\n    * Get users - GET http://localhost:3001/api/users\n    * Get user by id - GET http://localhost:3001/api/users/:id\n* Calculator workspace\n  * Localhost\n    * Calculate - POST http://localhost:3002/api/calculate\n\nЧасто бывает так, что в одной компании может быть несколько проектов, на каждый проект- отдельная коллекция, но все проекты в одном пространстве.\n\nЕсли есть версионирование, можно разбивать коллекции по версиям.\n\n* Users dashboard\n  * Api v1.0\n    * Get users - GET https://users-dashboard.com/api/v1.0/users\n  * Api v1.1\n    * Get users - GET https://users-dashboard.com/api/v1.1/users\n    * Get users by name - GET https://users-dashboard.com/api/v1.1/users/:name\n  * Api v2\n    * Get users - GET https://users-dashboard.com/api/v2/users\n    * Get users by ID - GET https://users-dashboard.com/api/v2/users/:id\n\n## Использование переменных в Postman\n\nЧтобы постоянно не клонировать коллекции с одними и теми же запросами и минимальными различиями в параметрах, можно задать переменные окружения. Сделать это можно в настройках коллекции:\n![image](https://github.com/Max-Starling/Notes/assets/22237384/80a01de8-5c23-4b8f-be23-03d41f521def)\n\nнапример, вместо того, чтобы создавать две коллекции `Users dashboard Local`, `Users dashboard Production`, досточно просто завести переменную `base_url`.\n```\nbase_url = http://localhost:3001\n```\nи использовать её в запросе:\n```\nGET {{base_url}}/api/v1.0/users\n```\n\n\n## Postman pre-request script\nСкрипт, который производит ре-аутентификацию с каждым запросом. Сохраняется в \n```js\nconst postRequest = {\n  url: `${pm.collectionVariables.get(\"base_url\")}${pm.collectionVariables.get(\"access_token_endpoint\")}`,\n  method: \"post\",\n  header: {\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    clientId: pm.collectionVariables.get(\"client_id\"),\n    secret: pm.collectionVariables.get(\"secret\")\n  }),\n};\n\npm.sendRequest(postRequest, (error, response) => {\n  if (error) {\n    console.log(error); // catch in Postamn debug console\n  } else {\n    pm.collectionVariables.set(\"access_token\", response.json().accessToken);\n  }\n});\n```\n"
  },
  {
    "path": "tech/Python.md",
    "content": "\n# Types\n\n## String\n\n### Concat string\nBinary `+` operator is used for *string concatenation*.\n```py\nhi = \"Hi\"\nthere = \"There\"\nresult_str = hi + \" \" + there\nprint(result_str)\n```\n### Convert to string\n\n```py\nn = 17\ns = str(num) \nprint(s) // \"17\"\n```\n\n## Список `list`\n\n## Кортеж `tuple`\n\n## Словарь `dictionary`\n\n## Конкатенация `contatenation` при помощи `join`\n\nКонкатенация при помощи`join`может быть применена к любому перечисляемому типу данных (англ. `iterable`), то есть к:\n* множествам `set`\n* спискам `list`\n* кортежам `tuple`\n* словарям `dict`\n* строкам `str`\n\nПримеры\n```python\n# set\ngreeting_set = {\"world\", \"Hello\"}\nprint(\" \".join(greeting_set)) # --> \"Hello world\" (в обратном порядке)\n\n# list\nvowels_list = ['a', 'e', 'i', 'o', 'u']\nprint(\", \".join(vowels_list)) # --> \"a, e, i, o, u\"\n\n# tuple\ncountry_tuple = (\"Ukraine\", \"Ukrainian\", 36.7) # (\"country\", \"language\", \"population (m)\")\nprint(\" - \".join(country_tuple)) # --> Ukraine - Ukrainian - 36.7\n\n# dict\nusers_dict = {\"Max\": 17, \"Frank\": 21, \"Tom\": 13}\nprint(\" and \".join(users_dict)) # --> Max and Frank and Tom\n\n## str\nnickname = 'starling'\nprint(\"_\".join(nickname)) # s_t_a_r_l_i_n_g\n```\n\n## JSON\n### Parse JSON to Dictionary\n```py\nimport json\n\nrepo_json =  '{ \"user\":\"Max-Starling\", repo: \"Notes\" }'\n\n# Parse repo_json\nrepo_dict = json.loads(repo_json)\n\n# Access a dictionary field\nprint(repo_dict[\"user\"])\n```\n### Convert to JSON\n```py\nimport json\n\nrepo_dict = {\n  \"user\":\"Max-Starling\"\n  repo: \"Notes\"\n}\n\n# Convert to JSON\nrepo_json = json.dumps(x)\n\n# Print JSON\nprint(repo_json)\n```\n### Intends in JSON\nYou can set number of indents in JSON\n```py\njson.dumps(x, indent=4)\n```\n### JSON Types mapping table\n\n| Python Type  | JSON   |\n| ------- | ------ |\n| dict    | Object |\n| list, typle\t  | Array  |\n| str\t    | String |\n| int, float |\tNumber |\n| True\t| true |\n| False\t| false |\n| None\t| null |\n\n# Типизация (`Typing`)\n\nPython не требует от нас явного указания типов, но их указание помогает избежать множества ошибок, а ещё делает ваш код более понятным для других разработчиков.\n\nЗадать тип можно\n* переменной после двоеточия `:`:\n```py\nBASE_URL: str = 'http://localhost:8080'\n```\n* аргументу функции после `:` и возвращаемому значению функции после `->`.\n```py\ndef getFullUrl(baseUrl: str, endpoint: str) -> str:\n    return f\"{baseUrl}{endpoint}\";\n```\n* параметру класса после `:` (причём в классах тип обязателен для каждого поля)\n```py\nclass User:\n    email: str\n    age: int\n\ntestUser: User = {\n    'email': 'test@email',\n    'age': 0\n}\n```\n### Примитивные типы данных\n* `str` - строковый тип\n* `int` - целочисленный тип\n* `float` - тип числа с правающей точкой\n* `bool` - логический тип\n* `complex` - тип комплексного числа, то есть числа с действительной и мнимой частью, содержащей мнимую единицу `j`\n\n## Составные типы данных\n\nСоставные типы данных (например, состоящие из нескольких значений, возможно, разных типов данных) испортируются из библиотеки `typing`:\n* `List[x]` - список, массив\n* `Set[x]` - множество\n* `Tuple` - кортеж\n* `Dict[x, y]` - словарь, объект, ассоциативный массив (\"ключ-значение\")\n* `Union` - объединение, перечисление допустимых типов.\n```py\nUnion[int, str] # тип `int` или `str`\nUnion[int] == int # объединение одного типа эквивалентно этому типу\nUnion[int, str, int] == Union[int, str] # повторяющиеся типы исключаются из объединения\nUnion[Union[int, str], float] == Union[int, str, float]\nUnion[int, str] == Union[str, int] # порядок задания типом в объединении не важен\n```\n* `Option` означает, что значение задавать не обязательно. Если значение не задать, ошибки не будет.\n```py\nOptional[T] # эквивалентно `Union[T, None]`\n```\n```py\nfrom typing import Optional\n\nclass DatabricksConnectionStringConfig:\n    hostname: str\n    token: str\n    port: Optional[int] = 443\n```\n\n### Чем отличаются List, Set, Dict\n\n#### Список\n\nList - упорядоченный набор элементов. Каждому элементу присваивается индекс (порядок), по которому вы можете найти нужный элемент.\n\nИспользуете его, если *порядок элементов важен*.\n\nНапример, список призёров турнира, номера камер хранения в магазине.\n\n#### Множество\n\nSet - неупорядоченный набор уникальных элементов. Используете его, если порядок не важен - важна уникальность элементов.\n\nНапример, список имён всех авторов комментариев под постом в некой социальной сети (один человек может отставить комментарий сколько угодно раз, но в список его имя попадёт лишь единожды).\n\n#### Словарь\n\nDict - словарь, ассоциативный массив \"ключ-значение\", хеш-таблица, карта. \n\nИспользуйте словарь, если вам нужно связать некоторые данные с каким-то ключом, по которому вы всегда эффективно сможете их найти.\n\nНапример, англо-русский словарь: ключ: \"hello\" - значение \"привет\".\n\nDo you just need an ordered sequence of items? Go for a list.\nDo you just need to know whether or not you've already got a particular value, but without ordering (and you don't need to store duplicates)? Use a set.\nDo you need to associate values with keys, so you can look them up efficiently (by key) later on? Use a dictionary.\n\n# Функции\n\n## Лямбда-функции\n\nTODO\n\nЛямбда-выражение в программировании — специальный синтаксис для определения функциональных объектов, заимствованный из λ-исчисления.\n\nЛямбда-выражения принимают две формы. Форма, которая наиболее прямо заменяет анонимный метод, представляет собой блок кода, заключенный в фигурные скобки. Это — прямая замена анонимных методов. Лямбда-выражения, с другой стороны, предоставляют ещё более сокращенный способ объявлять анонимный метод и не требуют ни кода в фигурных скобках, ни оператора return. Оба типа лямбда-выражений могут быть преобразованы в делегаты.\n\nВо всех лямбда-выражениях используется лямбда-оператор =>, который читается как «переходит в» (в языках Java, F# и PascalABC.NET используется оператор ->). Левая часть лямбда-оператора определяет параметры ввода (если таковые имеются), а правая часть содержит выражение или блок оператора. Лямбда-выражение x => x * 5 читается как «функция x, которая переходит в x, умноженное на 5»\n```py\nlambda <args>: <statement>\n```\n\nЛямбда-фунция возведения в степень.\n```py\npow = lambda x, power: x ** power\nprint(pow(2, 5))\n```\nчто эквивалентно\n```py\ndef pow(x, power):\n  return x ** power\nprint(pow(2, 5))\n```\n\n\n\n# `*args` and `**kwargs`\n\nОператор `*args` (`asterisk operator`) используется для *\"распаковки\" перечисляемых объектов* (англ. `iterable`), например, таких как *массив* и *список аргументов функции*.\n\nТо есть, `*[1, 2, 3]` это ничто иное, как `1, 2, 3`.\n\nОператор `**kwargs` (`double asterisk operator`) используется для *\"распаковки\" объектов*.\n\nТо есть, `**{ name: \"Max\", sex: \"male\" }` это ничто иное, как `name=\"Max\", sex=\"male\"`.\n\nНе путайте унарный оператор `**kwargs`, который применим только к объектам, с бинарным оператором возведения числа в степень `number ** power`.\n\nНе путайте унарный оператор `*args`, который применим только к перечисляемым объектам, с бинарным оператором умножения чисел `a * b`.\n\nОператоры `*args` и `**kwargs` являются аналогами оператора `...` (`spread operator`) в JavaScript.\n\n## Массивы и `*args`\n\nПри применении унарного оператора `*` к *массиву* вы передаёте в выражение все значения массива через запятую. Например, это может быть полезным при построении нового массива путём расширения предыдущего.\n\nНа примере ниже происходит слияние двух массивов.\n```py\narr = [3, 4]\nanother_arr = [1, 2]\nmerged = [*another_arr, *arr]\nprint(merged) # [1, 2, 3, 4]\n```\n```javascript\n// JavaScript\nconst foo = [3, 4];\nconst bar = [1, 2];\nconst merged = [...bar, ...foo];\nconsole.log(merged); // [1, 2, 3, 4]\n```\nЕщё немного слияний массивов для закрепления.\n```py\nfoo = [1, 2, 3]\nbar = [*foo, 4]\nprint(bar) # [1, 2, 3, 4]\n\nbaz = [0, *bar, 5]\nprint(baz) # [0, 1, 2, 3, 4, 5]\n```\n```javascript\n// JavaScript\nconst foo = [1, 2, 3];\nconst bar = [...foo, 4];\nconsole.log(bar); // [1, 2, 3, 4]\n\nconst baz = [0, ...bar, 5]\nconsole.log(baz); // [0, 1, 2, 3, 4, 5]\n```\n## Параметры функции и `*args`\nАналогично примерам выше, можно \"передать значения массива через запятую\" в качестве параметров функции. \n```python\ndef sum(a, b):\n    return a + b\nnumbers = [3, 7]\nprint(sum(*numbers)) # 10\n```\n```js\nfunction sum(a, b) {\n    return a + b;\n}\n\nconst numbers = [3, 7];\nconsole.log(sum(...numbers)); // 10\n// что эквивалентно\nconsole.log(sum(3, 7)); // 10\n```\n## Аргументы функции и `*args`\nПрименим `*args` к аргументами функции, вы получите функцию, которая может обрабатывать любое количество аргументов вплоть до бесконечности. Собранные аргументы помещаются в кортеж `tuple`.\n```python\ndef sum(*numbers):\n    result = 0\n    for num in numbers:\n        result = result + num\n    print(result)\n\nsum(3, 7) # 10\nsum(3, 7, 2) # 12\nsum(3, 7, 2, 5) # 17\n```\n```js\n// JavaScript\nfunction sum(...numbers) {\n    let result = 0;\n    for (const num of numbers) {\n      result += num;\n    }\n    console.log(result);\n}\n\nsum(3, 7); // 10\nsum(3, 7, 2); // 12\nsum(3, 7, 2, 5); // 17\n```\n## Объекты и **kwargs\n\nОператор `**kwargs` (keyword args) может быть использован для \"распаковки\" объектов. \n\nНапример, для слияния двух и более объектов. \n```py\nuser_personal_info = { \"first_name\": \"Max\", \"last_name\": \"Starling\" }\nuser_job_info = { \"job\": \"engineer\", \"experience\": \"6 years\" }\nuser_info = { **user_personal_info, **user_job_info }\nprint(user_info) # { \"first_name\": \"Max\", \"last_name\": \"Starling\", \"job\": \"engineer\", \"experience\": \"6 years\" }\n```\n```js\n// JavaScript\nconst userPersonalInfo = { firstName: \"Max\", lastName: \"Starling\" };\nconst userJobInfo = { job: \"engineer\", experience: \"6 years\" };\nconst userInfo = { ...userPersonalInfo, ...userJobInfo };\nconsole.log(userInfo); // { firstName: \"Max\", last_name: \"Starling\", job: \"engineer\", experience: \"6 years\" }\n```\nили для создания копии объекта с возможностью расширения его свойств\n```py\nfile_info = { \"file_name\": \"test.json\", \"size\": \"1376kb\" }\nprint({ **file_info, \"created_at\": \"15/12/2022\" }) # { \"file_name\": \"test.json\", \"size\": \"1376kb\", \"created_at\": \"15/12/2022\" }\n```\n```js\n// JavaScript\nconst fileInfo = { fileName: \"test.json\", size: \"1376kb\" }\nconsole.log({ ...fileInfo, createdAt: \"15/12/2022\" }) // { fileName: \"test.json\", size: \"1376kb\", createdAt: \"15/12/2022\" }\n```\n\n## Аргументы функции и `**kwargs`\nФункции в питоне могут принимать **ключевые аргументы** (англ. `keyword arguments`) в виде `kwarg=value`. В этом случае *порядок передачи параметров* функции *не важен*.\n```py\ndef print_user_info(status, name, age): # порядок не важен\n    print(\"\\nUser info:\")\n    print(\"- {} is {}\".format(\"name\",name))\n    print(\"- {} is {}\".format(\"age\",age))\n    print(\"- {} is {}\".format(\"status\",status))\n\nprint_user_info(name=\"Alex\", age=22, status=\"married\")\n# User info:\n# - name is Alex\n# - age is 22\n# - status is married\n```\nЧтобы функция могла принимать любое количество параметров и при этом было удобно их обрабатывать, можно использовать `**kwards`, который соберёт все параметры функции в словарь `dict` (англ. `dictionary`).\n```py\ndef print_user_info(**user_info):\n    # type(user_info) = dict\n    print(\"\\nUser info:\")\n    for key, value in user_info.items():\n        print(\"- {} is {}\".format(key,value))\n\nprint_user_info(name=\"Alex\", age=22, status=\"married\")\n# User info:\n# - name is Alex\n# - age is 22\n# - status is married\nprint_user_info(name=\"Kate\", age=18, sex=\"female\", status=\"single\")\n# User info:\n# - name is Kate\n# - age is 18\n# - sex is female\n# - status is single\n```\n```js\n// JavaScript\n// ключевые аргументы не доступны в языке - передаём объект с любым количеством свойств\nfunction printUserInfo(userInfo) {\n  console.log(\"\\nUser info:\");\n  for (const [key, value] of Object.entries(userInfo)) {\n      console.log(`- ${key} is ${value}`);\n  }\n}\nprintUserInfo({ name: \"Alex\", age: 22, status: \"married\" })\n// User info:\n// - name is Alex\n// - age is 22\n// - status is married\nprintUserInfo({ name: \"Kate\", age: 18, sex: \"female\", status: \"single\" })\n// User info:\n// - name is Kate\n// - age is 18\n// - sex is female\n// - status is single\n```\n## Все виды аргументов сразу в функции\n`error_type` - одиночный позиционный аргумент.\n`*messages` собирает позиционные элементы, начиная со второго и до последнего, в кортеж `tuple`.\n`**metadata` собирает все ключевые аргументы в словарь `dict`.\n```py\ndef print_errors(error_type, *messages, **metadata):\n    # type(user_info) = dict\n    print(f\"{error_type}:\")\n    for index, message in enumerate(messages):\n    \tprint(f\" * Error #{index}: - {message} *\")\n    for key, value in metadata.items():\n        print(\"** Metadata: - {} is {} **\".format(key,value))\n\nprint_errors(\n    \"ValidationError\",\n    \"password is too short\",\n    \"password must contain at least one special character\",\n    \"password must contain at least one capital letter\",\n    email=\"test@smail.com\",\n    browser=\"Safari\"\n)\n# ValidationError:\n#  * Error #0: - password is too short *\n#  * Error #1: - password must contain at least one special character *\n#  * Error #2: - password must contain at least one capital letter *\n# ** Metadata: - email is test@smail.com **\n# ** Metadata: - browser is Safari **\n```\n```js\n// JavaScript\n// ключевые аргументы в языке отсутствуют, оператор `...` должен быть в конце,\n// значит используем объект в качестве `metadata` и меняем его местами с `messages`\nfunction printErrors(errorType, metadata, ...messages) {\n    console.log(`${errorType}:`)\n    for (const [index, message] of Object.entries(messages)) {\n    \tconsole.log(` * Error #${index}: - ${message} *`)\n    }\n    for (const [key, value] of Object.entries(metadata)) {\n\tconsole.log(`** Metadata: - ${key} is ${value} **`)\n    }\n}\n\nprintErrors(\n    \"ValidationError\",\n    { email: \"test@smail.com\", browser: \"Safari\" },\n    \"password is too short\",\n    \"password must contain at least one special character\",\n    \"password must contain at least one capital letter\"\n)\n// ValidationError:\n//  * Error #0: - password is too short *\n//  * Error #1: - password must contain at least one special character *\n//  * Error #2: - password must contain at least one capital letter *\n// ** Metadata: - email is test@smail.com **\n// ** Metadata: - browser is Safari **\n```\n\n\n## Полезные функции\n\n### `is_json`\n\n```py\nimport json\n\ndef is_json(myjson):\n  try:\n    json.loads(myjson)\n  except ValueError as e:\n    return False\n  return True\n  \nprint is_json(\"{}\") # True\nprint is_json(\"{abc}\") # False\nprint is_json('{\"name\":\"ghost\"}') # True\nprint is_json(\"{'rank':17 }\") # False\nprint is_json(\"{\\\"rank\\\":17 }\") # True\n```\n"
  },
  {
    "path": "tech/Redis.md",
    "content": "# Redis for Windows\n\n**Redis** is an in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams.\n\n## Get started\n* Download [redis-latest.zip](https://github.com/ServiceStack/redis-windows/raw/master/downloads/redis-latest.zip).\n* Unzip archive.\n* Open appeared folder.\n* Run *redis-server.exe*.\n* Run *redis-cli.exe*.\n* Use Redis CLI (command line interface) to execute commands.\n\n## Basic commands\n\n* `GET key` - get value by key\n* `SET key value` - save key with value in storage\n* `DEL key` - delete key\n* `Keys *` - show all keys\n* `FLUSHALL` - drop all keys\n"
  },
  {
    "path": "tech/SQL.md",
    "content": "- [Основы SQL](#основы-sql)\n  - [Типы команд в SQL: `DDL`, `DQL`, `DML`, `DCL`, `TCL`](#типы-команд-в-sql-ddl-dql-dml-dcl-tcl)\n- [Инструкции SQL](#инструкции-sql)\n  - [Инструкции для работы с базой данных](#инструкции-для-работы-с-базой-данных)\n  - [Создание таблицы с помощью `CREATE`](#создание-таблицы-с-помощью-create)\n  - [Вставка в таблицу с помощью `INSERT`](#вставка-в-таблицу-с-помощью-insert)\n  - [Запросы `SELECT`](#запросы-select)\n  - [Запросы `SELECT` с условиями `WHERE`](#запросы-select-с-условиями-where)\n  - [Приоритет операторов (`Operator Precedence`)](#приоритет-операторов-operator-precedence)\n  - [Сортировка при помощи `ORDER BY`](#сортировка-при-помощи-order-by)\n  - [Агрегатные функции `AVG`, `COUNT`, `MAX`, `MIN`, `SUM`](#агрегатные-функции-avg-count-max-min-sum)\n  - [Группировка значений при помощи `GROUP BY`](#группировка-значений-при-помощи-group-by)\n  - [Связь таблиц при помощи `JOIN`](#связь-таблиц-при-помощи-join)\n  - [Виртуальная таблица `VIEW`](#виртуальная-таблица-view)\n  - [Изменение данных с помощью `UPDATE`](#изменение-данных-с-помощью-update)\n  - [Удаление данных с помощью `DROP`](#удаление-данных-с-помощью-drop)\n  - [Удаление данных с помощью `TRUNKATE` и `DELETE`](#удаление-данных-с-помощью-trunkate-и-delete)\n  - [Вложенные запросы `SELECT`](#вложенные-запросы-select)\n  - [Объединение результатов запросов при помощи `UNION` и `UNION ALL`](объединение-результатов-запросов-при-помощи-union-и-union-all)\n- [Типы данных SQL](#типы-данных-sql) \n\n\n# Основы SQL\n\n**Язык структурированных запросов** (Structured Query Language, SQL) — язык, предназначенный для управления данными в системе реляционных баз данных (RDBMS).\n\n## Типы команд в SQL: `DDL`, `DQL`, `DML`, `DCL`, `TCL`\n\n### Язык определения данных (`DDL`)\n\n**Язык определения данных** (`Data Definition Language`, `DDL`) состоит из команд, которые используются для определения схемы базы данных. \n\nDDL-команды помогают задать или изменить структуру объектов базы данных.\n\n**Объектами базы данных** (`Database objects`) являются таблицы, индексы, функции, представления, процедуры и триггеры.\n\nПримеры DDL-команд\n* `CREATE` — создание базы данных или её объектов.\n* `ALTER` — изменение структуры базы данных.\n* `DROP` — удаление объектов из базы данных.\n* `TRUNCATE` — удаление всех записей из таблицы, в том числе и записей о самом удалении.\n\n### Язык запросов данных (`DQL`)\n\n**Язык запросов данных** (`Data Query Language`, `DQL`) предоставляет выражения, которые позволяют получить часть данных из базы данных по заданному запросу (query).\n\nПример DQL-команд\n* `SELECT` — получение данных из базы данных.\n\n### Язык манипулирования данными (`DML`)\n\n**Язык манипулирования данными** (`Data Manipulation Language`, `DML`) состоит из команд, которые используются для манипулирования данными, которые хранятся в базе.\n\nПример DML-команд\n* `INSERT` — вставка данных в таблицу.\n* `UPDATE` — обновление данных таблицы.\n* `DELETE` — удаление записей из таблицы.\n\n### Язык контроля над данными (`DCL`)\n\n**Язык управления данными** (`Data Control Language`, `DCL`) предоставляет команды, позволяющие управлять правами доступа, разрешения\n\nПример DCL-команд\n* `GRANT` — предоставление пользователю доступа к базе данных.\n* `REVOKE` — изымание доступа пользователя к базе данных.\n\n### Язык управления транзакциями (`TCL`)\n\n**Язык управления транзакциями** (`Transaction Control Language`, `TCL`) предоставляет команды, позволяющие управлять транзациями в базе данных.\n\nТранзакции выполняются либо полностью, либо никак. Промежуточных результатов быть не может: они отменяются при неудаче.\n\nПримеры TCL-команд\n* `COMMIT` — сохранение результатов успешно выполненной транзакции в базу данных.\n* `ROLLBACK` — откат транзакции в случае ошибок.\n* `SAVEPOINT` — создание контрольной точки в пределах транзакции, чтобы при неудаче откатывать не всю транзакцию целиком.  \n\n# Инструкции SQL\n\n## Инструкции для работы с базой данных\n```SQL\n-- создание бд\nCREATE DATABASE test;\n-- просмотр имеющихся бд\nSHOW DATABASES;\n-- выбор конкретной бд для использования\nUSE test;\n-- удаление бд\nDROP DATABASE test; \n```\n\n## Создание таблицы с помощью `CREATE`\n```SQL\n-- схема\nCREATE TABLE <table_name> (\n  <column_name> <column_type>,\n  <another_column_name> <another_column_type>,\n  <one_more_column_name> <one_more_column_type>,\n  /* ... */,\n  PRIMARY KEY (<column_name>),\n  FOREIGN KEY (<another_column_name>) REFERENCES <another_table_name>(<another_column_name>)\n); \n```\n```SQL\nCREATE TABLE notes (\n  ID INT,\n  title VARCHAR(255) NOT NULL,\n  description VARCHAR(255),\n  PRIMARY KEY (ID)\n); \n```\n### Ограничения\nСтолбцам таблицы можно устанавливать **ограничения** (constraints).\n* `NOT NULL` — столбец не может иметь значение `NULL`.\n* `UNIQUE` — каждое значение в столбце должно быть уникальным.\n* `PRIMARY KEY (<key>)` — *первичный ключ*; уникальный идентификатор строки (row) таблицы. Комбинирует в себе ограничения `NOT NULL` и `UNIQUE`. \n* `FOREIGN KEY (<key>) REFERENCES <table>(<key>)` — *внешний ключ*; уникальный идентификатор строки в другой таблице.\n* `CHEСK (<condition>)` — проверка удовлетворения заданному условию `condition`.\n* `DEFAULT <value>` — задаёт значение по умолчанию.\n\n```sql\nCREATE TABLE timers (\n  ID CHAR(5),\n  seconds INT CHECK (seconds < 60) DEFAULT 0,\n  PRIMARY KEY(ID)\n);\n```\n## Вставка в таблицу с помощью `INSERT`\n```SQL\n-- схема\nINSERT INTO <table_name> (<column_name>, <another_column_name>, /* ... */)\n  VALUES (<value>, <another_value>, /* ... */);\n```\nЕсли в `VALUES` перечислены значения для каждого столбца и в правильном порядке, то столбцы можно не указывать.\n```SQL\nINSERT INTO notes (ID, title, description)\n  VALUES (1, \"Article #1\", \"Description 1\");\n\nINSERT INTO notes\n  VALUES (2, \"Article #2\", \"Description 2\");\n\n-- error (2 values for 3 columns)\nINSERT INTO notes (ID, title, description)\n  VALUES (3, \"Article #3\"); \n\nINSERT INTO notes (ID, title, description)\n  VALUES (4, \"Article #4\", NULL);\n\nINSERT INTO notes (ID, title)\n VALUES (5, \"Article #5\"); \n\n-- error (NOT NULL constraint failed)\nINSERT INTO notes (ID, title, description)\n  VALUES (6, NULL, \"Description 6\");\n```\n```SQL\nINSERT INTO timers\n  VALUES (\"timer1\", 59);\n\n-- error (CHECK constraint failed)\nINSERT INTO timers\n  VALUES (\"timer2\", 61);\n\n-- error (1 value for 2 columns)\nINSERT INTO timers\n  VALUES (\"timer3\");\n\nINSERT INTO timers (ID)\n  VALUES (\"timer4\");\n```\n\n## Запросы `SELECT`\n\n*Получение данных* из *таблицы* происходит при помощи *запросов* `SELECT`.\n```SQL\n-- схема\nSELECT <column_name>, <another_column_name>, /* ... */\n  FROM <table_name>;\n```\nМожно *выбрать определённые столбцы таблицы*, а можно *выбрать* сразу *все* при помощи `*`.\n```SQL\nSELECT * FROM notes;\n\n```\n![SQL Example](../assets/SQL_1.png)\n```SQL\nSELECT * FROM timers;\n```\n![SQL Example](../assets/SQL_2.png)\n\n### `SELECT DISTINCT`\n\nЧтобы запрос *не выдавал повторяющиеся строки*, существует `SELECT DISTINCT`.\n\nНапример, в таблице `notes` две строки содержат `null` в `description`.\n```SQL\nSELECT DISTINCT description FROM notes;\n```\n\n![SQL Example](../assets/SQL_3.png)\n\n*Строка пропускается* только, если *все её значения совпадают* со *значеними другой строки*.\n```SQL\nSELECT DISTINCT title, description FROM notes;\n```\n\n![SQL Example](../assets/SQL_4.png)\n \n## Запросы `SELECT` с условиями `WHERE`\n\n**Ключевое слово WHERE** используется для *указания условий* в *запросе* `SELECT`.\n\n```SQL\n-- схема\nSELECT <column_names>\n  FROM <table_name>\n  WHERE <condition>; \n```\nВ условии `condition` можно *производить сравнение текста* и *чисел*, а также *использовать логические операторы* `AND`, `OR`, `NOT`.\n```SQL\nSELECT *\n  FROM notes\n  WHERE title = \"Article #4\" OR title = \"Article #5\";\n```\n![SQL Example](../assets/SQL_5.png)\n```SQL\nSELECT *\n  FROM notes\n  WHERE title != \"Article #1\" AND description IS NOT NULL;\n```\n![SQL Example](../assets/SQL_6.png)\n```SQL\nSELECT *\n  FROM timers\n  WHERE seconds > 30;\n```\n![SQL Example](../assets/SQL_7.png)\n\n## Логические операторы\n\n**Логические операторы** (англ. `Logical operators`) проверяют заданное выражение на истинность и возвращают логическое значение `TRUE` или `FALSE`.\n\nВ SQL имеются следующие логические операторы:\n* `AND` (логическое \"И\", условное \"И\") - бинарный логический оператор, который возвращает `TRUE`, если оба операнда (оба условия) выполняются, иначе `FALSE`.\n```SQL\nx AND y\n1 > 0 AND 2 > 1 = TRUE\n1 > 0 AND 2 < 1 = FALSE\n``` \n* `OR` (логическое \"ИЛИ\", условное \"ИЛИ\") - бинарный логический оператор, который возвращает `TRUE`, если хотя бы один операнд (одно из условий) выполняются, иначе `FALSE`.\n```SQL\nx OR y\n1 > 0 OR 2 > 1 = TRUE\n1 > 0 OR 2 < 1 = FALSE\n```\n* `NOT` (логическое \"НЕ\", отрицание) - унарный оператор, который возвращает `TRUE`, если значение ложно, и `FALSE`, если значение истино.\n* `IN` (включение, англ. `includes`) - бинарный логический оператор, который возвращает `TRUE`, если операнд содержится в списке выражений.\n```SQL\nx IN (y1, y2, ...)\n0 IN (1, 2, 3) = FALSE\n\"bar IN (\"foo\", \"bar, \"baz\") = TRUE\nWHERE name IN (\"Max\", \"Rob\", \"Tom\")\n```\n* `BETWEEN` (между) - тернарный логический оператор, который возвращает `TRUE`, если заданное значение (*число*, *строка*, *дата*) находится в промежутке между двумя другими значениями.\n```SQL\nx BETWEEN y AND z\n'20230217' BETWEEN '20221212' AND '20230605' = TRUE\n17 BETWEEN 0 AND 10 = FALSE\n```\n* `LIKE` (подобно) - бинарный логический оператор, который проверяет, удовлетволяет ли указанная строка заданному поисковому шаблону (регулярному выражению). Для поиска используются подстановочные знаки (англ. `wildcards`). Например, знак `%` заменяет *любое количество символов*, `_` — только один символ.\n```SQL\n'abc' LIKE 'a_c' AND 'adc' LIKE 'a_c' = TRUE\n'max.starling' LIKE 'max_' = TRUE\n'max.starling' LIKE '_star_' = TRUE\n```\n \n## Приоритет операторов (`Operator Precedence`)\nКогда мы имеем дело со сложным выражением, в котором участвует несколько разных операторов, встаёт вопрос, какие операции выполнятся раньше. Ведь в зависимости от порядка выполнения операций меняется результат.\n\nНапример, у умножения приортитет всегда выше, чем у сложения. Но если поставить оператор группировки, то приоритеты поменяются и результат тоже\n```SQL\n5 + 1 * 5 = 30\n(5 + 1) * 5 = 30\n5 + (1 * 5) = 10\n```\nЧем выше приоритет, тем раньше выполняется операция.\n\nСтоит отметить, что в каждой реализации SQL порядок операторов может отличаться.\n\n#### Приоритет операторов в Postgres\n| Operator/Element | Associativity | Description                                 |\n|------------------|---------------|---------------------------------------------|\n| ::               | left          | PostgreSQL-style typecast                   |\n| [ ]              | left          | array element selection                     |\n| .                | left          | table/column name separator                 |\n| -                | right         | unary minus                                 |\n| ^                | left          | exponentiation                              |\n| * / %            | left          | multiplication, division, modulo            |\n| + -              | left          | addition, subtraction                       |\n| IS               |               | test for TRUE, FALSE, UNKNOWN, NULL         |\n| ISNULL           |               | test for NULL                               |\n| NOTNULL          |               | test for NOT NULL                           |\n| (any other)      | left          | all other native and user-defined operators |\n| IN               |               | set membership                              |\n| BETWEEN          |               | containment                                 |\n| OVERLAPS         |               | time interval overlap                       |\n| LIKE ILIKE       |               | string pattern matching                     |\n| < >              |               | less than, greater than                     |\n| =                | right         | equality, assignment                        |\n| NOT              | right         | logical negation                            |\n| AND              | left          | logical conjunction                         |\n| OR               | left          | logical disjunction                         |\n\n#### Приоритет операторов в MySQL\n```MySQL\nINTERVAL\nBINARY, COLLATE\n!\n- (unary minus), ~ (unary bit inversion)\n^\n*, /, DIV, %, MOD\n-, +\n<<, >>\n&\n|\n= (comparison), <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN, MEMBER OF\nBETWEEN, CASE, WHEN, THEN, ELSE\nNOT\nAND, &&\nXOR\nOR, ||\n= (assignment), :=\n```\n\n#### Приоритет операторов в MariaDB\n```SQL\nINTERVAL\nBINARY, COLLATE\n!\n- (unary minus), [[bitwise-not|]] (unary bit inversion)\n|| (string concatenation)\n^\n*, /, DIV, %, MOD\n-, +\n<<, >>\n&\n|\n= (comparison), <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN\nBETWEEN, CASE, WHEN, THEN, ELSE, END\nNOT\n&&, AND\nXOR\n|| (logical or), OR\n= (assignment), :=\n```\n\n#### Приоритет операторов в Oracle\n```SQL\n()\n* /\n+ -\n= <> < > <= >=\nIS (IS NULL, IS NOT NULL, IS EMPTY, IS NOT EMPTY)\nBETWEEN\nNOT\nAND\nOR\n```\n\nКак видно, приоритеты могут отличаться, но общие правила есть всегда:\n1) Оператор группировки `()` (англ. `parentheses`, `grouping`) имеет *наивысший приоритет*, в то время, как оператор присваивания `=` - *наинизший*.\n2) Арифметические операторы всегда идут в следующем порядке: *возведение в степень*, *умножение/деление*, затем *сложение/вычитание*.\n3) Логические операторы всегда идут в следующем порядке: сперва отрицание `!`, `NOT`, затем логическое \"И\" (`AND`, `&&`), затем *логическое \"ИЛИ\"* (`OR`, `||`).\n4) Сперва идут *арифметические операторы*, затем *операторы сравнения*, затем *логические операторы*.\n\n\nНапример, поскольку у `AND` приоритет выше, чем у `OR`, следующие две строчки эквивалентны (то есть оператор группировки можно смело убирать)\n```SQL\n(x < y AND y < z) OR (x > y AND y > z)\nx < y AND y < z OR x > y AND y > z\n```\n\n## Сортировка при помощи `ORDER BY`\n\n**Ключевое слово ORDER BY** используется для *сортировки результатов запроса* `SELECT`.\n\nМожно указать *порядок сортировки*:\n1) `ASC` (ascending) — *по возрастанию*, используется *по умолчанию*;\n2) `DESC` (descending) — *по убыванию*.\n```SQL\nSELECT *\n  FROM notes\n  ORDER BY description;\n```\n![SQL Example](../assets/SQL_orderby)\n```SQL\nSELECT *\n  FROM notes\n  ORDER BY ID DESC;\n```\n![SQL Example](../assets/SQL_orderby_2.png)\n\n## Агрегатные функции `AVG`, `COUNT`, `MAX`, `MIN`, `SUM`\n\n**Агрегатные функции** *применяются* к *столбцу* и *возвращают единое значение*.\n\n* `AVG (column_name)` — среднее значение столбца `column_name`.\n* `COUNT (column_name)` — количество строк.\n* `MAX (column_name)` — наибольшее значение столбца.\n* `MIN (column_name)` — наименьшее значение столбца.\n* `SUM (column_name)` — сумма значений в столбце.\n\n```SQL\nSELECT COUNT(ID), MIN(ID), AVG(ID), MAX(ID), SUM(ID) FROM notes;\n```\n![SQL Example](../assets/SQL_aggregate.png)\n\n### Оператор `AS`\n\n**Оператор  AS** используется для *переименования столбца* в *результате* запроса `SELECT`.\n```sql\n-- схема\nSELECT <column_name> AS <name> FROM <table_name>;\n```\n\n```sql\nSELECT MAX(seconds) AS max_seconds FROM timers;\n```\n![SQL Example](../assets/SQL_as.png)\n\n## Группировка значений при помощи `GROUP BY`\n\n**Ключевое слово GROUP BY** используется для *группировки значений* в *столбце*. \n```SQL\n-- схема\nSELECT <column_names>\n  FROM <table_name>\n  GROUP BY <column_name>;\n```\n\n*Несколько строк группируются* в *одну*, что *позволяет* использовать *агрегатные функции* для *каждой* получившейся *группы*.\n```SQL\nSELECT *, COUNT(ID) AS rows_count\n  FROM notes\n  GROUP BY description;\n```\n![SQL Example](../assets/SQL_groupby.png)\n\nКогда имеет смысл использовать группировку?\nКогда столбец имеет повторяющиеся значения. Например, профессия - мы можем сгруппировать данные средней зарлаты по всем сотрудникам.\n ```SQL\nSELECT job, AVG(salary) AS job_salary\n  FROM jobs_data\n  WHERE job = \"SysAdmin\"\n  GROUP BY job;\n```\nВ этом случае в выборку попадут строки с уникальными значениями `job`, каждая из которых будет содержать вычисленное значение средней заработной платы `AVG(salary)`.\n\nЕсли необходимо вычислить зарплату по какой-либо конкретной профессии, то следует добавить условие `WHERE`. Но стоит учесть, что для `WHERE` нельзя указывать выражение, содержащее агрегатную функцию.\n```SQL\nSELECT job, AVG(salary) AS sysadmin_salary\n  FROM jobs_data\n  WHERE job = \"SysAdmin\"\n  GROUP BY job;\n```\n\nМожно *группировать одновременно* по *нескольким колонкам*: в этом случае в выборку попадут *все существующие комбинации* будут учтены все допустимые значения.\nНапример,\n```SQL\nSELECT job, seniority, AVG(salary) AS sysadmin_salary\n  FROM jobs_data\n  GROUP BY job, seniority;\n```\nЗапрос выше позволяет сгруппировать данные не только по профессии, но и по стажу работы в соответствии с позициями (`Junior`, `Middle`, `Senior`).\n\nВажно также отметить, что при использовании группировки `GROUP BY` в `SELECT` обычно имеет смысть выбирать только те колонки, которые:  \n1) либо указаны в `GROUP BY` через запятую.  \n2) либо вычисляются в одной из агрегационной функции.  \nВсе остальные колонки вернут непредвиденный результат, зависящий от движка SQL: это может быть первое значение из группы, последнее или же вовсе ошибка выполнения запроса с пометкой, что запрашиваемое поле нужно добавить в `GROUP BY`.\n\n### `GROUP BY` с условиями `HAVING`\n\nКак уже отмечалось выше, *ключевое слово* `WHERE` *не может использоваться* с *агрегатными функциями*. Для этих целей ввели **ключевое слово HAVING**, которое должно быть использовано сразу же после `GROUP BY`.\n```SQL\n-- схема\nSELECT <column_names>\n  FROM <table_name>\n  GROUP BY <column_name>\n  HAVING <condition>;\n```\n\n```SQL\nSELECT *, COUNT(ID) AS rows_count\n  FROM notes\n  GROUP BY description\n  HAVING COUNT(description) = 1;\n```\nЗапрос ниже выдаст ошибку:\n```SQL\n-- ошибка\nSELECT *, COUNT(ID) AS rows_count\n  FROM notes\n  WHERE COUNT(description) = 1;\n  GROUP BY description\n```\n\n### Когда использовать `WHERE`, а когда `HAVING`\n\nЕсли выражение не содержит агрегатную функцию, то его можно поместить либо в `WHERE`, либо в `HAVING` с сохранением результата. Например,\n```SQL\nWHERE created_at > '1998-02-17'\nGROUP BY created_at\n-- или\nGROUP BY created_at\nHAVING created_at > '1998-02-17'\n```\nЧто будет работать производительнее?\n\n`WHERE` должно отработать быстрее `HAVING`, поскольку `WHERE` применяется раньше, чем `HAVING` (`HAVING` выполняется предпоследним: перед `LIMIT`), а значит все последующие операторы оперируют меньшим числом строк (меньше работы - меньше затраченных ресурсов - меньше время выполнения). Некоторые SQL-движки позволяют сгладить это таким образом, что.\n\nНиже представлены несколько сносок со Stackoverflow на эту тему.\n\n> The SQL Standard theory says that WHERE restricts the result set before returning rows and HAVING restricts the result set after bringing all the rows. So WHERE is faster. On SQL Standard compliant DBMSs in this regard, only use HAVING where you cannot put the condition on a WHERE (like computed columns in some RDBMSs.)\n\n> The HAVING clause is applied nearly last, just before items are sent to the client, with no optimization. (LIMIT is applied after HAVING.)\n\n> The two queries are equivalent and your DBMS query optimizer should recognise this and produce the same query plan. It may not, but the situation is fairly simple to recognise, so I'd expect any modern system - even Sybase - to deal with it.\n\n> HAVING clauses should be used to apply conditions on group functions, otherwise they can be moved into the WHERE condition. For example. if you wanted to restrict your query to groups that have COUNT(DZIALU) > 10, say, you would need to put the condition into a HAVING because it acts on the groups, not the individual rows.\n\n## Связь таблиц при помощи `JOIN`\n\n**Ключевое слово JOIN** используется для связи нескольких таблиц в одну по их общим атрибутам.\n```sql\n-- схема\nSELECT <column_names>\n  FROM <table_A>\n  JOIN <table_B>\n  ON <table_A.column_name_1> = <table_B.column_name_2>; \n```\n\nС `JOIN` удобно использовать **псевдонимы** — *сокращённые названия таблиц*.\n```sql\n-- схема\nSELECT <column_names>\n  FROM <table_A> A\n  JOIN <table_B> B\n  ON A.<column_name_1> = B.<column_name_2>; \n```\n\nСоздадим и заполним ещё одну таблицу для примера.\n```sql\nCREATE TABLE seconds (\n  ID INT,\n  value INT CHECK (value < 60) DEFAULT 0,\n  PRIMARY KEY(ID)\n);\n\nINSERT INTO seconds VALUES(1, 0);\nINSERT INTO seconds VALUES(2, 30);\nINSERT INTO seconds VALUES(3, 59);\n\nINSERT INTO timers VALUES (\"timer5\", 15);\n\nSELECT * FROM timers;\nSELECT * FROM seconds;\n```\n![SQL Example](../assets/SQL_join_1.png)\n![SQL Example](../assets/SQL_join_2.png)\n\n* `JOIN`, `INNER JOIN` (внутреннее объединение) — строки, содержащиеся и в A, и в B.\n\n```sql\nSELECT *\n  FROM seconds A\n  JOIN timers B\n  ON A.value = B.seconds;\n```\n![SQL Example](../assets/SQL_join_3.png)\n\n* `LEFT JOIN` (левосторонее объединение) — строки, содержащиеся в A, даже при их отсутствии в B.\n```sql\nSELECT A.ID AS a_id, B.ID AS b_id, A.value, B.seconds\n  FROM seconds A\n  LEFT JOIN timers B\n  ON A.value = B.seconds;\n```\n![SQL Example](../assets/SQL_join_4.png)\n\n* `LEFT JOIN` без пересечения — строки, содержащиеся в A, но не содержащиеся в B.\n```sql\nSELECT *\n  FROM seconds A\n  LEFT JOIN timers B\n  ON A.value = B.seconds\n  WHERE B.seconds IS NULL;\n```\n![SQL Example](../assets/SQL_join_5.png)\n\n*Аналогично* определяются `FULL OUTER JOIN` (все строки в A и все в B), `FULL OUTER JOIN` без пересечения, `RIGHT JOIN` (правостороннее объединение), `RIGHT JOIN` без пересечения.\n\n* `FULL OUTER JOIN` без пересечения.\n```sql\nSELECT *\n  FROM seconds A\n  FULL OUTER JOIN timers B\n  ON A.value = B.seconds\n  WHERE B.seconds IS NULL AND A.value IS NULL;\n```\n\nМожно *объединить большее количество таблиц*.\n```sql\nSELECT A.column_1, B.column_2, C.column_3\n  FROM table_A A \n  JOIN table_B B ON A.column_1 = B.column_2 \n  JOIN table_C C ON B.column_2 = C.column_3;\n```\n\n## Виртуальная таблица `VIEW`\n\n**VIEW** — *виртуальная таблица*. \n\nВиртуальная таблица похожа на обычную таблицу, но ей не является, поскольку не хранит реальные данные, а лишь ссылается на них. По этой причине данные в виртуальной таблице нельзя изменить.\n\nСоздаётся на основании результата выполнения выражения (обычно результат запроса `SELECT`). Может содержать данные из нескольких таблиц одновременно.\n```SQL\n-- схема\nCREATE VIEW <view_name> AS\n  SELECT <column_names>\n  FROM <table_name>; \n```\n\n```SQL\nCREATE VIEW timers_less_30sec AS\n  SELECT *\n  FROM timers\n  WHERE seconds < 30;\n\nSELECT * FROM timers_less_30sec;\n```\n![SQL Example](../assets/SQL_view_1.png)\n\nПоскольку `VIEW` хранит ссылки на реальные данные, он знает всё об их изменениях и всегда отображает последнюю версию.\n```SQL\nINSERT INTO timers VALUES (\"timer7\", 25);\n\nSELECT * FROM timers_less_30sec;\n```\n![SQL Example](../assets/SQL_view_2.png)\n\n\n## Изменение данных с помощью `UPDATE`\n\nОбновление строк таблицы\n```SQL\nUPDATE <table_name>\n  SET <column_name> = <value>, <another_column_name> = <another_value>, /* ... */\n  WHERE <condition>; \n```\n# Удаление данных с помощью `DROP`\nУдаление таблицы.\n```SQL\nDROP TABLE <table_name>; \n```\nУдаление `VIEW`.\n```SQL\nDROP VIEW <view_name>; \n```\n\n## Удаление данных с помощью `TRUNKATE` и `DELETE`\n\nУдаление строк в таблице по какому-то условию.\n```SQL\nDELETE FROM <table_name>\n  WHERE <condition>;\n```\n\n*Удалить все строки* из *таблицы* можно *двумя способами*.\n```SQL\nDELETE FROM <table_name>;\n-- или (Transact-SQL)\nTRUNCATE TABLE <table_name>;\n```\n\n`TRUNCATE` работает быстрее, но имеет следующие ограничения.\n* Отсутствие условий `WHERE`.\n* Не отрабатывают триггеры (в том числе на удаление).\n* `TRUNCATE` не работает, если на удаляемую таблицу имеется ссылка по внешнему ключу.\n* Не журнализируется удаление отдельных строк таблицы.\n\n## Вложенные запросы `SELECT`\n\n*Запросы* `SELECT` могут быть **вложенными** (nested). *Вложенный запрос* называется **подзапросом** (subquery).\n```SQL\nSELECT <column_names>\n  FROM  <table_name>\n  WHERE <value> IN (SELECT <column-name>\n                      FROM <another_table_name>\n                      WHERE <condition>)\n```\n```SQL\nSELECT <column_name> = (SELECT <column_name> FROM <table_name> WHERE <condition>)\n  FROM <table_name>\n  WHERE <condition>\n```\n\n## Объединение результатов запросов при помощи `UNION` и `UNION ALL`\n\nОператор объединения `UNION` предназначен для объединения результатов двух и более `SELECT`-запросов.\n\nУсловия, при которых применим `UNION`, накладываются на его операнды. Каждый `SELECT`, попадающий под объединение, должен иметь\n1) Одинаковое количество столбцов с другими `SELECT`.\n2) Типы данных в столбцах должны совпадать.\n3) Порядок столбцов также должен совпадать.\n\n```sql\nSELECT <column_names...> FROM table1\nUNION\nSELECT <column_names...> FROM table2;\n```\nОператор `UNION` по умолчачнию отображает лишь уникальные строки.\n\nЧтобы позволить повторения, необходимо использовать оператор `UNIAN ALL`.\n```sql\nSELECT <column_names...> FROM table1\nUNION ALL\nSELECT <column_names...> FROM table2;\n```\n\n# Типы данных в PostgreSQL\n\n## Дата\n\nПриведение текущей даты в формате `YYYY-MM-DD`.\n```sql\nCAST(CURRENT_TIMESTAMP AS DATE)\n-- или\nCURRENT_TIMESTAMP::DATE\n```\nМожно подставить название столбца, чтобы привести его значение в формат `YYYY-MM-DD`.\n```sql\nSELECT created_at::DATE\n  FROM users\n```\n\n"
  },
  {
    "path": "tech/Scala.md",
    "content": "\n\n"
  },
  {
    "path": "tech/Shell.md",
    "content": "# Linux\n\n## Команды\n* `echo <данные>` - вывод данных в консоль.\n* `echo $USER`, `whoami` - вывод имени текущего пользователя\n* `mkdir directory_name` - создание папки.\n* `ls` - список файлов и папок в текущей директории.\n* `rm file_name` - удаление файла.\n* `rm -rf directory_name` - удаление папки.\n* `cat file_name` - вывод содержимого файла в консоль.\n* `cd directory_name` - переход в папку.\n* `cd ..` - переход в папку выше.\n* `printenv` - вывести все переменные окружения (англ. `environment variables`)\n\n```shell\n# /app/src\ncd ..\n# /app\n```\n* `ssh <username>@<password>` - вход на сервер по ssh при помощи имени и пароля (интерактивный режим).\n* `which <название команды>` - вывод месторасположения команды.\n```shell\nwhich docker-compose\n# выведет /usr/bin/docker-compose\n```\n\n### sudo и переменные окружения\n\nПо умолчанию `sudo` отказывается принимать переменные окружения.\n```\nsudo: sorry, you are not allowed to set the following environment variables: POSTGRES_USER, POSTGRES_PASSWORD\n```\nЧтобы это исправить, необходимо установить доступ к этим переменным в `sudoers`.\n```js\nsudo nano /etc/sudoers.d/ld_preload\n/* или */\nsudo vim /etc/sudoers.d/ld_preload\n```\nНужно дописать переменные в файл.\n```js\n/* /etc/sudoers.d/ld_preload */\nDefaults env_keep += \"POSTGRES_USER\"\nDefaults env_keep += \"POSTGRES_PASSWORD\"\n```\n\n# Windows\n\n## Команды\n* `dir` - список файлов и папок в текущей директории (аналог `ls` в Linux).\n* `type` - вывод содержимого файла в консоль (аналог `cat` в Linux).\n* `cls` - очистить консоль (аналог `clear` в Linux)\n* `<disk_name>:` - переход на диск disk_name.\n\n```shell\n# переход на диск C\nC:\n# переход на диск D\nD:\n```\n\n## PowerShell\n\n\n# Shebang `!#`\n\n**Шебанг** (англ. `shebang`, `sha-bang`, `sharp-exclamation`, `hashbang`, `pound-bang`, `hash-pling`) - последовательность из двух символов `#!` в начале скрипта. \n\nЕсли Unix выполняет скрипт с шебангом в начале скрипта, загрузчик программ рассматривает остаток строки после шебанга как имя файла программы-интерпретатора. \n\nНапример, чтобы выполнить файл с помощью `sh` (`Bourne shell`), необходимо написать в начале скрипта.\n```sh\n#!/bin/sh\n...\n```\n\nПоскольку `#` является символов начала комментария во многих скриптовых языках программирования, строка с шебангом обычно пропускается интерпретатором.\n\nJavaScript не является таким языком, поскольку комментарии в нём объявляются через `/**/`, `//`. Однако в ES14 (ECMAScript2023) добавили поддержку шебанга.\n```\n#!/usr/bin/env node\nconsole.log('Hello JavaScript!');\n```\nСкриншот из Chrome:  \n\n![image](https://github.com/Max-Starling/Notes/assets/22237384/c15eeac6-e9d6-495d-b9ad-6a54be56dc22)\n\n# Комментарии\n`#`\n\n# Замена символов\n`-replace '<from>','<to>'`\n```powershell\n$foo = 'hi-notes!'\n$bar = $foo -replace '[-]',' ' -replace '!', ''\necho $bar # hi notes\n```\n"
  },
  {
    "path": "tech/Shortcuts.md",
    "content": "# Сочетания клавиш\n\n## Chrome\n* `Ctrl + Tab`, `Ctrl + Shift + Tab` — переключение между вкладками вправо и влево.\n* `Ctrl + L` — выделение URL или текста в адресной строке.\n* `Ctrl + N` — новое окно.\n* `Ctrl + T` — новая вкладка.\n* `Ctrl + Shift + N` — новое окно в режиме инкогнито.\n* `Ctrl + клик` — открытие ссылки в новой фоновой вкладке.\n* `Ctrl + Shift + клик` — открытие ссылки в новой вкладке с переключением на неё.\n* `Ctrl + Shift + T` — восстановление последней закрытой вкладки.\n* `Ctrl + W` — закрытие активной вкладки или всплывающего окна.\n* `Ctrl + F` — поиск.\n* `Ctrl + U` — исходный код страницы.\n* `Ctrl + \"+\"`, Ctrl + \"-\", Ctrl + 0 — масштабирование.\n* `Space` — поэкранная прокрутка страницы.\n* `Shift + Esc` — диспетчер задач.\n* `Alt + Enter` — открытие URL в новой вкладке.\n"
  },
  {
    "path": "tech/Sisense.md",
    "content": "\n# Основные понятия\n#\n![image](https://user-images.githubusercontent.com/22237384/220296705-a06e8dca-9103-4e90-9bce-48ea758d6815.png)\n\n## Data Model\n\n**Модель данных** (англ. `Data Model`) в Sisense представляет собой ничто иное, как схему базы данных, то есть множество таблиц, отображённых в виде вершин графа и рёбер между вершинами, которые показывают связи между таблицами.\n\nКаждая таблица имеет набор столбцов, каждый из которых имеет определённый тип данных из списка поддерживаемых в Sisense типов.\n\nСтроки таблиц можно получить\n\nData Model может быть двух типов:\n* `live` - делает запросы напрямую к сторонней базе данных, используя строку подключения (англ. `connection string`), переданный при инициализации.\n* `elastic cube` - данные клонируются со сторонней базы данных в Sisense, далее запросы отправляются непосредственно к скопированным данным.\n\nСмешивать эти типы невозможно.\n\n## Dataset\n\nЧтобы получить строки таблиц, необходимо отправить запрос. Одной таблице в модели данных может соответствовать несколько источников данных.\n\n**Набор данных** (англ. `Dataset`) представляет собой один из источников данных, используемых текущей моделью данных. Например, если модель данных содержит данные из трёх CSV-файлов и двух баз данных SQL, то всего у нас 5 источников, а значит 5 наборов данных.\n\nНапример, `connection string` может быть\n```\nServer=ServerName;Database=DatabaseName;User Id=UserName;Password=UserPassword;\nServer=localhost;Database=SisenseNotes;User Id=SuperAdmin;Password=qwerty;\n```\n\nКаждый набор данных содержит информацию о подключении к своему источнику (например, строку подключения к SQL-базе или путь к CSV-файлу)\n\n\n## Table\n"
  },
  {
    "path": "tech/Snowflake.md",
    "content": "\n# Хранение больших объёмов информации\n\nОба понятие \"озеро\" и \"склад данных\" связаны с хранением огромного количества данных (обычно террабайты, десятки террабайт и больше) \n\n## Об озере даннных (`Data Warehouse`)\n\n**Озером данных** (англ. `Data Lake`) называют репозитарий, в котором *хранятся огромные объемы сырых данных* (англ. `vast pool of raw data`) в их *первоначальном*, *неотформатированном виде* до тех пор, *пока они не будут использованы*. \n\nНапример, хранение данных в *озере данных* может подобиться специалистам из области *Data Science* для *изучения* таких данных, *применения* к ним *машинного обучения* и *разничных аналитических функций*. \n\nНа *момент хранения данных* в *озере* ещё *не ясно*, *как и когда данные будут использованы* - они просто *ждут подходящего случая*.\n\n## О хранилище данных (`Data Lake`)\n\n**Хранилищем данных** (англ. `Data Warehouse`, `DW`, *дословно* **складом данных**, называют такую *систему управления данными* (англ. `data management system`), в которую *размещают огромные объёмы данных* из *разных источников* (*других баз данных*, *приложений*, *файлов* и так далее) и затем на основании этих данных *подготавливают отчётов бизнес-анализа* (англ. `business intelligence`) и *составляют* *аналитику* (англ. `analytics`). \n\nТаким образом, данные в хралищие данных чаще всего используется бизнес-аналитиками в аналитичеких целях. Например, для построения аналитических графиков и составления отчётов по ним. Данные в хранилище данных попадают в уже отформатированном виде и их цель применения уже задана.\n\nДанные, послупающие из разных источников, автоматически адаптируются под единый формат и становятся доступны для поисковых запросов.\n\n<!-- Чаще всего хранилища данных содержат огромные объёмы исторической информации (террабаты или даже десятки террабайт). -->\n\n<!-- Данные чаще всего поступают из многих источников в разных форматах, которые затем адаптируются под один формат и доступны для выполнения поисковых запросов. -->\n\n<!-- Данные в такой системе обычно доступны только для чтения. -->\n\n## Различия между складом и озером данных\n\n1) Хранение данных. Данные в озере хранятся в исходном, неотформатированном или слабоотформатированном виде. Данные на складе хранятся в чётко заданном виде, то есть они структурированны и отформатированны.\n2) Цель и время применения данных. Цель и время использования данных из озера обычно не ясны на данный момент, в то время, как данные на складе размещаются с целью их дальнейшего анализа (который происходит постоянно, в режиме реального времени, или интервально).\n3) Кто использует данные. Чаще всего данные в озере использубтся Data Science специалистами, а данные на складе - аналитиками.\n\nВполне логично, что данные из озера могут попасть на склад для их структуризации, форматирования и аналики, но отправлять данные со склада в озеро обычно не имеет смысла.\n\n# Что такое Snowflake?\n\n\nSnowflake - это хранилище данных, которое полностью выполняется в облачной инфраструктуре и не может быть запущено на некотором приватном облаке.\n\nСистема Snowflake не является реляционной базой данных, поэтому в ней не поддерживается концепция первичных и вторичных ключей (англ. `primary and foreign keys`, `PK & FK`).\n\nТем не менее, Snowflake поддерживает набор SQL-команд для удобства чтения, в том числе:\n- DDL/DML,\n- SQL Functions,\n- User Defined Functions (UDFs), процедуры с использованием JavaScript.\n\nПоддерживаются команды:\n```sql\nSELECT\nJOIN\nINSERT UPDATE DELETE\nVIEW\n```\nа также merge statements, subqueries, ACID transactions, Analytical Aggregations (cube, rollup, groping sets, windowing functions, connect-by, recursive CTE)\n\nA data warehouse is a type of data management system that is designed to enable and support business intelligence (BI) activities, especially analytics. Data warehouses are solely intended to perform queries and analysis and often contain large amounts of historical data.\n\nIn computing, a data warehouse, also known as an enterprise data warehouse, is a system used for reporting and data analysis and is considered a core component of business intelligence. DWs are central repositories of integrated data from one or more disparate sources.\n"
  },
  {
    "path": "tech/Vim.md",
    "content": "\n## Команды\n* `i` - режим вставки.\n* `ESC` - режим просмотра.\n* `:q` - выход.\n* `:q!` - выход без сохранения.\n* `u`, `:u` - отмена предыдущего действия.\n* `CTRL+R` - отмена отмены предыдущего действия.\n"
  },
  {
    "path": "tech/Webpack.md",
    "content": "\n## Плагины\n\n**DefinePlugin** позвоняет созначать глобальные константы, которые могут быть заданы во время компиляции. Это особенно полезно, когда есть разные версии сборок (builds): например, `production` и `development`.\n```sh\nnpm start NODE_ENV=production\n```\n```js\nnew webpack.DefinePlugin({\n  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),\n});\n/* после этого переменную можно использовать в JavaScript */\n```\n"
  },
  {
    "path": "tech/jQuery.md",
    "content": "\n## Обращение к элементу\nОбращение к элементу при помощи `jQuery` работает аналогино методу `document.querySelector`:\n```js\ndocument.querySelector('#element-id') // обращение к элементу по атрибуту id\ndocument.querySelector('.element-class') // обращение к элементу по атрибуту class \n```\nТолько для подобного поиска элемента используется метод `$`.\n```js\n$('#element-id')\n$('.element-class')\n```\n\n\n## Сокрытие элемента\n*Убрать элемент* со страницы:\n```js\n$('.element-class').hide();\n/* или */\n$('.element-class').css('display', 'none');\n```\n*Вернуть элемент* на страницу:\n```js\n$('.element-class').show();\n/* или */\n$('.element-class').css('display', 'block');\n```\n"
  },
  {
    "path": "tech/nvm.md",
    "content": "# NVM for Windows\n## Get started\n* Open [nvm-windows releases](https://github.com/coreybutler/nvm-windows/releases)\n* Download nvm-setup.zip\n* Unzip the archive\n* Start nvm-setup.exe\n* Agree with all (even in the case of a huge number of modal windows during installation)\n* Run in cmd as Administrator: \n```cmd\nC:\\Users\\<Username>\\AppData\\Roaming\\nvm\\nvm.exe\n```\n## Basic commands\n* `node -v` - check nodejs version\n* `nvm install <version>` - install necessary \n* `nvm list` - check installed versions\n* `nvm use <version>` - set nodejs version\n* `nvm version` - version of nvm\n\n## Issue\n\nIf **nodejs version doesn't change**:\n* Rename \"C:\\Program Files\\nodejs\" to \"C:\\Program Files\\nodejsx\"\n* Restart console\n\n\n"
  },
  {
    "path": "tech/pip.md",
    "content": "## О репозитории Python Package Index\n**Python Package Index (PyPI)** - это *репозиторий*, *хранящий программное обеспечение* для *языка* Python в виде *пакетов*. *Пакеты находятся* в *открытом доступе* и *разработанны сообществом* Python-разработчиков.\n\n*Аналогом* `PyPI` для языка *JavaScript* является *репозиторий* **NPM Registry**.\n\n## О менеджере пакетов PIP\n\n**PIP** (англ. `Package Installer for Pyton`) - это *менежер пакетов* для языка Python, который позволяет устанавливать их из репозитория PyPI и затем управлять ими при помощи консольной команды `pip`.\n\n*Аналогом* `PIP` является *репозиторий* **NPM** (англ. `NodeJS Package Manager`).\n\n## Установка и обновление `PIP`\n\nДля `Windows`, `Linux` и `MAC` *процесс установки* `PIP` *ничем не отличается*.\n\nДля начала убедитесь, что у вас установлен *Python* и он *доступен для запуска из терминала*:\n```css\n> py\n/* или */\n> python\n```\nУ меня установлены разные версии, поэтому обе команды работают.\n![image](https://user-images.githubusercontent.com/22237384/169065170-9258362e-cb7d-4882-8aa5-2e88bd6a8de0.png)\nЕсли заходит в терминал `python`, то всё хорошо.  \nНужно ввести `exit()` для выхода из него.\n\nЕсли Python установлен на Windows, но не виден в терминале, то следует добавить папку, в которой лежит `py.exe` или `python.exe` в переменные окружения, Path (`Environment Variables -> Path`).\n\nЕсли Python уже установлен и добавлен в переменные окружения, то достаточно ввести в теринале ОДНУ из следующих команд (`py` или `python` *зависит* от *версии* `Python`):\n```css\npy -m ensurepip --upgrade\n/* или */\npython -m ensurepip --upgrade\n```\n\n\n\n## Установка пакетов при помощи `PIP`\n### Установка пакета последней версии\n```css\npip install package_name\n``` \n### Установка пакета определённой версии\nВерсия пакета указывается через `==` после его названия в формате `MAJOR.MINOR.PATCH`:\n```css\npip install <package_name>==<version>\n```\nНапример, установим пакет `WTForms` версии 2.3.3:\n```css\npip install WTForms==2.3.3\n```\n### Установка пакетов из списка\nИмеется возможность передавать список зависимостей для установки в виде текстового файла. В этом случае менеджер поочерёдно установит все зависимости.\n```css\npip install -r ./dependencies.txt\n```\n```css\n/* ./dependencies.txt */\nflask\nflask-cors\nflask-api\nflask-admin\nflask-migrate\nWTForms==2.3.3\n```\n"
  }
]