Full Code of Max-Starling/Notes for AI

master 6c15bf62cb11 cached
72 files
1.1 MB
341.6k tokens
1 requests
Download .txt
Showing preview only (1,733K chars total). Download the full file or copy to clipboard to get everything.
Repository: Max-Starling/Notes
Branch: master
Commit: 6c15bf62cb11
Files: 72
Total size: 1.1 MB

Directory structure:
gitextract_2bqs7fmq/

├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       ├── custom.md
│       └── feature_request.md
├── Architecture-Design.md
├── Auth.md
├── Browsers.md
├── Bundling.md
├── CSS.md
├── CyberSecurityFundamentals.md
├── Data.md
├── DataModels-Databases.md
├── DataStructures.md
├── DataTypes.md
├── Development.md
├── DiscreteMath.md
├── Docker.md
├── Elasticsearch.md
├── Encoding.md
├── Features.md
├── Flux-Redux-Vuex-Mobx.md
├── FunctionalProgramming.md
├── Git.md
├── GraphQL-REST.md
├── HTML.md
├── InterviewQuestions.md
├── JavaScript.md
├── JavaScriptDOM.md
├── NodeJS.md
├── ProductQuality.md
├── Programming.md
├── ProgrammingLanguageCharacteristics.md
├── README.md
├── React.md
├── Testing.md
├── TypeScript.md
├── _config.yml
├── in-progress/
│   ├── Algorithms-Structures.md
│   ├── C++.md
│   ├── Colors.md
│   ├── English.md
│   ├── Protocols.md
│   ├── ReactNative.md
│   ├── VSCode.md
│   └── npm.md
└── tech/
    ├── ApacheVelocity.md
    ├── Chai-Mocha.md
    ├── ESlint-TSlint-Prettier.md
    ├── ErrorHandling.md
    ├── Files.md
    ├── Firebase.md
    ├── Heroku.md
    ├── Jest.md
    ├── Klaviyo.md
    ├── Kubernetes.md
    ├── Microservices.md
    ├── MongoDB.md
    ├── Parcing-Preprocessing-StaticChecking.md
    ├── PostCSS.md
    ├── Postman.md
    ├── Python.md
    ├── Redis.md
    ├── SQL.md
    ├── Scala.md
    ├── Shell.md
    ├── Shortcuts.md
    ├── Sisense.md
    ├── Snowflake.md
    ├── Vim.md
    ├── Webpack.md
    ├── jQuery.md
    ├── nvm.md
    └── pip.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version [e.g. 22]

**Smartphone (please complete the following information):**
 - Device: [e.g. iPhone6]
 - OS: [e.g. iOS8.1]
 - Browser [e.g. stock browser, safari]
 - Version [e.g. 22]

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/custom.md
================================================
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''

---




================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: Architecture-Design.md
================================================
- [Определения](#определения)
  - [Архитектурный стиль](#архитектурный-стиль)
  - [Архитектурный паттерн](#архитектурный-паттерн)
  - [Паттерн проектирования](#паттерн-проектирования)
  - [Принцип проектирования](#принцип-проектирования)
- [Архитектурные стили](#архитектурные-стили)
  - [Монолитная архитектура](#монолитная-архитектура)
  - [Микросервисная архитектура](#микросервисная-архитектура)
  - [Многослойная архитектура](#многослойная-архитектура)
- [Архитектурные паттерны](#архитектурные-паттерны)
  - [MVC (1979)](#mvc-1979)
  - [Иерархический MVC (2000), PAC (1987)](#иерархический-mvc-2000-pac-1987)
  - [MVP (1996)](#mvp-1996)
  - [MVVM (2005)](#mvvm-2005)
  - [MVPVM](#mvpvm)
  - [EBI (1992)](#ebi-1992)
  - [Трёхуровневая архитектура](#трёхуровневая-архитектура)
  - [DDD (2003)](#ddd-2003)
  - [Шестиугольная архитектура, Порты и Адаптеры (2005)](#шестиугольная-архитектура-порты-и-адаптеры-2005)
  - [Луковая архитектура (2008)](#луковая-архитектура-2008)
  - [Кричащая архитектура (2011)](#кричащая-архитектура-2011)
  - [Чистая архитектура (2012)](#чистая-архитектура-2012)
- [Паттерны проектирования](#паттерны-проектирования)
  - [Порождающие](#порождающие)
  - [Структурные](#структурные)
  - [Поведенческие](#поведенческие)
- [Принципы проектирования](#принципы-проектирования)
  - [Поддерживаемый код](#поддерживаемый-код)
  - [Принципы пакетов](#принципы-пакетов)
  - [Принцип разделения ответственности (SoC)](#принцип-разделения-ответственности-soc)
  - [Принцип инверсии управления (IoC)](#принцип-инверсии-управления-ioc)
  - [Основные принципы ООП](#основные-принципы-ооп)
  - [Основные термины ООП](#основные-термины-ооп)
  - [Композиционный принцип повторного использования](#crp-композиционный-принцип-повторного-использования)
  - [Принципы SOLID](#принципы-solid)
  - [Принцип DRY](#принцип-dry)
  - [Принцип KISS](#принцип-kiss)
  - [Принцип YAGNI](#принцип-yagni)


# Определения

**Архитектура программного обеспечения** (Software Architecture) - набор структур, необходимый для рассуждения о системе, содержащий элементы ПО, связи между ними и их свойства.

**Архитектор** — один из опытнейших программистов в команде, умеющий анализировать высокоуровневые проблемы и их решения.  

Каждый программист при написании кода вносит свой вклад в архитектуру приложения, и важно понимать, как это делать.

Важно понимать, что не существует универсальных решений для всех приложений: все они имеют свои сильные и слабые стороны. Подобрать хорошую архитектуру можно только при условии хорошего понимания требований приложения. Иногда не подходит ни одна существующая архитектура. Так появляются новые.

> 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. 
**Roy Fielding, 2000**

Понятия *архитектурный стиль*, *архитектурный паттерн* и *паттерн проектирования не взаимоисключающие* (not mutually exclusive), а *взаимодополняющие* (complementary). Они отличаются своей областью применения (scope).

## Архитектурный стиль

**Архитектурный стиль** (Architectural Style) в общих чертах указывает, как организовать код в проекте. Это наивысший уровень детализации (granularity), абстракции (abstraction), на котором определяются слои, модули высшего порядка приложения и их отношения, взаимодействия друг с другом.

Архитектурные стили не привязаны к конкретной реализации.

### Примеры архитектурных стилей
* Компонетный (component-based)
* Монолитный (monolithic)
* Многослойный (layered)
* Событийный (event-driven)
* Клиент-серверный (client-server)
* Сервис-ориентированный (service-oriented)

## Архитектурный паттерн

**Паттерн, шаблон** (Pattern) — типичное решение широко распространённой проблемы. Отличается от *алгоритма* тем, что не содержит чёткой последовательности действий, а описывает общую концепцию (идею) решения, реализации которой могут сильно отличаться.

Паттерны следует использовать только при необходимости, в противном случае они могут лишний раз усложнить код.

**Архитектурный паттерн** (Architectural Pattern) решает проблемы, связанные с архитектурными стилями.

### Примеры проблем, которые решают архитектурные паттерны
* Как много уровней (tiers) будет в наше клиент-серверной архитектуре? Как они будут взаимодействовать?
* Какие модули высшего уровня будут в нашей сервис-ориентированной архитектуре?

### Примеры архитектурных паттернов
* MVC (Model-View-Controller)
* EBI (Entity-Boundary-Interactor)
* Трёхуровневый (3-tier)

## Паттерн проектирования

**Паттерн проектирования** (Design Pattern) решает какую-то локальную проблему в проекте, затрагивая лишь конкретный блок кода, но не весь код проекта целиком.

### Примеры проблем и решающих их паттернов проектирования
* Нужно за чем-то проследить в приложении и отреагировать соотвествующем образом (например, показать уведомление при появлении пользователя в сети) — *поведенческий паттерн "Наблюдатель"*.
* Нужно написать простой интерфейс для работы со сложной системой, библиотекой, API — *структурный паттерн "Фасад"*.

## Принцип проектирования

**Принцип проектирования** (Design Principle) предоставляет высокоуровневые рекомендации (high level guidelines) для проектирования приложений. Они не предоставляют рекомендации по реализации (implementation guidelines) и не привязаны к какому-либо языку программирования.

Самыми популярными принципами проектирования являются принципы SOLID.

# Архитектурные стили

## Монолитная архитектура

**Монолитная архитектура** означает, что приложение — большой связанный модуль, все компоненты которого спроектированы для совместной работы, используя общую память и ресурсы.

## Микросервисная архитектура

Микросервисная архитектура означает, что приложение состоит из маленьких независимых приложений, использующих собственные ресурсы и развивающихся независимо друг от друга, которые потенциально могут быть размещены на разных машинах.

### Сага

**Сага** (Saga) — архитектурный паттерн для реализации транзакции, охватывающей (span) несколько сервисов. 

Внутри сервиса доступны ACID-транзакции, но между несколькими сервисами их использовать нельзя.

Saga — последовательность локальных транзакций. Каждый сервис в саге выполняет свою, локальную транзакцию и публикует событие. Другие сервисы прослушивают это событие и выполняют следующую локальную транзакцию. 

Если по какой-либо причине одна транзакция завершается неудачно, то нужно откатить (rollback) изменения предыдущих транзакций. В отличае от обыкновенных ACID-транзакций внутри сервисов, которые отменяются автоматически, в случае использования Saga каждая локальная транзакция делает commit и нужно явно (explicitly) отменить предыдущие действия. Каждая транзакция должна иметь компенсирующую (compensating) транзакцию, отменяющую её. В случае неудачи Saga поочерёдно выполняет компенсирующие транзакции.

<!-- Saga - транзакционный механизм сообщений (transactional messaging mechanism): сервисы взаимодействуют преимущественно обменом сообщений. -->

## Многослойная архитектура

**Разделение на слои** (layering) — обычная практика для организации блоков кода по их роли или обязанностям в системе.

# Архитектурные паттерны

## MVC (1979)
В 1970-ых обязанности не разделялись: смешивание HTML, CSS и работы с базой данных считалось нормальной практикой. С ростом таких приложений становилось понятно, что они очень запутанны и их невозможно поддерживать (тратится слишком много ресурсов), поэтому нередко разросшиеся приложения приходилось переписывать с нуля.

В 1979 появился архитектурный шаблон **MVC** (Model-View-Controller), попытавшийся разрешить проблему, продвигая идею **разделения ответственности** (separation of concerns, SoC) между UI и бизнес-логикой.

**Бизнес-логика** (business logic, domain logic) — часть приложения, задающая бизнес-правила, определяющие, как данные (состояние, state) предметной области (domain) приложения создаются, хранятся и изменяются.

![MVC](./assets/MVC.png)

MVC разделяет приложение на 3 концептуальные единицы
* **Модель** (Model) представляет бизнес-логику (данные и правила, методы работы с ними) приложения; не содержит информации, как отобразить данные.
* **Представление** (View) отвечает за пользовательский интерфейс и отображает часть данных из Model. Обычно это UI-компонента (кнопка, поле для ввода и прочее); то, что пользователь видит и с чем взаимодействует.  
* **Контроллер** (Controller) выступает координатором между View и Model (решает, какие Views показывать и с какими данными), переводит действия пользователя (например, клик по кнопке) в бизнес-логику.

Модель может быть представлена объектом или структурой объектов.

### Особенности MVC  
1) View использует объекты данных напрямую из Model, чтобы эти данные отобразить.  
2) Когда данные в Model меняются, срабатывает событие, немедленно обновляющее View.  
3) Один View обычно привязан к одному Controller.  
4) Каждый экран может иметь несколько пар View-Controller.  
5) Controller может быть связан с несколькими Views. 

### Проблема MVC в клиент-серверных приложениях

Когда MVC появился, протокола HTTP (1992) и клиент-серверных приложений ещё не было.  

Попытка наложить архитектуру MVC на современное клиент-серверное приложение приводит к множеству несоответствий (inconsistencies).

* View отвечает за пользовательский интерфейс, его можно отнести к клиенту.  
* Model обычно включет бизнес-логику приложения, такое лучше хранить на сервере в целях безопастности, но какая-то часть логики (например, валидация формы), может остаться на фронтенде, а соответственно и часть Model.  
* Controller реагирует на действия пользователя и манипулирует Model. Нет однозначного ответа о его расположении.  
* Изменения Модели не вызывают изменения во View напрямую: эта связь проходит через Controller. Это связано с тем, что клиент и сервер функционируют отдельно, как два разных приложения. Единственный способ исправить это — настроить веб-сокеты.

Во многих современных приложениях для того, чтобы разгрузить сервер (сократить количество запросов к нему), часть логики и состояния храяится на фронтенде. Например, взаимодействие React и Redux может можно представить в виде MVC.

## Иерархический MVC (2000), PAC (1987)

**HMVC** (Hierarchical MVC) увеличивает модульность в контексте виджетизации UI-блоков.

Существует мнение, что авторы HMVC переосмыслили другой паттерн: **PAC** (Presentation-Abstraction-Control).

HMVC разбивает уроверь клиента (client tier) на иерархию из MVC-слоёв. Это называется **проектированием клиентского уровня** (client-tier architecture) и должно увеличивать масштабируемость приложения: каждый MVC-слой независим от других и может работать при отсутствии любого другого.

![HMVC](./assets/HMVC.png)

Controller, обрабатывающий основной запрос, пересылает подзапросы другим Controllers, чтобы получить рендеринг виджетов и включить их в рендеринг основного View.

## MVP (1996)

*MVC* был хорош для приложений своего времени, но приложения выросли и настало время изменений.

**MVP** (Model-View-Presenter) видоизменяет MVC, разделяя View и Model и осуществляя их коммуникацию только через **Presenter**.  

Снимаем со View обязательство следить за обновлениями Model и наделяем таким обязательством Controller, переименовывая его в **Presenter**.

Если UI-логику нужно тестировать, то она размещается в Presenter, в противном случае — в View.

*Presenter* также называют **Supervisor Controller**.

![MVP](./assets/MVP.png)

В случае, когда два View зависят друг от друга (input — один View, кнопка — другой), нужно ViewModel одного View подписаться на событие, которые излучает другой ViewModel. 

### Особенности MVP 
1) View пассивен (passive) и ничего не знает о Model. 
2) Presenter не содержит бизнес-логики, он просто вызывает методы Model, а затем передаёт необходимые данные во View.  
3) Только один Presenter для каждого View.  
4) Изменение данных в Model не вызывает немедленное обновление View: событие всегда проходит через Presenter, что позволяет в нём перед обновлением View проделывать дополнительную логику, связанную с представлением.

### Зависимые Views

Пусть два Views зависят друг от друга.  
Например, первый View содержит поля для ввода (inputs), а второй View — кнопку.  
Кнопка недоступна (disabled) до тех пор, пока все поля не заполнятся.  

Если данные из полей записываются в Model, то Presenter, привязанный к полям, записывает данные в Model, а Presenter, привязанный к кнопке, читает данные из Model.  

Если данные не записываются в Model, то создаём оборачивающий Presenter, который реализует оба Presentors.

### Пример связи View и Presenter

```js
// View сообщает Presentor, что готов к обновлению данных 
const resume = () => {
  Presenter.load();
};
// View сообщает Presentor, что не хочет отображать что-либо ещё
const pause = () => {
   Presenter.stopLoad();
};
```

## MVVM (2005)
  
Сложность приложений продолжала расти и снова появилась необходимость изменений.

**MVVM** (Model-View-ViewModel) призван разделить UI и бизнес-логику таким образом, чтобы за них могли отвечать разные люди, использующие разные технологии для этих целей. 

Presenter производит (produce) данные, View потребляет (consume) их. Почему производитель должен беспокоиться о потребителе?

Таким образом, мы снимаем с Presenter обязательство изменять View. Вместо этого используем `Observable`: во View подписываемся на события (subscribe to events), излучаемые (emit) Presenter, который мы переименовываем во ViewModel.

Если UI-логику нужно тестировать, то она размещается в ViewModel, в противном случае — в View.

![MVVM](./assets/MVVM.png)

В случае, когда два View зависят друг от друга (input — один View, кнопка — другой), необходимо, чтобы ViewModel  View подписаться на событие, которые излучает другой ViewModel. 

### Особенности MVVM
1) Один ViewModel соответствует только одному View и наоборот.
2) Вся логика из View перемещается во ViewModel, чтобы упростить View и позволить ему выполнять свою задачу (визуализация).
3) Отношение один к одному установлено между данными во View и данными во ViewModel.
4) Изменение данных во ViewModel вызывает немедленное обновление View.

### Зависимые Views

Пусть два Views зависят друг от друга.  
Например, первый View содержит поля для ввода (inputs), а второй View — кнопку.  
Кнопка недоступна (disabled) до тех пор, пока все поля не заполнятся.  

Если данные из полей записываются в Model, то ViewModel, привязанный к полям, записывает данные в Model, а ViewModel, привязанный к кнопке, читает данные из Model.  

Если данные не записываются в Model, то создаём оборачивающий Presenter, который реализует оба Presentors.

Если данные не записываются в Model, то подписываем ViewModel, привязанный к кнопке, на событие, излучаемое ViewModel, привязанным к полям ввода.

### Пример связи View и Presenter

```js
// View сообщает Presentor, что готов к загрузке данных 
const resume = () => {
  ViewModel.subscribe(/* ... */);
};
// View сообщает Presentor, что не хочет отображать что-либо ещё
const pause = () => {
   ViewModel.unsubscribe(/* ... */);
};
```

## MVPVM

**Model** — набор классов, содержащий всю бизнес-логику и случаи использования.

**View** — шаблон (template), на основании которого геренируется HTML при помощи шаблонизатора (template engine).

**Необработанные данные** (raw data) — данные, которые не были обработаны для использования.

**ViewModel** (Presentation Model) получает необработанные данные из query и держит их для использования в шаблоне; инкапсулирует сложную логику представления для упрощения шаблона.

Почему важен ViewModel
* Изменения в Model могут всплыть и повлиять на ViewModel, но не на View.
* Сложная логика представления не просочится в предметную область (not leak into the domain), поскольку мы можем инкапсулировать эту логику во ViewModel.
* Зависимости View становятся явными, поскольку они должны быть установлены в ViewModel.

*View* и *ViewModel* имеют связь "один к одному".

**Presenter** получает HTTP-запрос (request), вызывает команду (command) или запрос (query), использует данные результата запроса (query), ViewModel, шаблон и шаблонизатор для генерации HTML и отправляет его клиенту в качестве HTTP-ответа (response).  

Все взаимодействия Views проходят через Presenter.

![MVPVM](./assets/MVPVM.png)

## EBI (1992)

Архитектурный шаблон **Entity-Boundary-Interactor** (EBI) был опубликован Иваром Якобсоном в одной из серии книг об объектно-ориентированной разработке ПО.

Изначально автор назвал архитектуру Entity-Interface-Control, но затем переименовал для понимания, что Interface не связан с одноимённой конструкцией в языках программирования, а Control не связан с Controller в MVC.

*Цель EBI*: обычно объектно-ориентированные методологии помещают все обязанности в одну сущность, без разделения, но автор книги считает, что разделение (инкапсуляция) обязанностей позволяет сделать систему более гибкой к изменениям, которые в таком случае становятся локальными (изменяется только один объект системы).
 
**Механизм доставки** (delivery mechanism) — часть приложения, которую клиент использует для выполнения приложения. Эта часть не должна определять логику приложения.

![EBI](./assets/EBI.png)

Схема работы в обозначениях, введённых автором книги.

![EBI](./assets/EBI_2.png)

### Entity

**Entities** содержат данные системы и всю логику, напрямую связанную с этими данными (такую логику, которая при внесении изменений в Entity тоже должна изменяться; меняется структура — меняются методы работы с ней). Эта логика не зависит от приложения.

*Entities* можно назвать бизнес-объектами приложения.

### Boundary

**Boundaries** содержат в себе функциональность, касающуюся интерфейса системы (системного окружения), то есть отвечают за связь системы с окружающим миром.

Любое *взаимодействие с системой* происходит через *Boundary*. Действующее лицо (actor) может быть не только человеком, но и другим устройсвом или сторонним API.

*Boundary принимает запрос* (request) и *выдаёт ответ* (response). Эти абстракции *реализуются* (implement) в *Interactor*.

### Interactor

**Interactors** содержат поведение, не вошедшее в Boundary и Entity (обычно это операции над несколькими Entity, результат  которых возвращается в Boundary; похоже на сервисы).

*Основная задача Interactor*: получить запрос от *Boundary*, манипулировать состоянием приложения (application state) через *Entities*, вернуть ответ *Boundary*. 

*Interactors* — слой *бизнес-логики приложения*.  

*Interactor* является конкретной реализацией (concrete implementation) *Boundary*.

*Антипаттерн*: Entity содержит лишь данные, всё поведение выносится в Interactor. 

*Interactors* важны, поскольку они содержат специфическую логику для конкретных *Boundaries*, в то время как *Entity* содержит общую (generic) логику для всех *Boundaries*. В случае, если вся логика будет храниться в *Entity*, некоторые *Boundaries* будут иметь доступ к тому, к чему не должны, что увеличивает сложность и может привести к ошибкам.

### Сравнение EBI и MVC

EBI стремится к тому, чтобы бизнес-логика приложения, представленная Entities и Interactors, была независима от механизма доставки. Эта логика не должна знать, что её использует. Это позволяет писать на любом языке для любого случая использования (use case). Требуется лишь умение определять абстракции на подобие интерфейсов. Причём статическая типизация здесь не нужна: нужен только способ создания чётких и проверяемых определений функциональности. Если необхомимой структуры нет, то дотаточно написания документации. Boundary должен быть уточнён (be specified), но не важно, каким образом.

MVC всегда зависит от механизма доставки.

View и Controller в MVC составляют весь уровень представления, Model включает в себя всё остальное в приложении, в том числе и бизнес-логику. Boundary в EBI — одно целостное соединение с окружающим миром, Entities и Interactors в EBI представляют всю бизнес-логику. Таким образом MVC и EBI не заменяют друг друга, но пересекаются. Совместное их использование породило бы паттерн View-Controller-Boundary-Interactor-Entity.

### Пример реализации EBI

```
api/
  article.ts
core/  
  entities/  
    article.ts
    entity.ts
  interactors/  
    article.ts  
services/  
  requests/  
    article.ts  
    request.ts  
  responses/  
    article.ts  
    response.ts  
  index.ts  
```

#### Entities
```ts
/* core/entities/entity.ts */
export interface Entity {
  id: string;
}

/* core/entities/article.ts */
import { Entity } from './entity.ts';

export interface Article extends Entity {
  title: string;
}

const validate = (request: Article) => {
  if (request.title.length === 0) {
    throw new Error('Article\'s title must not be empty!');
  }
  return true;
};
```

#### Interactors
```ts
/* core/interactors/article.ts */

export const createArticle (request: CreateArticleRequest): CreateArticleResponse => {
  
}

export const findArticle (request: FindArticleRequest): FindArticleResponse => {
  
}

```

#### Boundaries

```ts
/* services/article.ts */
export interface ArticleBoundary {
  createArticle(request: CreateArticleRequest): CreateArticleResponse;
  findArticle(request: FindArticleRequest): FindArticleResponse;
}
```

#### Модели запросов (Request Models)
```ts
/* services/requests/request.ts */
export interface Request {}

/* services/requests/article.ts */
import { Request } from './request.ts';

export interface FindArticleRequest extends Request {
  id: string;
}

export interface CreateArticleRequest extends Request {
  title: string;
}
```
#### Модели ответов (Response Models)
```ts
/* services/responses/response.ts */
interface Response {}

/* services/responses/article.ts */
import { Response } from './response.ts';

export interface FindArticleResponse extends Response {
  id: string;
  title: string;
}

export interface CreateArticleResponse extends Response {
  id: string;
  title: string;
}
```

## Трёхуровневая архитектура

**Трёхуровневая архитектура** (3-Tier) разбивает приложение на 3 логических уровня (слоя). 

Чаще всего используется для *клиент-серверных* приложений.

*Трёхуровневая архитектура* позволяет разрабатывать и размещать каждый уровень отдельно от остальных. Это делает приложение более масштабируемым, поскольку каждый уровень развивается и функционирует относительно независимо от других. Разбиение на уровни позволяет также нанимать узкоспециализированных специалистов (дизайнеры, верстальщики, бэкендеры, тестировщики бэкенда, DevOps), которые будут быстро и качественно выполнять свою часть работы.

![3-tier](./assets/3-tier.png)

### Уровни
* **Уровень представления** (Presentation Tier) содержит пользовательский интерфейс (UI). Соответствует фронтенд (HTML, JS, CSS, фреймворки и прочее).
* **Уровень приложения** (Application Tier) содержит бизнес-логику приложения. Соответствует бэкенду (Node.js, .NET, Java и прочее).
* **Уровень данных** (Data Tier) включает в себя базу данных или другую систему хранения данных, а также уровень доступа к данным (data access layer). Примеры: MySQL, MongoDB, DynamoDB и прочие.

*Уровни общаются* между собой при помощи *API-запросов*.

## DDD (2003)

Архитектуру **Domain-Driven Design** (DDD) — предметно-ориентированное проектирование — предложил Эрик Эванс.

Идея DDD: при разработке проекта основное внимание должно уделяться основной предметной области (domain) и её логике. Для этого нужно тесное сотрудничество между техническими специалистами и экспертами предметной области.

### Единый язык

**Единый, повсеместный, общий  язык** (Ubiquitous language) — общий язык между предметной областью и её технической реализацией. Источником этого языка должна быть предметная область (бизнес).  

Каждая предметная область имеет свои определения, часто встречающиеся в требованиях заказчика, но программисты часто переименовывают многие вещи по-своему, создавая таким образом свою собственную модель предметной области, придумывают свои понятия, которые потом нужно объяснять другим программистам. Так возникают барьеры недопонимания, чего нужно избегать.

Разработка единого языка требует много времени на тщательный анализ требований и консультации с экспертами предметной области, но это позволяет создать внутреннюю базу знаний клиента. 

### Слои в DDD
* **Пользовательский интерфейс** (User Interface) обеспечивает взаимодействие окружающего мира с приложением, преобразуя входные данные в команды приложению (аналогично Boundary в EBI).
* **Слой приложения** (Application Layer) управляет объектами предметной области (Domain Objects), чтобы выполнять приходящие команды — **Случаи использования** (the Use Cases). Не содержит бизнес-логику. На этом слое лежат *Сервисы приложения* (Application Services), являющиеся контейнерами, в которых используются такие объекты предметной области, как Репозитории, Domain-сервисы, Сущности, Value-объекты (аналогично Interactor в EBI, но не все "не Boundary, не Entity" объекты, а только соотвутствующие *Случаям использования*).
* **Слой предметной области** (Domain Layer) содержит всю бизнес-логику. (аналогично Entity в EBI).
* **Инфраструктура** (Infrastructure) содержит технические возможности, которые поддерживают слои выше (например, отправка сообщений).

### Объекты предметной области

**Контекст** (Context) — окружение рассматриваемого объекта, влияющее на его смысл.

**Предметная область** (Domain) — сфера знаний, к которой относится программа.

**Модель** (Model) — система абстракций, описывающая выбранные аспекты предметной области и использующаяся для решения её проблем.

**Сущность** (Entity) — объект, имеющий индивидуальные черты. Имеет уникальный идентификатор (id). Две Сущности считаются разными даже в случае совпадения всех свойств.

Пример Сущности: Несмотря на то, что в кинотеатре все места выглядят одинаково, каждое место имеет свой номер (уникальный идентификатор). Посетитель приходит и садится на место, указанное в его билете. 

**Value-объект** (Value Object) — неизменяемый (immutable) тип объекта, полностью определяемый значениями своих свойств. В отличие от Сущности не имеет уникального идентификатора (id). Два Value-объекта с одинаковыми свойствами считаются идентичными.

Пример Value-объекта: В общественном транспорте все места одинаковы: можно сесть куда угодно, нумерация не имеет значения. 

**Агрегат** (Aggregate) — коллекция объектов, связанных вместе корневой Сущностью — **корнем агрегата** (Aggregate Root). 

Корневой агрегат гарантирует согласованность изменений, вносимых в агрегат, не позволяя внешним объектам хранит ссылки на его элементы. 
Агрегаты можно рассматривать как ограниченный контекст, предоставляющий корневому объекту и всему графу объектов контекст, в котором они используются.

**Репозиторий** — объект, сохраняющий Сущности или Агрегаты в базовый механизм хранения или извлекающий ихиз него.  

Репозиторий является частью модели предметной области (domain model), поэтому он должен быть независим от поставщика (vendor) базы данных.

**Сервис** — объект, содержащий методы (команды), концептуально не привязанные к какому-либо объекту (Entity, Value-объекту). Не имеет состояния (stateless).

### Ограниченный контекст

В корпоративных (enterprise) системах, где работает огромное количество разработчиков, модель может сильно разрастить. Сложно держать всю структуру огромного проекта в голове. Сложно распределять обязанности, когда все работают в одном месте. В этом случае систему обычно разбивают на подсистемы. 

**Ограниченный контекст** (Bounded context) — подсистема в рамках DDD, определяющая контекст применения изолированной части модели.

Изолированность достигается разными способами (например, разделением схемы базы данных на части или разделением обязанностей между программистами по их умениям).  

Хорошее разделение: сильная функциональная связь внутри подсистем и слабая между подсистемами.

### Антикоррупционный слой

**Антикоррупционный слой** — промежуточный слой между двумя подсистемами, заменяющий их взаимодействие друг с другом на взаимодействие с собой; изолирующий подситемы друг от друга. В таком случае при удалении или замене одной из подсистем вторая останется неизменной (изменится только антикоррупционный слой).

Антикоррупционный слой можно использовать, когда одна или обе подсистемы не контролируются, или когда нужно предотвратить утечку из  модели одной системы в другую (изменяем одну подсистему под требования другой).

### Общее ядро

Иногда, несмотря на преимущества изолированности, имеет смысл использовать общий код между несколькими компонентами (чтобы не дублировать его). Компоненты остаются независимыми друг от друга, хотя и используют **общее ядро** (shared kernel). 

Так один компонент может прослушивать событие, запущенное другим копонентом. Другой пример: использование сервиса.

Изменять общее ядро нужно очень осторожно, поскольку оно может сломать сразу несколько компонент.

### Общая подобласть

**Подобласть** (subdomain) — хорошо изолированная часть предметной области (domain).

**Общая подобласть** (generic subdomain) — подобласть, которая не специфична конкретно для приложения, а может использоваться почти в любом другом. (например, рисование статистики, работа с камерой, платёжные методы).

Если в приложении есть общие подобласти, нужно уделять им как можно меньше внимания по сравнению с предметной область. Это не самая важная часть приложения, а если и важная, то не критичная (essential but not crucial). 

Общие подобласти лучше не писать, а устанавливать как сторонние пакеты.

## Шестиугольная архитектура, Порты и Адаптеры (2005)

**Архитектура "Порты и Адаптеры"** (Ports & Adapters Architecture), **Шестиугольная архитектура**  (Hexagonal Architecture) была предложена Алистером Кокбёрном.

*Цель Шестиугольной архитектуры*: позволить приложению одинаково управляться пользователями, программами, автоматизированными тестовыми сценариями, а также разрабатываться и тестироваться изолированно от возможных устройств и баз данных.

Основная идея заключается в том, чтобы оградить наше приложение (центральный блок системы) от всего ввода и вывода при помощи порта, ограничивающего приложение от внешних инструментов и технологий. Таким образом приложение не знает, что именно отправило ему входные данные и что получило выходные данные из него. Это позволяет обеспечить некоторую защиту приложения от изменений внешних технологий (например, замены технологии) и бизнес-требований.

### Проблемы традиционного подхода
Проблема на фронтенде: происходит утечка бизнес-логики в UI (например, логика для конкретного случая использования попадает во View или Contoller, что не может переиспользоваться в других UI-компонентах).

Проблема на  бэкенде: происходит утечка внешних библиотек и технологий в бизнес-логику, потому что мы часто напрямую ссылаемся на эти технологии, используем их типы, создаём подклассы и экземпляры классов библиотек в нашей бизнес-логике.

### Шестиугольник

Многослойные архитектуры по типу EBI, DDD показывают преимущества использования слоёв в архитектуре.

Автор подхода осознал, что у всех многослойных архитектур есть что-то общее, их можно упростить следующим образом:  
Представление (Presentation) — Бизнес-логика (Business logic) — Данные (Data).

Также Алистер заметил, что Представление и Данные являются по сути точками входа/выхода приложения, то есть в приложении есть симметрия.

Представим схему со слоями не сверху вниз, а слева направо и разделим на две симметричные части, каждая из которых будет содержать несколько точек входа/выхода, а в центре приложение. Например, левая содержит точки UI и API, а правая — точки ODM, поисковой движок и сервис отправки SMS-сообщений.

Чтобы показать, что приложение имеет много точек входа/входа, приложение представляется в виде многоугольника (одна сторона — одна точка). Несмотря на то, что сторон может быть сколько угодно, архитектуру назвали шестиугольной (hexagonal).

### Порт и Адаптер

**Порт** (Port) — независимая точка входа в приложение и (или) точка выхода из него. Во многих языках это называют *интерфейсом*. Мы используем этот интерфейс как точку входа/выхода, не зная конкретной реализации.

**Адаптер** (Adapter) — класс, адаптирующий (adapt) один интерфейс к другому. 

**Первичный, управляющий адаптер** (Primary, Driving Adapter) — адаптер левой стороны схемы, отвечающей за UI и API. Запускает какое-то действие в приложении. 

*Первичный адаптер* зависит от порта и содержит конкретную реализацию порта, содержащую случай использования (the Use Case). Порт и его реализация (случай использования) принадлежат приложению.

**Вторичный, управляемый адаптер** (Secondary, Driven Adapter) — адаптер правой стороны схемы, отвечащей за подключение бэкенд-технологий. Реагирует на действие первичного адаптера.

*Вторичный адаптер* — конкретная реализация порта, внедряющаяся в бизнес-логику приложения (хотя бизнес-логика  знает только об интерфейсе). Порт принадлежит приложению, а его конкретная реализация лежит снаружи и оборачивает какой-то внешний инструмент.

### Пример использования Вторичных Адаптеров

Нужно подключить Mongoose ODM к нашему приложению, чтобы связать его с базой данных MongoDB.  

При ипользования традиционного подхода, мы напрямую используем возможности Mongoose.
```js
const mongoose = require('mongoose');

const User = mongoose.model('User', { name: String });

const create = (name) => {
  const user = new User({ name });
  return user.save();
};
```
Через месяц заказчик передумал: теперь он хочет реляционную базу данных.  
Нужно заменить Mongoose ODM на Sequelize ORM.
```js
const { Sequelize, Model, DataTypes } = require('sequelize');
const sequelize = new Sequelize(/* ... */);

class User extends Model {}
User.init({ name: DataTypes.STRING }, { sequelize, modelName: 'user' });

const create = name => User.create({ name });
```
Такая миграция кода достаточно болезненна.

В случае использования Портов и Адаптеров, мы создаём интерфейс (порт), подсказывающий, что должно быть реализовано.
```ts
interface IUser {
  name: string!
}

/* порт */
interface IUserRepository {
  create: (name: string) => IUser
}
```
Создаём Адаптер `UserMongoAdapter`, в котором и будет хранится вся логика работы с Mongoose, включающая реализацию порта.

Теперь, когда поступает задание о смене базы данных, мы просто создаём ещё один Адаптер `UserSequelizeAdapter` с другой реализацией порта. 

Теперь мы можем достаточно просто заменять один адаптер другим.  
Если мы хотим делать это в режиме реального времени (runtime), то следует использовать паттерн "Фабрика".

Тестирование также упрощается. Создаются Макет (Mock) или Зуглушка (Stub) на основании интерфейса (порта) и приложение тестируется без взаимодействия с реальной базой данных.

### Пример использования Первичных Адаптеров

Пусть у нас есть два UI: основной сайт для пользователей и инструменты модераторов.  
Хотим сделать функциональность отправки электронных сообщений, доступный в обоих UI.

Используя Порты и Адаптеры, мы должны создать интерфейс (порт) предполагаемого сервиса `EmailService` и реализовать его в приложении.
```ts
interface IEmailService {
  sendEmail: (receiver: string, message: string) => void
}
```
Метод `sendEmail` является Случаем использования (Use Case).

Каждый UI имеет свой Controller (или команду консоли), использующий интерфейс, чтобы вызывать реализованные в сервисе методы. В данном случае Controller и есть Адаптер (адаптер находится на фронтенде, а порт и его реализация на бэкенде).

Теперь мы можем быть уверены, что UI не затронет бизнес-логику.

Тестирование такогоподхода тоже имеет свои преимущества. Мы тестируем UI отдельно от приложения и тестируем случаи использования отдельно от UI.

## Луковая архитектура (2008)

**Луковая архитектура** (Onion Architecture) предложена Джефри Палермо.

Луковая архитектура имеет схожую идею с Шестиугольной: использовать что-то похожее на Адаптер во избежание утечки инфраструктуры (сторонние библиотеки и API) в бизнес-логику. Но, помимо предложенного в Шестиугольной архитектуре разделения на внутренний (internal) слой с бизнес-логикой и внешний (external) слой с инфраструктурой, добавляются дополнительные слои, напоминающие слои в DDD.

*Слои* предтавляются в *форме луковицы*.  
*Внешние слои зависят* от *внутренних*, *внутренние* ничего *не знают* о *внешних*.  
Это позволяет нам изменять внешние слои, не затрагивая внутренние, что напоминает принцип Dependency Inversion из SOLID, но на архитектурном уровне.

Более того, любой внешний слой может напрямую обращаться к любому внутреннему.  
Это позволяет избежать создания вспомогательных прокси-методов и классов, усложняющих код.

![Onion Architecture](./assets/Onion.png)

### Ключевые принципы Луковой архитектуры
* Приложение построено вокруг независимой объектной модели (в центре луковицы располагается модель предметной области, которая ни от чего не зависит).
* Внутренние слои определяют интерфейсы, внешние слои их реализовывают.  
* Направление связанности идёт к центру (direction of coupling is toward the center).
* Ядро приложения (Application Core) может быть запущено отдельно от инфраструктуры.

## Кричащая архитектура (2011)

**Кричащая архитектура** (Screaming Architecture) предложена Робертом С. Мартином.  

Идея Кричащей архитектуры: Архитектура должна чётко показывать, в чём заключается суть системы.   

Первые папки в проекте должны быть связаны с предметной областью, верхним уровнем абстракции (Users, Admins, Messages), но не с названием технологий (mongodb, jwt, redis), не с функциональными блоками (services, controllers, repositories), не с механизмами доставки (http, консоль).

## Чистая архитектура (2012)

**Чистая архитектура** (Clean Architecture) предложена Робертом С. Мартином (дядей Бобом).

*Чистая архитектура* объединяет в себе многие известные идеи, стили и паттерны чтобы показать, как они могут работать вместе.

*Цель Чистой архитектуры* та же: независимость от внешних инструментов и механизмов доставки.

### Слои в Чистой архитектуре
Шестиугольная архитектура имеет только два слоя: внешний и внутренний.

Луковая архитектура: Сервисы приложения (Application services), Сервисы предметной области (Domain Services), Модель предметной области (Domain Model) — основная часть приложения (Application Core), пользовательский интерфейс и инфраструктура — внешняя часть.

Чистая архитектура: Сервисы приложения, Слой сущностей (Entities Layer) — основная часть приложения. 

Таким образом, Роберт Мартин объединил два слоя в один для простоты, переопределив понятие сущности.

**Сущность** (Entity) — объект с методами или набор структур данных и функций.

### Поток управления

1. HTTP-запрос поступает в Controller.  
2. Создаётся модель запроса (Request Model).  
3. Выполняется метод в Interactor, передавая ему модель запроса.  
4. Interactor использует реализацию Entity Gateway, чтобы найти необходимые сущности; оперирует ими; генерирует из результата модель ответа (Response Model); передаёт модель ответа в Presenter и возвращает его в Controller.  
5. Используется Presenter, чтобы сгенерировать ViewModel.  
6. ViewModel привязывается ко View.  
7. View возвращается клиенту.  

# Паттерны проектирования

## Порождающие

**Порождающие паттерны** отвечают за удобное и безопасное создание новых объектов или групп объектов.

- [Одиночка (Singleton)](#одиночка)
- [Внедрение зависимостей (Dependency Injection)](#внедрение-зависимостей)
- [Фабричный метод (Factory Method)](#фабричный-метод)
- [Абстрактная фабрика (Abstract Factory)](#абстрактная-фабрика)
- [Строитель (Builder)](#строитель)
- [Прототип (Prototype)](#прототип)
### Одиночка

**Одиночка** (Singleton) — порождающий паттерн, гарантирующий существование только одного объекта определённого класса. Одиночка позволяет обратиться к этому объекту из любого места в приложении.

Часто считается антипаттерном, поскольку нарушает модульность кода (похож на глобальную переменную).

Для реализации одиночки пишется класс с приватным конструктором; стратическим полем, хранящим экземпляр (instance) класса, и статическим методом, создающим и сохраняющим экземпляр при первом обращении и возвращает при последующих.
```ts
class Singleton {
  private static instance: Singleton;
  private constructor() {}
  static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
}

const singletonA = Singleton.getInstance();
const singletonB = Singleton.getInstance();
console.log(singletonA === singletonB); // true
```

### Внедрение зависимостей

**Внедрение зависимостей** (Dependency Injection) — порождающий паттерн, реализующий *принцип инверсии управления* (IoC). 

*Внедрение зависимостей* позволяет полностью вынести создание объектов, от которых зависит некоторый класс, за пределы этого класса, предоставляя эти объекты другим путём.

*Внедрение зависимостей* вводит следующие 3 понятия
* **Клиент** — класс, зависимый (dependent) от *Сервиса*.
* **Сервис** — класс, предоставляющий свой объект *Клиенту*.
* **Инжектор** — класс, внедряющий объект *Сервиса* в *Клиент*.

Возьмём пример из раздела с *принципом инверсии управления* и улучшим его при помощи *принципа инверсии зависимостей* (DIP), заменив классы на интерфейсы, где это возможно.
```ts
interface ISalary {
  value: number;
  currency: string;
}

interface IEmployee {
  salary: ISalary;
}

const SalaryFactory = (value: number, currency: string): ISalary => new Salary(value, currency);

class Employee implements IEmployee {
  salary: ISalary;

  constructor() {
    this.salary = SalaryFactory(500, '$');
  }

  getSalary() {
    return this.salary.toString();
  }
}

class Salary implements ISalary {
  value: number;
  currency: string;

  constructor(value: number, currency: string) {
    this.value = value;
    this.currency = currency;
  }

  toString() {
    return `${this.value}${this.currency}`;
  }
}
```
Паттерн "Фабрика" отвечает за создание объекта класса `Salary`, но само создание (пусть и при помощи фабрики) всё ещё происходит в классе `Employee`.

В данном примере класс `Employee` — *Клиент*, `Salary` — *Сервис*.  
Рассмотрим 3 способа, как можно написать *Инжектор*, который назовём `EmployeeInjector`:
* *Внедрение в конструктор*
* *Внедрение свойства*
* *Внедрение метода*

**Внедрение в конструктор** (Constructor Injection): зависимость предоставляется через параметры конструктора (`constructor(salary:  ISalary)`).
```ts
class Employee implements IEmployee {
  salary: ISalary;

  constructor(salary: ISalary) {
    this.salary = salary;
  }
}
```
Инжектор в этом случае:
```ts
class EmployeeInjector {
  emp: IEmployee;

  constructor() {
    const salary = new Salary(500, '$');
    this.emp = new Employee(salary);
  }
}
```

**Внедрение свойства** (Property Injection): зависимость предоставляется через публичное свойство (`emp.salary`).
```ts
interface IEmployee {
  salary?: ISalary;
}

class Employee implements IEmployee {
  salary?: ISalary;

  constructor() {}
}
```
Инжектор в этом случае:
```ts
class EmployeeInjector {
  emp: IEmployee;

  constructor() {
    this.emp = new Employee();
    this.emp.salary = new Salary(500, '$');
  }
}
```
Поскольку начальное значение `Salary` не задано в примере, пришлось сделать его необязательным (`?:`) в интерфейсе `IEmployee` и классе `Employee` во избежание ошибок инициализации. Задать начальное значение *внедрением свойства* нельзя. 

**Внедрение метода** (Method Injection): зависимость предоставляется через метод интерфейса или класса (`setSalary(salary)`).
```ts
/* определяем метод интерфейса, принимающий salary */
interface IEmployeeMethods {
  setSalary: (salary: ISalary) => void;
}

/* реализуем метод интерфейса в классе */
class Employee implements IEmployeeMethods {
  salary?: ISalary;

  constructor() {}

  setSalary(salary: ISalary): void {}
}
```
Инжектор в этом случае:
```ts
class EmployeeInjector {
  emp: IEmployee;

  constructor() {
    this.emp = new Employee();
    const salary = new Salary(500, '$');
    /* приводим this.emp к типу интерфейса, чтобы вызвать метод */
    (<IEmployeeMethods>this.emp).setSalary(salary);
  }
}
```

*Внедрение зависимости* на примере *модулей*:  
Пусть есть два модуля A и B, модуль B использует модуль A.  
Простой случай включения модуля:
```js
/* B.js */
const A = require('A');

module.exports = {
  /* работа с модулем A */
};
```
*Внедрение зависимости*:
```js
/* B.js */

module.exports = (A) => {
  /* работа с модулем A */
};
```
```js
/* C.js */
const A = require('A');
const B = require('B');

module.exports = {
  B: B(A);
};
```

**IoC-контейнер, DI-контейнер** — *фреймворк*, реализующий *автоматическое внедрение зависимостей*. Он управляет созданием объектов и их временем жизни, а также внедряет зависимости в класс.

*IoC-контейнер* создаёт объект какого-то класса и встраивает все объекты, от которых зависит класс, в конструктор, свойство или метод класса во время выполнения (at run-time) и утилизирует (dispose), когда это необходимо.

*IoC-контейнер* предоставляет следующий жизненный цикл внедряемых зависимостей (DI lifecycle):
* Регистрация (Register)
* Разрешение (Resolve)
* Ликвидация (Dispose)

Пример использования *IoC-контейнера* в *NodeJS-приложении*.
```js
const awilix = require('awilix');

const container = awilix.createContainer({
  injectionMode: awilix.InjectionMode.PROXY
});

const createUserRepository = ({ db }) => ({
  getUserById(id) { return db.query(/* ... */) }
});

function Database(connectionString) { /* ... */ }

/* регистрация */
container.register({
  connectionString: awilix.asValue('/* ... */'),
  userRepository: awilix.asFunction(createUserRepository),
  db: awilix.asClass(Database),
});

/* разрешение и использование */
container.resolve('userRepository').getUserById('1');

/* ликвидация контейнера */
container.dispose();
```

### Фабричный метод

**Фабричный метод** (Factory Method) — порождающий паттерн, определяющий интерфейс для создания объекта некоторого класса, при этом решение о том, какой класс будет у объекта, принимается в дочернем классах.

Связываем объекты разных классов в одно семейство, характеризующее их общие черты.

Лебеди и утки являются летающими птицами. Пусть их классы `Dove` и `Duck` реализуют общий интерфейс `IFlyingBird`. 
```ts
interface IFlyingBird { /* ... */ }
class Dove implements IFlyingBird { /* ... */ }
class Duck implements IFlyingBird { /* ... */ }
```
Описываем абстрактный фабричный метод, создающий летающий птиц, и реализуем его в конкретных классах (фабриках).
```ts
interface IFlyingBirdFactory {
  create(): IFlyingBird /* фабричный метод */
}

class DoveFactory implements IFlyingBirdFactory {
  create(): IFlyingBird {
    return new Dove();
  }
}

class DuckFactory implements IFlyingBirdFactory {
  create(): IFlyingBird {
    return new Duck();
  }
}

const doveFactory = new DoveFactory();
const dove = DoveFactory.create();

const duckFactory = new DuckFactory();
const duck = DuckFactory.create();

const flyingBirds: IFlyingBird[] = [dove, duck];
```
Использовать фабрики очень удобно, поскольку мы избегаем использования оператора `new` напрямую, инкапсулируя эту логику внутри метода.
Это позволяет также инкапсулировать внутри метода некоторые общие свойства для какой-то подгруппы объектов, не дублируя при этом код и не создавая новый класс для них (не считая самой фабрики).
```ts
class BlackDoveFactory implements IFlyingBirdFactory {
  create(): IFlyingBird {
    return new Dove({ color: 'black', size: 'large' });
  }
}
```

Сделать что-то похожее можно при помощи функций, но это уже нельзя назвать фабричным методом.
```ts
const createDove = (): Dove => new Dove();
const createWhiteDove = (): Dove => new Dove({ color: 'white', gender: 'female' });
const createBlackDove = (): Dove => new Dove({ color: 'black', gender: 'male' });

/* создаём 5 чёрных лебедей */
const doves: Dove[] = [...new Array(5)].map(createBlackDove);
/* добавляем к ним одного белого */
doves.push(createWhiteDove());
```

### Абстрактная фабрика

**Абстрактная фабрика** (Abstract Factory) — порождающий паттерн, позволяющий создавать семейство концептуально связанных объектов, не привязываясь к классам этих объектов.

Интерфейс `IFlyingBirdFactory` из примеров выше по сути описывает абстрактную фабрику с одним абстрактным методом. Рассмотрим случай, когда абстрактных методов несколько.

Пусть у нас есть люди, собаки и кошки. Выделим кое-что, что объединяет их всех — разделение пола на мужской и женский.
```ts
interface ICreatureFactory {
  createMale(): Male {}
  createFemale(): Female {}
}

class HumanFactory implements ICreatureFactory {
  createMale(): Male {
    return new HumanFemale();
  }
  
  createFemale(): Female {
    return new HumanFemale();
  }
}
```

Пусть у нас есть набор инструментов: топор, кирка, лопата. Инструменты могут состоять из разных материалов, поэтому каждый из них должен быть абстрактным (чтобы нельзя было создать сущность без материала, то есть представлен либо абстрактным классом (если содержит реализации методов), либо интерфейсом.
```ts
abstract class Axe { /* ... */ }
abstract class Pickaxe { /* ... */ }
abstract class Shovel { /* ... */ }
```
Пусть материалами будут камень и железо.
```ts
class StoneAxe extends Axe { /* ... */ }
class StonePickaxe extends Pickaxe { /* ... */ }
class StoneShovel extends Shovel { /* ... */ }
class IronAxe extends Axe { /* ... */ }
class IronPickaxe extends Pickaxe { /* ... */ }
class IronShovel extends Shovel { /* ... */ }
```
Создаём абстрактную фабрику, с помощью которой мы сможем создавать инструменты из нужного материала.
```ts
interface IToolFactory {
  createAxe(): Axe {}
  createPickaxe(): Pickaxe {}
  createShovel(): Shovel {}
}
```
Создаём фабрики, которые её реализуют.
```ts
class StoneToolFactory implements IToolFactory {
  createAxe(): Axe {
    return new StoneAxe();
  }
  createPickaxe(): Pickaxe {
    return new StonePickaxe();
  }
  createShovel(): Shovel {
    return new StoneShovel();
  }
}

class IronToolFactory implements IToolFactory {
  createAxe(): Axe {
    return new IronAxe();
  }
  createPickaxe(): Pickaxe {
    return new IronPickaxe();
  }
  createShovel(): Shovel {
    return new IronShovel();
  }
}

const toolFactory = new StoneToolFactory();

const stoneTools = [
  toolFactory.createAxe(),
  toolFactory.createPickaxe(),
  toolFactory.createShovel(),
];
```
Такой подход очень расширяемый. Если в будущем мы захотим добавить новый материал для набора инструментов (например, сталь или золото), можно будет это очень просто реализовать созданием новой фабрики.

### Строитель

**Строитель** (Builder) — порождающий паттерн, предоставляющий возможность поэтапного (пошагового) создания составных объектов.

Будем делать пиццу.  
Пусть пицца может быть разных размеров, на разном тесте и с разными ингредиентами.
```ts
enum Size { Small = 'Small', Medium = 'Medium', Large = 'Large' }
enum DoughType { Thin = 'Thin', Thick = 'Thick' }

enum CheeseType { Mozzarella = 'Mozzarella', Parmesan = 'Parmesan', Gouda = 'Gouda' , Cheddar = 'Cheddar' }
enum HamType { Chicken = 'Chicken', Pork = 'Pork', Turkey = 'Turkey' }
enum MushroomsType { Chanterelles = 'Chanterelles', Champignons = 'Champignons' }
enum SauceType { BBQ = 'BBQ', Ketchup = 'Ketchup', Ranch = 'Ranch', Garlic = 'Garlic' }
enum VegetableType { Tomato = 'Tomato', Pepper = 'Pepper' }

type Ingredient = CheeseType | HamType | MushroomsType | SauceType | VegetableType;

class Pizza {
  size: Size;
  dough: DoughType;
  ingredients: Ingredient[];
  constructor(size: Size, dough: DoughType) {
    this.size = size;
    this.dough = dough;
    this.ingredients = [];
  }
  addIngredient(ingredient: Ingredient): void {
    this.ingredients.push(ingredient);
  }
}
```
Описываем *абстрактного строителя* `PizzaBuilder`, который может создать любую пиццу.  
Его наследники будут переопределять метод `build`, в котором определяется последовательность добавления ингредиентов.
```ts
abstract class PizzaBuilder {
  private pizza: Pizza;
  constructor(size: Size, dough: DoughType) {
    this.pizza = new Pizza(size, dough);
  }
  addCheese(cheese: CheeseType): void {
    this.pizza.addIngredient(cheese);
  };
  addHam(ham: HamType): void {
    this.pizza.addIngredient(ham);
  };
  addMushrooms(mushrooms: MushroomsType): void {
    this.pizza.addIngredient(mushrooms);
  };
  addSauce(sauce: SauceType): void {
    this.pizza.addIngredient(sauce);
  };
  addVegetable(vegetable: VegetableType): void {
    this.pizza.addIngredient(vegetable);
  };
  abstract build(): void
  async cook(time: number): Promise<Pizza> {
    this.build();
    await new Promise(resolve => setTimeout(resolve, time));
    return this.pizza;
  }
}
```
Описываем *конкретного строителя* `MargheritaPizzaBuilder`, который может создать пиццу "Маргариту" (тонкое тесто, томаты, мацарелла, томатная паста).
```ts
class MargheritaPizzaBuilder extends PizzaBuilder {
  constructor(size: Size) {
    super(size, DoughType.Thin);
  }
  build(): void {
    this.addVegetable(VegetableType.Tomato);
    this.addCheese(CheeseType.Mozzarella);
    this.addSauce(SauceType.Ketchup);
  }
}
```
Описываем *директора*, который знает, как работать со строителем, чтобы получить готовую пиццу (нужно собрать пиццу и готовить её в течение 3 секунд).
```ts
class Director {
  async cookMargherita(size: Size): Promise<Pizza> {
    const margheritaBuilder = new MargheritaPizzaBuilder(size);
    margheritaBuilder.build();
    const pizza = await margheritaBuilder.cook(3000);
    console.log('Margherita is ready!');
    return pizza;
  }
}

const director = new Director();
director.cookMargherita(Size.Medium).then(pizza => console.log(pizza));
```

Для полноты картины рассмотрим ещё один небольшой пример: построение ландшафта. Здесь `add`-методы будут реализованы не в родительском абстрактном строителе, а в конкретном дочернем.
```ts
class Landscape { /* ... */ }

abstract class LandscapeBuilder {
  private landscape: Landscape;
  constructor() {
    this.landscape = new Landscape();
  }
  abstract addTree(): void;
  abstract addBush(): void;
  abstract addRiver(): void;
  render(): Landscape {
    return this.landscape;
  }
}

class ForestLandscapeBuilder extends LandscapeBuilder {
  addTree(): void { /* ... */ };
  addBush(): void { /* ... */ };
  addRiver(): void { /* ... */ };
}

class ForestDirector {
  buildForest() {
    const builder = new ForestLandscapeBuilder();
    builder.addBush();
    builder.addTree();
    builder.addTree();
    return builder.render(); 
  }
}

const director = new ForestDirector();
director.buildForest();
```

### Прототип

**Прототип** (Prototype) — порождающий паттерн, позволящий копировать объект любой сложности без привязки к его конкретному классу.

```ts
interface Prototype<T> {
  clone(): T
}

class Article implements Prototype<Article> {
  title: string;
  date: Date;
  constructor(title: string) {
    this.title = title;
    this.date = new Date();
  }
  clone(): this {
    return Object.assign({}, this);
  }
}

const article = new Article('Prototype Pattern');
const copy = article.clone();
console.log(article === copy); // false
```
Есть [много способов клонирования объектов](https://github.com/Max-Starling/Notes/blob/master/JavaScript.md#клонирование-объектов) реализовать функцию `clone()`. Каждый из них имеет свои преимущества и недостатки по сравнению с остальными.  

## Структурные

## Поведенческие

# Принципы проектирования

## Поддерживаемый код

Наличие **поддерживаемого кода** (maintainable code base) означает возможность применения максимума концептуальных изменений с минимальным изменением кода, то есть изменение одного блока кода должно оказывать минимально возможное влияние на другие блоки. В таком случае изменения делаются проще и быстрее, а баги появляются реже.

**Связанность, зацепление** (coupling) отражает зависимость блоков кода друг от друга. Два блока кода **сильно связанны** (highly coupled), если изменения в одном блоке порождают изменения в другом, **слабо связанны** (loosely coupled), если один блок не зависит или почти не зависит от изменений другого. 

**Сплочёность, связность** (cohesion) отражает, насколько тесно связаны по смыслу функции одного модуля. **Низкая сплоченность** (low cohesion) характерна модулям, имеющим разные несвязнные обязанности, **высокая сплочёность** (high cohesion) — модулям, функции которых выполняют во многом похожие задачи.

### Основные принципы поддерживаемого кода
1) *Инкапсуляция* (encapsulation). Процесс сокрытия внутренних деталей реализации.  
2) *Слабая связанность* (low coupling). Достигается за счёт выбора правильного интерфейса.  
3) *Высокая сплочёность* (high cohesion).

Слово "**функциональный** (functional; блок кода, метод, класс и тд) отражает чисто техническую роль в приложении, оно не связано с предметной областью. Примеры функционального: слой (layer), фабрика (factory), View.

Слово "**концептуальный**" (conceptual; блок кода, метод, класс и тд) отражает бизнес-роль в приложении, оно напрямую связано с предметной областью (domain). Примеры концептуального: User, Article, Comment, Like.

Одна и та же вещь в зависимости от того, с какой стороны на неё смотреть, может быть функциональной и концептуальной одновременно.

**Пакет** — сгруппированный набор классов.  
**Модуль** — функциональный пакет.  
**Компонента** — концептуальный пакет.

Описанные принципы написания поддерживаемого кода часто применяются к отдельным классам, но также и к *пакетам* (*модулям*, *компонентам*). Это позволяет разрабатывать их максимально независимо друг от друга, разделяя обязанности между разными командами разработчиков. Выполнение принципов концептуально изолирует код, который в этом случае хорошо читается, легко тестируется и переиспользуется.

В *хорошо организованном проекте* (well-organised codebase) есть только одно конкретное место, где может лежать какой-то блок кода. 
Можно не знать точное местоположение, но есть только один логический путь, ведущий туда. Это позволяет избежать несогласованности данных, потерянного и дублированного кода, экономит время и нервы разработчиков.

## Принципы пакетов

Группируя классы в пакеты по каким-то критериям (концептуально), мы можем рассуждать об организации проекта на высшем уровне абстракции, управляя отношениями между этими пакетами.

### Принципы сплочёности пакетов (cohesion)
* **Reuse-Release Equivalency (RRE)**. Пакеты должны выпускаться и версионироваться отдельно друг от друга, чтобы всегда можно было выбрать старую версию пакета, если новая не корректно работает.
* **Common-Reuse Principle (CRP)**. Классы в пакете переиспользуются (reuse) вместе. Переиспользуешь один — переиспользуешь все.
* **Common-Closure Principle (CCP)**. Классы, изменяющиеся вместе, должны быть упакованы (packeged) вместе. Чтобы в случае изменений изменялся только один пакет, не затрагивая другие.

### Принципы связанности пакетов (coupling)
* **Acyclic-Dependencies Principle (ADP)**. Циклы в графе пакетных зависимостей недопустимы.
* **Stable-Dependencies Principle (SDP)**. Зависимости в направлении стабильности. Часто меняющиеся части системы должны зависеть от редко меняющихся (стабильных), но не наоборот. 
* **Stable-Abstractions Principle (SAP)**. Стабильные пакеты должны быть более абстрактными и содержать те классы и модули, которые можно расширять, а не те, которые должны изменяться. При изменении классов в пакете велика вероятность изменений их использования, а значит и изменений в зависимых пакетах.

## Принцип разделения ответственности (SoC)

**Принцип разделения ответственности** (Separation of Concerns, SoC) предполагает разделение приложения на функциональные блоки, как можно меньше перекрывающие функции друг друга.

Объектно-ориентированные языки разделяют ответственность между объектами, процедурные языки — между процедурами и функциями, микросервисная архитектура — между сервисами. Многие архитектуры стремятся отделить представление (Presentation) от бизнес-логики (Business Logic) приложения, некоторые архитектуры дополнительно разделяют бизнес-логику на предметную область (Domain) и на её конкретную реализацию. OOCSS разделяет структурные свойства (Structure) и свойства оформления (Skin). В сетевой модели OSI протокол HTTP отвечает за взаимодействие с пользователем — прикладной уровень (Application layer), за саму передачу данных отвечает протокол TCP — транспортный уровень (Transport  layer). 

## Принцип инверсии управления (IoC)

**Принцип инверсии управления** (Inversion of Control, IoC) предполагает передачу, дилегирование управления из одного места в другое, что позволяет достичь слабой связанности (loose coupling) кода.

Пример из жизни: человек может приготовить еду сам, а может просто заказать её, тогда ему не нужно заботиться о процессе приготовления пищи.  

Путь есть два класса: `Employee`, отвечающий за рабочих, и `Salary`, отвечающий за заработную плату. 
Пусть также каждый новый рабочий по умолчанию получает `500$`.
```ts
class Employee {
  salary: Salary;

  constructor() {
    this.salary = new Salary(500, '$');
  }

  getSalary() {
    return this.salary.toString();
  }
}

class Salary {
  value: number;
  currency: string;

  constructor(value: number, currency: string) {
    this.value = value;
    this.currency = currency;
  }

  toString() {
    return `${this.value}${this.currency}`;
  }
}

const emp: Employee = new Employee();
console.log(emp.getSalary());
```
Класс `Employee` напрямую создаёт объект класса `Salary` в своём конструкторе, что делает их сильно связанными. Изменения в `Salary` влекут за собой непосредственные изменения в `Employee`.

Более того, мы не можем изолированно протестировать класс `Employee`, поскольку не можем заменить создание объекта класса `Salary` заглушкой (stub) или заменить класс `Salary` макетом (mock).

Сделаем фабрику, которая будет создавать Salary-объекты.
```ts
const SalaryFactory = (value: number, currency: string): Salary => new Salary(value, currency);
```
и заменим строку `this.salary = new Salary(500, '$')` на `this.salary = SalaryFactory(500, '$')`.

Результат выполнения кода тот же, но мы ослабили связь между классами `Employee` и `Salary`, передав контроль над созданием объектов фабрике. Теперь, если класс `Salary` изменится, мы сможем оградить `Employee` от изменений, внеся изменения только в фабрику.

Например, конструктор класса `Salary` должен теперь принимать один параметр вместо двух: `new Salary('500$')`, тогда фабрика изменится следующим образом
```ts
const SalaryFactory = (value: number, currency: string): Salary => new Salary(`${value}${currency}`);
```

<!-- Также мы теперь можем протестировать `Employee` независимо от `Salary` при помощи следующей заглушки:
```ts
const SalaryFactory = (): object => ({ value: 500, currency: '$', toString() { return this.value + this.currency; } });
```-->

## Основные принципы ООП

### Инкапсуляция

**Инкапсуляция** скрывает те вещи, которые не должны быть получены или изменены извне. Обычно это детали реализации.

Простой пример реализации инкапсуляции на классах при помощи модификаторов доступа.
```ts
class BlackBox {
  private state: string = 'off';

  start(): void {
    this.state = 'on';
    console.log('Something started...');
  }
}

const box: BlackBox  = new BlackBox();
box.start();
console.log(box.state); // Error: Property 'state' is private and only accessible within class 'BlackBox'.
```
Свойство `state` инкапсулировано при помощи модификатора доступа `private` и до него нельзя достучаться снаружи, поскольку будет ошибка. Аналогично можно инкапсулировать методы класса. 

По умолчанию исползуется модификатор доступа `public`, который явно не пишется. У метода `start` установлен этот модификатор.

Модификатор доступа `private` не позволяет дочерним классам использовать переменные и методы родительского класса, что позволяет сокрыть некоторые его детали реализации. Если же нужно открыть доступ дочерним классам, используется модификатор `protected`. 
```ts
class BlackBox {
  protected state: string = 'off';

  start(): void {
    this.state = 'on';
    console.log('Something started...');
  }
}

class WhiteBox extends BlackBox {
  logState(): void {
    console.log(this.state);
  }
}

const box: WhiteBox = new WhiteBox();
box.logState(); // off
box.start();
box.logState(); // on
```
Как видно из примера выше, дочерний класс, имеющий доступ к скрытым деталям родительского, может их выставить на всеобщее обозрение. Поэтому с модификатором доступа `protected` нужно быть осторожным.

Инкапсуляция на примере функций в JavaScript выглядит ещё проще. Каждая функция по умолчанию создаёт новое лексическое окружение. Все переменные `var`, `let`, `const`, объяслвенные в ней, недоступны снаружи. Доступен лишь результат выполнения функции.
```js
const fn = () => {
  var a = 1;
  let b = 2;
  return a + b;
};

console.log(a); // ReferenceError: a is not defined
console.log(b); // ReferenceError: a is not defined
console.log(fn()); // 3
```

Жизненный пример инкапсуляции:  
Вентилятор. Его интерфейс представлен несколькими кнопками: вкл/выкл и иногда регулировка мощности. Пользователь просто жмёт на кнопки, а внутри вентилятора проиходят процессы, скрытые от глаз пользователя. В частности, работа редуктора и электродвигателя.

### Наследование

**Наследование** позволяет описать *новый* класс на *основе* уже *существующего*.

*Существующий* класс в этом случае называют **базовым** (base), **родительским** (parant), **суперклассом** (superclass).
*Новый* класс — **производным** (derived), **дочерним** (child), **подклассом** (subclass).

*Наследование* — включение поведения и состояния родитеского класса в дочерний. **Поведение** представлено *методами*, **состояние** — *свойствами*.

Дочерний класс включает в себя свойства и методы родительского класса, которые он может доопределять, переопределять или же дополнять своими.

*Наследование* — один из способов *повторного использования кода*. Оно влечёт за собой *сильную связанность* кода: если нужно *изменить класс*, то также придётся *изменить* его *дочерние классы*.

Ниже представлены класс `Point`, описывающий точку с координатами `(x, y)`, и его дочерний класс `ColorfullPoint`, представляющий точку какого-то цвета.
```ts
class Point {
  x: number = 0;
  y: number = 0;
  
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }

  setX(x: number) {
    this.x = x;
  }

  setY(y: number) {
    this.y = y;
  }
}

class ColorfullPoint extends Point {
  color: '#000';

  constructor(x: number, y: number, color: string) {
    super(x, y);
    this.color = color;
  }
}
```

### Полиморфизм

**Полиморфизм** — способность предоставлять один и тот же интерфейс для различных реализаций.

В программировании есть 3 типа полиморфизма:
* *Ad-hoc*
* *Параметрический*
* *Полиморфизм подтипов*

В ООП *полиморфизм подтипов* называется **полиморфизмом**, *параметрический полиморфизм* — **обобщённым программированием**. 
Понятия *Ad-hoc-полиморфизм* в рамках ООП нет.

**Ad-hoc-полиморфизм** поддерживается посредством перегрузки (overload) функций, операторов и методов, в слаботипизированных языках - неявное приведение типов.

* Перегрузка операторов в C++.  

Задана структура `Point`, описывающая точку на плоскости `(x, y)`.  
В примере перегружаются операторы сложения и вывода для структуры `Point`.
```cpp
#include <iostream>
using namespace std;

typedef struct point {
  int x;
  int y;

  point(int x0 = 0, int y0 = 0) {
    x = x0;
    y = y0;
  }
} Point;

/* перегрузка оператора вывода */
ostream& operator << (ostream &stream, const Point &p) {
  return stream << "(" << p.x << ", " << p.y << ")";
}

/* перегрузка оператора сложения */
Point operator + (const Point &A, const Point &B) {
  return Point (A.x + B.x, A.y + B.y);
}

int main() {
  Point A(1, 2);
  Point B(3, 4);
  cout << A + B; /* выведет (4, 6) */
  return 0;
}
```

* Перегрузка функций и методов в TypeScript.

Задан класс `Point`, описывающий точку на координатной прямой `(x)`.  
В примере перегружены методы и функции, связанные со сложением точек.
```ts
/* перегрузка функции */
function sum(A: Point, B: Point): Point;
function sum(A: Point, B: number): Point;
function sum(A: any, B: any): Point {
    return new Point(A + B);
}

class Point {
  x = 0;
  constructor(x: number) {
    this.x = x;
  }
  valueOf() { return this.x; }
  /* перегрузка методов */
  add(point: number): void;
  add(point: Point): void;
  add(point: any): void {
    this.x += point;
  }
}

const A = new Point(1);
A.add(10);
console.log(A); // // Point { x: 11 }
console.log(sum(A, 6)); // Point { x: 17 }

const B = new Point(20);
console.log(sum(A, B)); // Point { x: 31 }
```

* Неявное приведение типов в JavaScript.
```js
1 + "7" // 17
1 + true // 2
true == 'true' // false
```

**Параметрический полиморфизм** позволяет определять функцию или тип данных обобщённо (generically), чтобы их значения обрабатывались идентично вне зависимости от их типа.

```ts
function valueOf<T>(obj: T): any {
  return obj.valueOf();
}

/* экземпляр класса Point из примеров выше */
const A = new Point(17);
console.log(valueOf<Point>(A));
```
```ts
interface Item<T> {
  field: T;
  method(prop: T) => T; 
}

class NumberItem extends Item<number> {
  field: number;
  method(prop: number) => prop + 1;
}

```
```ts
type Props = { /* ... */ };
type State = { /* ... */ };

class MyComponent extends React.Component<Props, State> { /* ... */ }
```

**Полиморфизм подтипов**, **полиморфизм включения** — свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.
```ts
abstract class Shape {
  abstract draw(): void;
}

class Triangle extends Shape {
  draw(): void {
    console.log('drawing a triangle');
    /* ... */
  }
}

class Circle extends Shape {
  draw(): void {
    console.log('drawing a circle');
    /* ... */
  }
}

const shapes: Shape[] = [new Triangle(), new Circle()];
shapes.forEach(shape => shape.draw());
```

## Основные термины ООП

### Ассоциация

**Ассоциация** (Association) — связь, отношение между двумя объектами. Слова «один-к-одному» (one-to-one), «один-ко-многим» (one-to-many), «многие-к-одному» (many-to-one), «многие-ко-многим» (many-to-many) определяют связь между объектами.

Примеры:
* У человека только одна жизнь (один-к-одному).
* В библиотеке много книг (один-ко-многим).
* Несколько книг могут принадлежать одному писателю. (многие-к-одному)
* Здесь есть десятки ресторанов, в каждом из которых есть по-своему интересные блюда (многие-ко многим).

### Агрегация

**Агрегация** (Aggregation) является формой ассоциации, когда один объект "имеет" (отношение "has-a") имеет ссылку на другой объект. Содержащий объект иногда называют **контейнером**.

Агрегация не подразумевает владения: оба объекта могут существовать независимо друг от друга. Уничтожение контейнера не означает уничтожение содержащегося в нём объекта.
```js
class Book {
  constructor(name, author) {
    this.name = name;
    this.author = author;
  }
}

/* книги созданы вне класса Library, поэтому они не зависят
от его экземпляров */
const books = [
  new Book({ name: 'Martin Eden', author: 'Jack London' });
  new Book({ name: '1984', author: 'George Orwell' });
];

class Library {
  books = [];
  /* одним из способов передачи книг при агрегации является
  передача через конструктор */
  constructor(books) {
    this.books = books;
  }
}

let library = new Library(books);
library = null; // уничтожение экземпляра класса Library 
console.log(books); // книги продолжают существовать
```

*Агрегация* на примере *React*. *Компоненту* можно представить как *класс* (функцию), *блоки* можно представить как *объекты*, *экземпляры класса* (константы).  
```jsx
/* компонента (класс, функция) */
const Text = ({ title }) => (<span>{title}</span>);

/* блок (экземпляр класса, константа) */
const helloBlock = (<Text title="Hello" />);

const TextWrapper = () => (
  <div>{helloBlock}</div>
);
```
Блок `helloBlock` создаётся вне компонента `TextWrapper`, поэтому в случае создания и уничтожения блока `<TextWrapper />`, `helloBlock` не уничтожится вместе с ним (он может быть удалён сборщиком мусора, если не будет использован в других местах, но всё же возможность его использования имеется).

### Композиция

**Композиция** (Composition) является *более строгой формой агрегации*, при которой *содержащийся объект не может существовать без своего контейнера*: *уничтожается вместе с контейнером*.

```js
class Book {
  constructor(name, author)
}

class Library {
  books = [];
  constructor() {
    /* книги созданы в конструкторе, поэтому при уничтожении
    объекта они не смогут больше использоваться и будут удалены */
    this.books = [
      new Book({ name: 'Martin Eden', author: 'Jack London' });
      new Book({ name: '1984', author: 'George Orwell' });
    ];
  }
}

let library = new Library();
library = null; // книги удаляются и к ним нет доступа
```

Примеры композиции:
* Квартира не может существовать без дома.
* Работник не может существовать без места работы.

Пример использования композиции в React.
```jsx
/* Экземпляр компонента Title создаётся внутри Article. */
const Article = ({ title }) => (<Title>{title}</Title>);

class App {
  state = {
    isShown: false,
  };

  render() {
    return (
      <div>
        { this.state.isShown
          ? <Article title="notes" />
          : null }
      </div>
    );
  }
};

ReactDOM.render(<App />, document.getElementById('root'));
```
```js
this.setState({ isShown: true });
/* Когда экземпляр компонента Article создаётся,
экземляр компонента Title создаётся вместе с ним. */

this.setState({ isShown: false });
/* Когда экземпляр компонента Article уничтожается (unmount),
экземляр компонента Title уничтожается вместе с ним. */
```

### Обобщение, Специализация и Наследование

**Обобщение** (Generalization) — процесс извлечения общих характеристик (shared characteristics) из нескольких похожих классов и размещения этих характеристик в **обобщённом суперклассе** (generalized/generic/general superclass). Похожие классы становятся его подклассами (subclasses). 

*Общими характеристиками* могут выступать *атрибуты*, *методы* и *ассоциации*.

Общие характеристики перечислены только в суперклассе, но они также применяются и к его подклассам.

**Наследование** (Inheritance) — механизм, передающий характеристики суперкласса его подклассам. Подклассы **наследуют** характеристики свеого суперкласса.

Примет *обобщения*: собака и лошадь являются домашними животными, самолёт и машина являются транспортными средствами.

*Обобщение* использует *отношение "is-a" (является)*.

*Horse is an Domestic Animal, Dog is an Domestic Animal* (класс `DomesticAnimal` — *обобщённый суперкласс*, `Horse`, `Dog` — его *подклассы*).
```js
class DomesticAnimal {}
class Horse extends DomesticAnimal {}
class Dog extends DomesticAnimal {}
```

**Специализация** (Specialization) — *процесс создания новых подклассов из* уже *существующего*. 

*Специализация используется*, если *определённые характеристики* (атрибуты, методы и ассоциации) *применяются только* к *некоторым* *объектам класса*. В таком случае *может быть создан специальный подкласс* (special/specific subclass).

*Специализация не* всегда *означает*, что в *подклассе* должны быть *поля*, которых *нет* в *суперклассе*. *Фиксация конкретных полей суперкласса* в *дочернем подклассе* также *является специализацией*.

*Специализация* может *достигаться* за счёт *наследования* и *композиции*.

Пример *специализации* за счёт *композиции* в *React*.
```jsx
/* два параметра */
const Mesage = ({ title, text }) => (
  <div>
    <p>{title}</p>
    <p>{text}</p>
  </div>
);

/* один параметр фиксирован */
const WelcomeMessage = ({ text }) => (
  <Message title="Welcome" text={text} />
);
```

### Реализация

**Реализация** (Realization) — связь между классом и каким-то объектом, содержащим сведения о том, что должно быть реализовано в классе. Этот объект обычно называют **интерфейсом** (Interface).
```ts
/* пример на typescript */
interface IArticle {
  title: string;
  getTitle: () => string;
}

class Article implements IArticle {
  title: string = '';
  getTitle(): string {
    return this.title;
  }
}
```

Говорят, что *класс реализует* (implements) *интерфейс*.

## Зависимость

**Зависимость** (Dependency) — связь между двумя классами, при которой изменение структуры или поведения одного класса влияет на другой класс. 

Зависимость работает только в одну сторону. 

Зависимым является класс, использующий другой класс.
 
### Абстракция

**Абстрагировать** объект означает отбросить его маловажные, несущественные детали, чтобы выделить самую важную часть. Что войдёт в "самую важную часть", зависит от решаемой задачи.

**Абстракция** — использование только тех характеристик объекта (свойств и методов), которые с необходимой точностью представляют его данные в системе.

*Абстракция* позволяет работать с объектами, не вдаваясь в особенности их реализации.

У всего есть **уровни абстракции**.

Пытаясь что-то описать, мы выбираем какой-то уровень абстракции.
Например, мы можем описать птицу следующим образом.
```ts
class Bird {
  age: number;
  size: number;
  name: string;
}
```
Сейчас мы создали некоторую абстракцию, но также можем добавить некоторые другие детали, чтобы выйти на новый уровень абстракции.
```ts
class Bird {
  age: number;
  size: number;
  name: string;
  eat(): void { /* ... */ };
  sleep(): void { /* ... */ };
}
```
Хорошо подумав, мы решаем, что одного класса для нашего приложения мало: необходимы более конкретные виды птиц (чайка, лебедь). Помечаем класс `Bird` как абстрактный, чтобы нельзя было создавать экземпляры "неконкретных" птиц (`new Bird()`), а также помечаем некоторые его методы (в приложении голубь и лебедь могут спать и есть по-разному, пусть в них это и определяется).
```ts
abstract class Bird {
  age: number;
  size: number;
  name: string;
  abstract eat(): void;
  abstract sleep(): void;
}

class Gull extends Bird {
  eat(): void {
    console.log('eating a fish');
  }

  sleep(): void {
    console.log('sleep on the mountain');
  }
}

class Dove extends Bird { /* ... */ }
```
Затем мы решаем, что есть плавающие и летающий птицы.
```ts
abstract class Bird { /* ... */ }

abstract class SwimmingBird extends Bird {
  abstract swim():void;
}

abstract class FlyingBird extends Bird {
  abstract fly():void;
}
```
Поскольку в TypeScript множественное наследование запрещено, а в абстрактных классах у нас нет реализованных методов, используем интерфейсы.
```ts
interface IBird {
  age: number;
  size: number;
  name: string;
  eat: () => void;
  sleep: () => void;
}

interface ISwimmingBird extends IBird {
  swim: () => void;
}

interface IFlyingBird extends IBird {
  fly: () => void;
}

class Gull implements ISwimmingBird, IFlyingBird { /* ... */ }
class Starling implements IFlyingBird { /* ... */ }
class Chicken implements IBird { /* ... */ }
```
Можно продолжать детализировать и дальше, если этого требует приложение.


## CRP: Композиционный принцип повторного использования

**Composite Reuse Principle** (CRP) гласит, что *классам* следует *достигать полиморфного поведения* и *переиспользуемости кода* за счёт *композиции* классов, а *не наследования*.

### Способ реализации CRP
Создаётся множество интерфейсов, описывающих поведение системы. Классы выборочно реализуют эти интерфейсы, формуруя таким образом доменные модели. 

При таком подходе *интерфейсы помогают добиться полиморного поведения*.

Если нужно создать класс, похожий на уже существующий, но в то же время имеющий некоторые изменения, можно просто создать новый класс, по-своему реализующий тот же интерфейс. 

Вероятно, при использовани композиции часть кода будет дублироваться, но он будет менее связанным, а значит более поддерживаемым.

### Преимущества композиции над наследованием
* **Переиспользуемость кода**. Множественное наследование запрещено во множестве языков программирования из-за ромбовидной проблемы (diamond problem): два родительских класса могут иметь метод с одинаковым названием и не известно, какой из них нужно унаследовать. По этой причине есть возможность наследоваться только от одного класса. Композицию же можно использовать неограниченное количество раз.
* **Тестирование**. Композицию можно просто подменить тестовым объектом (заглушкой, реализующей интерфейс), наследование требует использование родительского класса, заменить который нельзя и придётся напрямую его импортировать.
* **Инкапсуляция**. Наследование нарушает принцип инкапсуляции, так как дочерний класс зависит от поведения своего родительского класса и изменение последнего повлечёт за собой непосредственное изменение дочернего класса. Модификаторы доступа частично решают эту проблему.

## Принципы SOLID

Принципы SOLID были разработаны Робертом С. Мартином.

* **Single Responsibility Principle** (SRP, Принцип единственной ответственности).
* **Open-Closed Principle** (OCP, Принцип открытости-закрытости).
* **Liskov Substitution Principle** (LSP, Принцип подстановки Барбары Лисков).
* **Interface Segregation Principle** (ISP, Принцип разделения интерфейса).
* **Dependency Inversion Principle** (DIP, Принцип инверсии зависимостей).

### SRP: Принцип единственной ответственности

Должна быть лишь одна причина изменить класс (функцию, метод), то есть класс должен быть ответственен лишь за что-то одно. Если у класса несколько причин для изменения, то он отвечает за несколько задач.

Если класс отвечает за решение нескольких задач, его подсистемы, реализующие решение этих задач, оказываются сильно связанны друг с другом. Изменения в одной такой подсистеме ведут к изменениям в другой.

Пусть есть массив каких-то объектов и функция поиска объекта в этом массиве по `id`.
```js
import _ from 'lodash';

const items = [{
  id: '1',
  name: 'n',
  description: 'd',
}, /* ... */];

const findItem = (id) => {
  /* находим элемент */
  const item = items.find(item => item.id === id);
  /* убираем ненужные поля */
  return _.omit(item, ['description']);
}

const item = findItem('1');
```
Функция `findItem` нарушает принцип единственной ответственности, поскольку совершает две операции: поиск элемента и удаление ненужных полей из найденного объекта. Если изменятся поля, которые не нужно возвращать, то изменится и функция `findItem`. Если изменится поле `id` (например, `_id`, `username`), то `findItem` снова изменится. Имеем две причины для изменения. Такой код сильно связан и его сложно переиспользовать. 
Разбиение функции `findItem` на две сделает код более переиспользуемым и менее связанным. Пример можно применить и к классу, создав класс `Items` с методами `findItem` и `omitFields`.
```js
const findItem = id => items.find(item => item.id === id);
const mapItem = (item, fields) => _.omit(item, fields);

const searchResult = findItem('1');
const item = mapItem(searchResult, ['description']);
```

Рассмотрим более неоднозначный пример. Допустим, мы отправляем HTTP-запрос на создание пользователя к серверу. Часто такой запрос сперва создаёт пользователя, а затем возвращает его. То есть по сути совмещает в себе две операции: создание и получение пользователя. Несмотря на то, что это является оптимизацией (один запрос к серверу вместо двух), это также является нарушением принципа единственной ответственности. Такую логику можно разбить на Команду (Comand), которая совершает действие (создаёт пользователя) и ничего не возвращает, и Запрос (Query), который получает пользователя из базы данных и возвращает его. Правда, чаще всего выбирают оптимизацию.
```js
const createUser = async (req, res) => {
  try {
    const user = await userRepository.create(req.body);
    return user;
  } catch (e) {
    res.send(404);
  }
};
```
Существуют и исключения из правила. Например, структура данных Стек имеет функцию `pop()`, которая удаляет элемент и возвращает его. Такое поведение функции общепринято и считается ожидаемым.

Минусом подхода является увеличение количества классов (функций, методов), которые хоть и делают код более переиспользуемым, но усложняют его в целом.

### OCP: Принцип открытости-закрытости

Программные сущности (классы, модули, функции) должны быть открыты для расширения (extension) и закрыты для модификации (modification).

Это довольно сложный в реализации принцип.  
Его идея заключается в том, что следует код, который будет достаточно гибким для изменений в будущем. Новый функционал должен вносить минимум изменений в уже существующий. Но обычно новый функционал не всегда просто предсказать. 

Пытаясь продумать многие случаи использования наперёд, разработчик часто создаёт такие вещи, которые в данный момент ему не нужны, а в будущем могут даже и не понадобиться. При этом они увеличивают сложность кода, требуют время на написание и поддержку. Нелегко найти баланс между написанием гибкого кода и написанием ненужного кода, поэтому у принципа открытости-замкнутости есть противоположный принцип - принцип YAGNI.

Пусть нужно необходимо написать класс, описывающий личного ассистента с возможностью приветствия.  
Создаём класс `Assistant` с методом для приветствия `sayHi`.
```ts
class Assistant {
  sayHi(): void {
    console.log('Hi!');
  }
}
```
Пока сложно предсказать, как изменится класс. Можно только догадываться.

Требования изменились и нужно написать функциональность для прощания `sayGoodbye`.
```ts
class Assistant {
  sayHi(): void {
    console.log('- Hi! Nice to meet you.');
  }
  sayGoodbye(): void {
    console.log('- Bye! See you later.');
  }
}
```
Часть логики повторилась и теперь понятно, что в будущем могут появиться новые методы (например, поблагодарить).
Сделаем класс более гибким для дальнейшего расширения.
```ts
class Assistant {
  say(phrase): void {
    if (phrase === 'hi') {
      console.log('- Hi! Nice to meet you.');
    } else if (phrase === 'bye') {
      console.log('- Bye! See you later.');
    }
  }
}
```
Теперь на каждую новую фразу не создаётся новый метод: есть один метод, который обрабатывает все фразы. Но с появлением новых фраз метод будет бесконечно изменяться и расти (новые `else-if` блоки), а следовательно и класс. Более того сами фразы могут измениться в будущем или же разные экземпляры класса `Assistant` захотят произносить разные фразы.

Упрощаем метод `say`, вынося фразы за его пределы.
```ts
const SARA_PHRASES = {
  hi: 'Hi! Nice to meet you.',
  bye: 'Bye! See you later.',
};

class Assistant {
  say(phrase: string): void {
    console.log(`- ${phrase}`);
  }
}

const Sara: Assistant = new Assistant();
Sara.say(SARA_PHRASES.hi);
```

Пусть помимо слов в привествии, появляется дополнительное действие (например, похмахать рукой), тогда можно создать новый метод, не затрагивая старый код.
```ts
great(phrase: string): void {
  this.say(phrase);
  waveHand();
}
```

Удалось сделать достаточно гибкий к изменениям класс. Eсли в дальнейшем понадобится дополнительно добавить локализацию, заменить фразу, считать фразы из файла или получать их по запросу, потребуется минимум изменений в уже написанном коде.


### LSP: Принцип подстановки Барбары Лисков

**Принцип Барбары Лисков**.
> Пусть `q(x)` — свойство, справедливое для любых объектов `x`, принадлежащих типу `T`, тогда свойство `q(y)` должно быть также справедливо для любых объектов `y`, принадлежащих типу `S`, являющимся подтипом типа `T`.

```math
q(x) = true ∀x ∈ T => q(y) = true ∀y ∈ S, ∀S ⊂ T
```

*Роберт Мартин переформулировал принцип Барбары Лисков* следующим образом.
> Функции, использующие базовый тип, должны иметь возможность использовать его подтипы, не зная об этом.

Рассмотрим пример с *птицами*. Есть *класс* (базовый тип) `Bird` и его *дочерний класс* (подтип) `Chicken`.
```ts
import { loadAnimation, showAnimation } from '/* ... */';

const animateFlight = (bird: Bird) => {
  /* подгружается анимация полёта птицы и отображается */
  const animation = loadAnimation(bird.constructor.name);
  showAnimation(animation);
};

class Bird {
  makeSound(): void {};
  fly(): void {
    animateFlight(this);
  }
}

class Chicken extends Bird {
  constructor() { 
    super();
  }
  
  /* переопределение метода родительского класса */
  makeSound(): void {
    console.log('cackle');
  }
}
```
Функция `animateFlight(bird: Bird)` использует базовый тип `Bird`. Опираясь на высказывание Роберта Мартина, функция `animateFlight` не должна знать о том, что использует класс `Chicken` вместо `Bird`, то есть в ней не должно быть проверки принадлежности к конкретному классу (например, `if (bird instanceof Chicken)`).

Класс `Bird` имеет метод `fly` (летать), `Chiken` наследует этот метод. Тем не менее, пример ниже выдаст ошибку.
```ts
animateFlight(new Bird());
animateFlight(new Chicken()); // ошибка
```
*Курица не умеет летать* и функция `animateFlight()` *не имеет* подходящей *анимации* для её *полёта*. Такие случаи придётся *обрабатывать отдельно* для *нелетающих птиц*. То есть мы не можем использовать функцию `animateFlight()` с `Chicken`, но можем использовать её с родительским классом `Bird`. Это свидетельствует о *плохо подобранной абстракции*, *нарушающей принцип Барбары Лисков*.

Чтобы принцип *соблюдался*, следует убрать метод `fly()` из класса `Bird` (поскольку не всем птицам характерно летать). 

Одним из способов это сделать является перенос метода `fly()` из `Bird` в новый подкласса `FlyingBird`. В этом случае используется принцип разделения интерфейса. 
```ts
class Bird {
  makeSound(): void {};
}

class Chicken extends Bird {
  makeSound(): vold { /* ... */ }
}

class FlyingBird extends Bird {
  fly(): void { /* ... */ }
}
```
Поскольку полёт теперь не является обязательным поведением птицы `Bird`, но является для `FlyingBird`, меняем также тип параметра у функции `animateFlight()`. 
```ts
const animateFlight = (bird: FlyingBird) => { /* ... */ };

class Dove extends FlyingBird {
  makeSound(): vold { /* ... */ }
}

animateFlight(new FlyingBird());
animateFlight(new Dove());
```
Функция `animateFlight` не может работать с `Chiken`, но она не может работать и с `Bird`: принцип Барбары Лисков не нарушен. 

<!--Вообще говоря, класс `Bird` следовало бы сделать абстрактным вместе с его методами, поскольку в подобранной абстракции рассматриваются конкретные виды птиц (голубь, курица и так далее). Более того, и спользуется функция с анимацией полёта и трудно представить полёт какой-то абстрактной птицы. Но в случае абстрактного класса `Bird` невозможно было бы создать экземпляр `new Bird()` и передать -->

<!-- не существует неконкретной птицы: каждая птица имеет название, внешний вид, способы передвижения. Невозможно также представить анимацию полёта `Bird` (в примере мы предполагаем, что она задана). Но в случае абстрактного класса `Bird `невозможно было бы создать его экземпляр `new Bird()`, а следовательно и заменить его на `new Chicken()`. -->

### ISP: Принцип разделения интерфейса

Нужно создавать узкоспециализированные интерфейсы, предназначенные для конкретных целей.  

Клиент не должен зависеть от тех элементов интерфейса, которые он не использует.

Например, имеем следующий интерфейс.
```ts
interface IPerson {
  sleep: () => void;
  watchFilm: () => void;
  visitMeeting: () => void;
  writeCode: () => void;
  testCode: () => void;
}
```
Допустим, нужно написать класс `Worker`, который описывает программиста и реализует интерфейс `IPerson`. В этом случае он также реализует методы `sleep()` и `watchFilm()`, хоть они ему и не нужны. Получается, что имеем лишний код, который увеличивает сложность приложения.
```ts
class Worker implements IPerson {
  visitMeeting() { /* ... */ }
  writeCode() { /* ... */ }
  testCode() { /* ... */ }
  
  watchFilm() {
    throw new UnsupportedActionError();
  }
  sleep() {
    throw new UnsupportedActionError();
  }
}
```
Интерфейс `IPerson` выше называют жирным (fat) или загрязнённым (polluted). Такие интерфейсы по возможности следует разбивать на несколько. Разбиение проводится до тех пор, пока интерфейсы не будут содержать только действительно необходимые им методы.
```ts
interface IPerson {
  sleep: () => void;
  watchFilm: () => void;
}

interface IWorker {
  visitMeeting: () => void;
  writeCode: () => void;
  testCode: () => void;
}
```

Недостатком принципа считается рост количества интерфейсов. Их может стать слишком много.

### DIP: Принцип инверсии зависимостей

Зависимость должна идти в сторону абстракции, а не чего-то конкретного.

Абстракции не должны зависеть от деталей.  
Детали должны зависеть от абстракций.

Модули верхних уровней (high-level modules) не должны зависеть от модулей нижних уровней (low-level modules).  
Оба типа модулей должны зависеть от абстракций.

Пусть у нас есть класс `Service` (модуль верхнего уровня), который использует класс `Repository` (модуль нижнего уровня) и, соответственно, зависит от него. Аналогично, класс `Repository` использует класс-сущность `User`.
```ts
class UserService {
  repo: UserRepository;
  constructor () {
    this.repo = UserRepositoryFactory();
  }
}

const UserRepositoryFactory = (): UserRepository => new UserRepository();

class User {
  id: string;
  name: string;
  constructor (id: string, name: string) {
    this.id = id;
    this.name = name;
  }
}

class UserRepository {
  constructor () {}
  getUserById(id: string): User { /* ... */ }
}
```
В примере выше использован паттерн проектирования "Фабрика", чтобы вынести логику создания объектов из сервиса (Inversion of Control).

Выделим абстракцию, определив интерфейсы.
```js
interface IUser {
  id: string;
  name: string;
}

interface IUserRepository {
  getUserById(id: string): IUser
}
```
Заменяем в `UserService` строчку `repo: UserRepository` на `repo: IUserRepository` и в `UserRepository` строчку `getUserById(id: string): User` на `getUserById(id: string): IUser`. Теперь модули верхних уровней не зависят от модулей нижних уровней, а зависят от интерфейсов, то есть абстракции.

Заменим также строчку `class User {}` на `class User implements IUser {}`,  строчку `class UserRepository {}` на `class UserRepository implements IUserRepository {}`. Теперь модули нижних уровней (классы; детали реализации) зависят от абстракции, но не наоборот.

## Принцип DRY

**Принцип DRY** (Don’t repeat yourself) переводится как "Не повторяйся" и нацелен на снижение повторения информации различного рода.
> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

«Каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в рамках системы».

Когда DRY применяется правильно, изменение элемента системы не требует изменений в других, логически не связанных элементах.

Нарушение принципа DRY называют WET (Write Everything Twice), переводящееся как "Пиши всё дважды".  
Игра слов: dry — сухой, wet — влажный.

Простой пример применения принципа DRY: видишь часто повторяющийся код — выносишь его в функцию или переменную.

## Принцип KISS

**Принцип KISS** (keep it short and simple) переводится как "Держи это коротким и простым" и утверждает, что в большинстве случаев система работает лучше, если отаётся простой, поэтому в области проектирования простота должна быть одной из ключевых целей и следует избегать ненужной сложности.

## Принцип YAGNI

**Принцип YAGNI** (You aren't gonna need it) переводится как "Вам это не понадобится" и утверждает, что не следует добавлять функциональность пока не возникнет необходимость в этом.
> Always implement things when you actually need them, never when you just foresee that you need them.

«Всегда реализуй вещи, когда ты действительно в них нуждаешься, никогда, если ты предвидишь, что они тебе понадобятся».

Написание ненужного в данный момент кода требует время и поддержку, которые могли быть затрачены на важные в данный момент вещи. Такой код ограничивает то, что может быть сделано в будущем, увеличивает сложность приложения.


================================================
FILE: Auth.md
================================================
- [Аутентификация, Авторизация и Идентификация](#аутентификация-авторизация-и-идентификация)
- [Разновидности идентификаторов. `ID`, `UID`, `UUID` и `GUID`](#разновидности-идентификаторов-id-uid-uuid-и-guid)
- [Виды аутентификации](#виды-аутентификации)
  - [Базовая (`Basic`)](#базовая-basic)
  - [`Bearer`](#bearer)
- [Сессии (`Sessions`) и `Cookie`](#сессии-sessions-и-cookie)
- [`JSON Web Token` (`JWT`)](#json-web-token-jwt)
  - [Виды токенов](#виды-токенов)
- [`OAuth`](#oauth)

## Аутентификация, Авторизация и Идентификация

**Идентификация** (англ. `identification`, лат. `identifico` — отождествлять) — *процедура распознавания объекта* (субъекта) по его *идентификатору*.  

Например, можно *идентифирировать* человека по имени, электронной почте, номеру телефона или паспорта.

**Аутентификация** (англ. `authentication`, греч. `αυθεντικός` — подлинный) — процедура проверки подлинности.

Например, можно определить подлинность проверкой пароля, отпечатка пальца, наличием пропуска или ключа от двери.

**Авторизация** (англ. `authorization` - разрешение, уполномочивание) – предоставление доступа к какому-либо ресурсу.

### Взаимосвязь
Стандартная последовательность действий на любом сайте.
* *Установление личности* (username, email, phone) – *идентификация*.
* *Проверка подлинности* (password) – *аутентификация*.
* *Предоставление доступа* – *авторизация*.

## Объект и субъект
**Объект** (лат. objectum — предмет) — философская категория, обозначающая вещь, явление или процесс, на которые направлена предметно-практическая, управляющая и познавательная деятельность субъекта (наблюдателя); при этом в качестве объекта может выступать и сам субъект.

## Разновидности идентификаторов. `ID`, `UID`, `UUID` и `GUID`

**Идентификатор** (англ. `identifier`, `ID`) — уникальный признак объекта (субъекта), позволяющий отличать его от других объектов (идентифицировать). 

Поле `ID` обычно является главным ключом (англ. `primary key`), по которому можно найти конкретный объект в коллекции объектов.

Поле `ID` чаще всего представлено в виде некоторого хэша, сгенерированного в заданном формате по некоторому алгоритму. Существует множество форматов ID, при этом для генерации могут использовать индексы (последовательное инкрементирование - самый простой случай), даты (англ. `timestamps`), рандомные числа, другие поля объекта, которые тоже по некоторому алгоритму становятся хэшем. Далее рассмотрим некоторые варианты `ID`.

`UUID` (англ. `Universally Uique IDentifier`) — *стандарт идентификации*, стандартизированный Open Software Foundation как часть среды распределённых вычислений (DCE). 

UUID позволяет уникально идентифицировать информацию, не имея при этом центра координации.  
То есть любой может создать `UUID` и использовать его для идентификации чего-либо с достаточным уровнем уверенности, что этот идентификатор не будет использован для чего-либо ещё непреднамеренно.

`UUID` имеет *несколько версий*, в которых используются:
* **Версии 1 и 2** - *время* и *MAC-адрес*.
* **Версия 3** - *хеширование* алгоритмом *MD5*.
* **Версия 4** - генерация *случайным образом*.
* **Версия 5** - *хеширование* алгоритмом *SHA-1*.

Библиотека в `npm`, позволяющая создавать и проверять валидность `UUID`:
```bash
npm i uuid
```

# Виды аутентификации

Для *проверки подлинности* (аутентификации) пользователя обычно на сервер вместе с запросом передаётся заголовок `Authorization`, в котором размещают тип аутентификации и соответствующие ей данные.
```http
Authorization: <type> <credentials>
```

## Базовая (`Basic`)

**Базовая** (Basic) **аутентификация** подразумевает передачу в заголовок имени пользователя и пароля в виде строки следующего вида `username:password`, зашифрованной в Base64.
```http
Authorization: Basic <encodedCredentials>
```
В JavaScript можно кодировать и декорировать Base64 следующим образом.
```js
const credentials = 'admin:admin';
/* кодировка */
const encodedCredentials = btoa('admin:admin');
console.log(encodedCredentials); // 'YWRtaW46YWRtaW4='
/* декодировка */
const decodedCredentials = atob('YWRtaW46YWRtaW4=');
console.log(decodedCredentials); // 'admin:admin'
```

## `Bearer`

```http
Authorization: Basic <encodedCredentials>
```

# Сессии (`Sessions`) и `Cookie`

```js
/* получение Cookie */
       /* Auth request
       with credentials */
Client --------------------> Server

        /* Set-Cookie 
        response header */
Client <-------------------- Server

        /* Cookie 
        request header */
Client --------------------> Server
```
Когда Cookie устаревают, они удаляются и на клиенте, и на сервере, поэтому описанный выше алгоритм повторяется.
```js
/* обновление auth_token */

        /* Cookie is
        missing (401) */
Client <-------------------- Server
```

<!-- Когда появляется внешний сервер аутентификации (другой url), -->

# `JSON Web Token (JWT)`

## Содержимое токена
```js
/* header.payload.signature */
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImdhcnJ5QGl0ZWNoYXJ0LWdyb3VwLmNvbSIsInN1YiI6MSwiaWF0IjoxNTgxMzUzMTk1LCJleHAiOjE1ODEzNjM5OTV9.sIAg18cc66dLi4PyLGzITOou8nppH056pxVVH0pgWMs
```
## Виды токенов

### Токен доступа

```js
/* получение или обновление refresh_token */

       /* Auth request
       with credentials */
Client --------------------> Auth Server

        /* auth_token,
        refresh_token */
Client <-------------------- Auth Server

        /* Authorization
        request header with
        auth_token */
Client --------------------> Server
```

**Токены доступа** (Access Tokens) представляют собой учётные данные (credentials), которые используеются для доступа к защищенным ресурсам.

Токен доступа отправляется на сервер с каждым запросом в `Authorization` заголовке запроса.

*Токены доступа* можно использовать неограниченное количество раз, но они обычно имеют *короткую продолжительность жизни* (обычно устанавливают ~15 минут), чтобы их нельзя было украсть и постоянно использовать.

Таким образом, если токен доступа украдут, то им можно будет пользоваться не более указанного в нём времени (например, не более 15 минут).

Время истечения токена может быть определено по полям  `exp` (expiration time), определяющим время жизни токена и `iat` (issued at), определяющим дату создания токена. Подделать это время нельзя, поскольку формула формирования токена нарушится и он станет невалидным.

Токены доступа не хранятся на сервере, на клиенте чаще всего хранятся в Local Storage (и берутся оттуда, чтобы их можно было отправить в заголовке запроса).

### Токен обновления

```js
/* обновление auth_token */

        /* auth_token is
        expired (401) */
Client <-------------------- Auth Server

        /* Auth request
       with refresh_token */
Client --------------------> Auth Server

        /* auth_token,
        refresh_token */
Client <-------------------- Server
```

Когда токен доступа истекает и требуется новый для доступа к ресурсу, клиенту необходимо обратиться к серверу аутентификации (Auth Server) для получения нового токена.

**Токен обновления** (Refresh Token) содержит информацию, необходимую для получения нового токена доступа.

Серверу аутентификации отсылается токен обновления и возвращается токен доступа.

Токены обновления одноразовые и обычно имеют долгую продолжительность жизни. 

Если токен обновления украден, его можно использовать только один раз, а значит получить только один токен доступа.

Если клиент видит, что его токен доступа невалиден (им уже воспользовались), он может перелогиниться. Таким образом сгенерируется новый токен обновления, а старый станет невалидным.

Токены обновления не хранят в себе какую-то информацию, как JWT-токены, и обычно представляют обычную хеш-строку. Токены обновления необходимо хранить на сервере.

В отличие от токенов доступа токены обновления хранятся на сервере. Они также хранятся и на клиенте.

# `OAuth`


================================================
FILE: Browsers.md
================================================
- [Файлы](#файлы)
- [Как всё устроено](#как-всё-устроено)
	- [Адресная строка браузера](#адресная-строка-браузера)
	- [Обработка URL](#обработка-url)
  - [Этап поиска DNS](#этап-поиска-dns)
  - [Построение объектных моделей (DOM, CSSOM)](#построение-объектных-моделей-dom-cssom)
  - [Запуск JavaScript](#запуск-javascript)
  - [Построение дерева рендеринга](#построение-дерева-рендеринга)
  - [Рассчёт макета (Layout, Reflow)](#рассчёт-макета-layout-reflow)
  - [Прорисовка, отрисовка (Painting, Paint)](#прорисовка-отрисовка-painting-paint)

# Файлы

<!-- Физическая память компьютера имеет байтовую структуру – единицей адресации является байт -->

**Файл** (англ. `file`) — определенное количество информации (в виде программы или данных), имеющее имя и хранящееся во внешней памяти компьютера.

**Текстовый** файл (англ. `text file`) содержит *текстовые данные* (символы, составлящие слова, формулы и прочее).

**Двоичный, бинарный** файл (англ. `binary file`) содержит последовательность байт (состоящих из бит), которая что-то может значить.

Обычно принято *разделять файлы* на *текстовые* и *бинарные*, но оба этих типа хранятся в памяти компьютера *одинаково*: как *последовательность байт*. Текстовый файл просто интерпретируется в текст, но на самом деле является *частным случаем бинарного файла*. На низком уровне кодировки и символы компьютером не воспринимаются: он понимает только нули и единицы.  

**Имя файла** (англ. `filename`) состоит из двух частей, разделенных точкой `.`: *название файла* и *расширение*, *тип файла* (англ. `extension`).
Например, `Notes.pdf`, `Browsers.md`, `users.txt`.

**Файловая система** (англ. `file system`) — система хранения файлов и организации каталогов (папок).

# Как всё устроено

Примечание: в браузерах Webkit (Chrome, Safari) и Gecko (Mozilla) *терминология* немного отличается.  
Далее *будет рассматриваться версия Webkit* с *пометками* о том, как это называется в *Gecko*.

## Адресная строка браузера

**Uniform Resource Locator** (URL) — ссылка на веб-ресурс (документ, изображение и прочее), определяющая его расположение в компьютерной сети и способ его получения (зависит от протокола).

**Адресная строка** (address bar, URL bar) — элемент интерфейса веб-браузера, который отображает текущий URL и позволяет изменять его на любой другой.

Следующая схема URL описывает большинство случаев использования адресной строки.
```
protocol:[//hostname[:port]][/path][?query]
```
Допустим, пользователь ввёл URL и нажал Enter.

## Обработка URL

**Лексема** — последовательность допустимых символов языка, имеющая определённый смысл для транслятора.

Браузер принимает введённую пользователем строку и пытается понять, из чего она состоит — разбивает её на лексемы (за счёт наличия `:`, `//`, `/`, `?`).  

Примеры *разбиения URL на лексемы*:  
* http://localhost:3000  
`http` - протокол, `localhost` - hostname, `3000` - порт
* https://twitter.com/favicon.ico:  
`https` - протокол, `twitter.com` - hostname, `favicon.ico` - путь
* https://github.com/Max-Starling?from=2018-12-01&to=2018-12-31  
`https` - протокол, `github.com` - hostname, `Max-Starling` - путь, `from=2018-12-01&to=2018-12-31` - query.

При наличии в URL корректного hostname и отсутствии протокола, браузер подставляет нужный протокол самостоятельно (по умолчанию HTTP, HTTPS для сайтов).

Если отсутствует протокол и браузеру не удаётся найти корректный hostname в строке, то она воспринимается как поисковой запрос.  

Например, если пользователь ввёл в адресную строку `hello world`, то браузер с поисковиком Chrome по умолчанию перейдёт на https://www.google.com/search?q=hello%20world.

## Этап поиска DNS

**Domain Name System** (DNS) — система доменных имён, которая хранит информацию о доменах по их именам.  
Обычно её используют для получения IP-адреса по имени хоста.

**Домен** (domain) — узел в дереве имён, вместе со всеми дочерними (подчинёнными) ему узлами.  

**Доменное имя** — символьное обозначение, зарегистрированное для сетевой адресации, в которой используется DNS. 
Доменные имена в доменах отделяются точками. Разрешённые символы в доменном имени: `a-z`, `0-9`, `-`. 

DNS имеет *древовидную* (иерархическую) структуру.

Например, система доменных имён из двух доменов `subdomain.domain.com` и `wikipedia.org` имеет вид:
![DNS](./assets/DNS.png)

На самом деле полный домен выглядит вот так `wikipedia.org.`, cимвол `.` в конце — **корневой домен**, `org` — домен верхнего (первого) уровня, `wikipedia` — домен второго уровня.  

**Поддомен** (subdomain) — подчинённый домен.  
`domain.com` - поддомен домена `com`, `subdomain.domain.com` - поддомен домена `domain.com`.

Сперва браузер проверяет *локальный кэш DNS*.

В случае отсутствия кэша, для поиска (lookup) браузер вызывает функцию `gethostbyname`, которая работает по-разному в зависимости от операционной системы.

Функция `gethostbyname` сперва проверяет локальный файл `hosts` (текстовый файл, содержащий базу данных доменных имён и используемый для их трансляции в IP-адреса), содержимое которого задаётся администрартором компьютера.
```css
/* файл hosts */
127.0.0.1       localhost
```

Если файл `hosts` не содержит нужного домена, делается запрос к DNS-серверу, являющемуся частью настроенного стека протоколов (network stack).

Есть два популярных DNS-сервера: `1.1.1.1` — Cloudflare, `8.8.8.8` — Google), однако большинство людей используют DNS-сервер, предоставленный интернет-провайдером.

DNS-сервер может хранить IP-адрес домена в своём кэше, если недавно уже разрешал (resolve) его.  
Если в кэше домена нет, то делается запрос к корневому DNS-сервер `.` (система из множества серверов по всему миру, управляющая интернетом).

Корневой DNS-сервер не знает IP-адреса каждого домена в мире, но может перенаправить запрос на тот DNS-сервер, который может знать — сервер верхнего уровня.



Браузер выполняет DNS-запрос по протоколу UDP, являющимся транспортным протоколом без установки соединения (connectionless).

## Построение объектных моделей (DOM, CSSOM)

**Байты** (bytes) → **Символы** (characters) → **Лексемы** → **Токены** (tokens) → **Узлы** (nodes) → **Объектная модель** (object model)

*Алгоритм обработки HTML-файла*:
1) **Загрузка файла**. Браузер загружает файл (обычно из сети или локальной файловой системы, т.е. с диска), закодированный какой-то кодировкой. В компьютерной памяти все файлы представляются байтами, поэтому при загрузке файла браузер получает поток байтов.
```css![DNS](./assets/DNS.png)
EF BB BF EF B9 A4 68 74 6D 6C EF B9 A5
```
2) **Распознавание кодировки**. Сначала браузер пытается распознать кодировку. Он считывает первые три байта из потока в буфер и проверяет, являются ли они символом маркера последовательности байтов (Byte Order Mark, `U+FEFF`), который указывает кодировку. Если да, то оставшиеся символы расшифровываются с учётом этой кодировки, если нет, то браузер пытается распознать кодировку самостоятельно, в случае неудачи далее используется кодировка UTF-8.
```css
0xEF 0xB9 0xA4 → U+FEFF → маркер кодировки UTF-8
```
3) **Преобразование байтов в символы (decoding)**. Браузер расшифровывает (decode) поток байтов (stream of bytes) в символы, опираясь на заданную кодировку (encoding). [Подробнее об алгоритме декодирования можно прочитать здесь](./Encoding.md).
```html
0xEF 0xB9 0xA4 → U+FE64 → "<"
0x68 → U+0068 → "h"
0x74 → U+0074 → "t"
0x6D → U+006D → "m"
0x6C → U+006C → "l"
0xEF 0xB9 0xA5 → U+FE65 → ">"

<html>
```
4) **Лексический анализ (lexing, tokenization)**. Браузер конвертирует последовательности символов в отдельные распознанные группы — **лексемы**, каждая из которых имеет определённый смысл. Затем лексемы преобразовываются в объекты — **токены**, имеющие определённые свойства и правила.  

Пример *разбиения HTML на лексемы*: `<div class="box"></div>`.  
`<` - начало тега, `div` - название тега, `class` - название атрибута, `=` - символ, объединяющий атрибут и его значение, `"` - начало значения атрибута, `box` - значение атрибута, `"` - конец значения атрибута, `>` - конец тега, `</` - начало закрывающего тега, `div` - название тега, `>` - конец тега.  

5) **Построение объектной модели**. На основании информации токенов и их последовательности выясняется, из каких элементов состоит HTML и как они расположены по отношению друг к другу. Первое позволяет представить html-элементы в виде объектов. Второе — в виде дочерних и родительских элементов одного дерева. Поскольку в качестве узлов, вершин (nodes) дерева берутся объекты, полученная модель называется **объектной моделью**.  

Полученную модель называют **объектной моделью документа** (Document Object Model, DOM).  

Этот *алгоритм браузер* проделывает *каждый раз*, когда нужно *обработать HTML*.

Теперь *браузер загружает подключенные* к HTML-документу *ресурсы* (делая к ним запросы): картинки, видео, иконки, шрифты, а также *CSS*.
```html
<link rel="stylesheet" type="text/css" href="/styles.css"/>
```

Любой CSS, присутствующий в документе (загруженный файлом или встроенный в документ при помощи `<style>`, `style=""`), браузер обрабатывает по тому же алгоритму, что и HTML, однако до построения объектной модели происходит ещё два важных действия:
* каскад
* обработка значений.


Получившееся в результате работы алгоритма представление в виде дерева называется **объектной моделью CSS** (CSS Object Model, CSSOM).

Пример *разбиения CSS на лексемы* : `.box { width: 40px; }`.  
`.` - начало селектора по классу, `box` - название класса, `{` - начало блока объявлений, `width` - свойство, `:` - символ, объединяющий свойство с его значением, `40px` - значение свойства, `;` - конец объявления, `}` - конец блока объявлений.

В CSS многие стили применяются не только к самому элементу, но и к его потомкам.  
Это называется наследованием: потомки наследуют свойства предка.  
Именно поэтому CSS имеет древовидную структуру.

## Обработка значений (Value Processing)

*Значение* при *обработке* проходит *следующие этапы*.
* **Объявленное значение** (Declared value) — значение, *объявленное автором* (author declaration), то есть *разработчиком*.
* **Каскадное значение** (Cascaded value) — значение, полученное в *результате применения каскада*. Если есть *конфликтующие объявления*, *применяется более приоритетное* из них и становится *каскадным*. *Значение*, заданное *браузером* (user-agent declaration), *становится каскадным*, если *бъявленное значение отсутствует*. Примеры *браузерных значений*: синий цвет и подчёркивание ссылок `<a>`.
* **Указанное значение** (Specified value) — значение, заданное по умолчанию для свойства (initial value). *Указанное значение* *используется*, если *каскадное значение отсутствует*. *Каждое свойство* имеет *указанное значение*. *Значение*, заданное *браузером не является* *значением по умолчанию*. Пример значений *по умолчанию*: для `margin` и `padding` по умолчанию применяется `0px`, для шрифтов — `16px`, для цвета — `#000`. *Наследуемое значение* также *становится указанным* (применяется, если объявленное и каскадное отсутствуют). *Наследуется вычисленное значение* (computed value) *родительского элемента*, а *не объявленное*.
* **Вычисленное значение** (Computed value) — значение, переведённое из *относительных единиц* (relative) в *абсолютные* (absolute). Также на этом шаге происходит перевод таких значений, как `auto`, `green`, `bold` и других.
* **Используемое значение** (Used value). CSS-движок использует *отрисованный макет* (rendered layout), чтобы понять, как следует поступить с оставшимися значениями, зависящими от макета (например, `vh`, `vw`, `%`). Используемое значение считается точно (с плавающей точкой). Например, `359.6px`.
* **Фактическое значение** (Actual value) — финальное значение, готовое для использования в макете. Вычисляется на этапе этапе рендеринга страницы. Используемое значение может быть значением с плавающей точкой. Браузер не может разделить пиксели, поэтому округляет значение до целого `360px`.

### Перевод относительных величин и процентов в пиксели

`%` технически не является единицой измерения (unit).

Все величины переводятся в пиксели, поскольку страница состоит из них.

*Относительное значение переводится* в *пиксели относительно* *вычисленного значения* (computed value).
 
* `%` для шрифта (font) вычисляется относительно размера шрифта родительского элемента.
```css
.parent {
  font-size: 24px;
}
.child {
  font-size: 150%; /* 36px */
}
```
* `%` для `width`, `margin`, `padding` вычисляются относительно `width` родительского элемента, `height` — относительно `height` родительского элемента, `border` *не вычисляется* в *процентах*.
```css
.parent {
  width: 200px;
  height: 100px;
  border: 2px solid #000;
}

.child {
  width: 80%; /* 160px (от ширины родителя) */
  padding: 10%; /* 20px (от ширины родителя) */
  margin-top: 50%; /* 100px (от ширины родителя) */
  /* но */
  height: 50%; /* 100px (от высоты родителя) */
  border: 50% solid #000; /* 0px (не считается в процентах) */
}
```
* `em` для шрифта вычисляется относительно `font-size` родительского элемента. `1em` = `font-size` родителя.
```css
.parent {
  font-size: 16px;
}

.child {
  font-size: 4em; /* 64px */
}
```
* `em` для `width`, `height`, `margin`, `padding` вычисляется относительно `font-size` текущего элемента.
```css
.parent {
  font-size: 24px;
}

.child {
  font-size: 16px;
  height: 4em; /* 64px (от шрифта текущего элемента) */
  padding: 1em; /* 16px (от шрифта текущего элемента) */
}
```
* `rem` для всех свойств вычисляется относительно корневого (root) `font-size`. Браузер обычно по умолчанию задаёт корневой `font-size` в `16px`. Корневым элементом является `html`.
```css
.block {
  width: 10rem; /* 160px */
  font-size: 2rem; /* 32px */
}
```
* `vh` и `vw` для всех свойств вычисляются относительно высоты и ширины `viewport` соответственно.
```css
.block {
  width: 10vw; /* 160px */
  font-size: 10vh; /* 32px */
}
```

## Запуск JavaScript

JavaScript — блокирующий ресурс для отрисовки, поскольку может напрямую работать с DOM.

Если скрипт внешний, то файл сперва загружается, а потом запускается (evaluate).
```js
<script src="/main.js"></script>
```
Если скрипт встроенный, то файл сразу запускается.
```js
<script>console.log('Hi')</script>
```

Если скрипт как-то повлиял на объектные модели, то в них вносятся соответствующие изменения.

<!-- * файл загружается
* код парсится и строится Абстрактное Синтаксическое Дерево
* код преобразуется в машинный
* запускается машинный код -->

## Построение дерева рендеринга

Деревья DOM и CSSOM вместе объединяются в **дерево рендеринга** (Render Tree).  

*Алгоритм построения дерева рендеринга*   
1) На рассмотрение берутся вершины DOM.
2) Из рассмотрения *исключаются вершины*, которые не видны на странице: `<head>` со своим содержимым, `<script>` и прочие подобные.  
3) Для каждой *оставшейся вершины* находятся соответствующие наборы правил в CSSOM.  
4) Из рассмотрения *исключаются вершины*, которые *скрыты* при помощи *CSS*: имеют *объявление* `dispalay: none`.  
5) Каждая оставшейся вершина DOM вместе со своими наборами правил становится вершиной дерева рендеринга.  

*Вершина дерева рендеринга* называется **объектом рендеринга** (Render Object).  

В браузерах на движке *Gecko дерево рендеринга* может также называться **деревом фреймов** (Frame Tree), а *вершина дерева* - **фреймом** (frame) или **боксом** (box).

После построения дерева рендеринга браузер знает, какие объекты видны на странице и какие стили к ним нужно применить.

## Рассчёт макета (Layout, Reflow)
При **рассчёте макета** (Layout) *вычисляются расположение элементов на экране* и *занимаемое* ими *место*, то есть их *геометрия*.  

В первую очередь проверяется вьюпорт.  
Его значение берётся из соответствующего метатега, в случае его отсутствия браузером задаётся значение по умолчанию.  
```html
<meta name="viewport" content="width=device-width, initial-scale=1">
```
Значения высоты и ширины вьюпорта берутся как базовые, затем браузер обходит (traverse) дерево рендендеринга, начиная от его корня `<html>`, вычисляя все значения элементов: часто значения дочерних элементов зависят от родительских.

Пусть ширина вьюпорта составляет 800px.
```html
<body>
	<div class="parent">
		<div class="child"></div>
	</div>
</body>
```
```css
html {
  width: 50%; /* 400px, 50% от viewport */
}
body {
  width: 25%; /* 100px, 25% от html */
}
.parent {
  width: 100vw; /* 800px, 100% от viewport */
}
.child {
  width: 10%; /* 80px, 10% от parent */
}
```

После рассчёта макета каждый элемент на странице имеет свою блочную модель (box model), а все величины измерения переводятся в пиксели.  
## Прорисовка, отрисовка (Painting, Paint)

**Прорисовка** (Painting) – это процесс заполнения пикселей на экране.  
Иногда этот процесс называют **растеризацией, растрированием** (rasterisation, rasterization).

<!-- **Растеризация (информатика)** - один из методов рендеринга, при котором визуализация проводится проецированием объектов сцены на экран без сохранения эффекта перспективы -->

Он подразумевает вывод текста, цветов, изображений, границ и теней, по сути – всех визуальных частей элементов. Прорисовка обычно выполняется на нескольких поверхностях, которые называются слоями.



================================================
FILE: Bundling.md
================================================
# Сборка проекта (Bundling)

## Встряска дерева (Three Shaking)

**Встряска дерева** (англ. `three shaking`) - подход к сборке проекта, позволяющий добиться исключения "мёртвого кода" (англ. `dead code elimination`) - тех файлов (или методов в этих файлах), которые не задействованы в приложении. 

При этом подходе проект представляется в виде **дерева зависимостей**:
* Выбирается *лючевой, корневой* файл (`app.js`, `index.js`), с которого *начинается запуск приложения* (например, `node index.js`). Этот *файл* становится *корнем дерева* (англ `root`).
* *Корневой файл* чаше всего *имеет зависимости* (`require`, `import`) в виде *других файлов*, которые в *дереве зависимостей* *становятся* его *дочерними узлами*. Поскольку эти *файлы тоже* могут *иметь зависимости*, то и у них могут быть *дочерние узлы*. *Выстроив все цепочки зависимостей файлов*, *получаем* некоторый *граф*.
* Если некоторая *зависимость* уже была *внедрена* (импортирована), то *второй раз* она в итоговую *сборку не включается* - *берётся уже существующая ссылка на файл*. Это *позволяет исключить циклические зависимости*, а значит *граф можно представить* в виде *дерева*.
```js
/* пример циклической зависимости */
// a.js
require('b.js');
// b.js
require('a.js');
```
* Таким образом, те файлы в проекте, которые по каким-либо причинам нигде не были импортированы, не попадают в дерево зависимотей, а значит и в сборку проекта. Это позволяет уменьшить размер бандла, а значит ускорить загрузку страницы.
* Если учесть, что можно импортировать не только файлы, но и отдельные методы, то узлами дерева могут становиться эти самые методы. В таком случае неимпортированные методы не так же не попадут в сборку.

Отсюда можно сделать *вывод касательно названия*. Когда мы *трясём дерево* - то, что *держится за ветку не* достаточно *прочно*, - *падает* (яблоки, шишки, каштаны, листья, а в нашем случае - *"мёртвый код"*).

*Концепция "встряски дерева"* была *популяризирована* [Rollup](https://github.com/rollup/rollup#tree-shaking) и [Webpack](https://webpack.js.org/guides/tree-shaking/).

Рассмотрим *пример исключения неиспользуемых методов* из *сборки* (с *файлами* всё *аналогично*).

Допустим, есть некоторый *модуль* `utils.js` по типу `lodash`, *содержащий множество полезных утилит*.
```js
// utils.js
export const debounce = () => { /* ... */ };
export const throttle = () => { /* ... */ };
export const groupBy = () => { /* ... */ };
```
И есть *модуль* `app.js`, который *использует метод* `splitIntoChunks` из этого *модуля*. Пусть *другие модули не используют* `utils.js`.
```js
// app.js
import { groupBy } from 'utils';
```
В таком случае в *сборку попадёт только метод* `groupBy`, а *методы* `debounce` и `throttle` в неё *не попадут*.

## Разбиение на чанки




================================================
FILE: CSS.md
================================================
- [Основы CSS](#основы-css)
  - [Утверждения](#утверждения)
  - [Правило и его составляющие](#правило-и-его-составляющие)
  - [Типы элементов в CSS](#типы-элементов-в-css)
  - [CSS Box Model](#css-box-model)
  - [Типы отношений элементов в CSS](#типы-отношений-элементов-в-css)
  - [Селекторы в СSS](#селекторы-в-сss)
  - [Специфичность селекторов](#специфичность-селекторов)
  - [Наследование](#наследование)
  - [Каскад](#каскад)
- [Подходы к написанию CSS](#подходы-к-написанию-css)
  - [Проблемы CSS](#проблемы-css)
  - [Некоторые рекомендации](#некоторые-рекомендации)
  - [БЭМ](#бэм)
  - [OOCSS](#oocss)
  - [SMACSS](#smacss)
  - [Atomic CSS](#atomic-css)
  - [AMCSS](#amcss)
  - [Enduring CSS](#enduring-css)
- [Динамический CSS](#динамический-css)
  - [Добавление и удаление классов по условию](#добавление-и-удаление-классов-по-условию)
  - [CSS-in-JS](#css-in-js)
- [Вёрстка под различные девайсы](#вёрстка-под-различные-девайсы)
  - [Viewport](#viewport)
  - [Виды вёрстки](#виды-вёрстки)
- [Препроцессоры и постпроцессоры CSS](#препроцессоры-и-постпроцессоры-css)
  - [Препроцессоры](#препроцессоры)
  - [Постпроцессоры](#постпроцессоры)
  - [Возможности постпроцессоров в деталях](#возможности-постпроцессоров-в-деталях)
- [CSS Modules](#css-modules)

# Основы CSS
**Cascading Style Sheets**, **CSS** — *каскадные таблицы стилей*; *язык таблиц стилей* (stylesheet), используемый для *представления внешнего вида* HTML-документа. 

*Язык CSS* описывает, *как* элемент должен *отображаться* на экране.

*Документом* (`document`) называют *текстовый файл*, структурированный при помощи *языка разметки HTML*, *SVG* или *XML*.

## Утверждения

**Утверждение**, **заявление** (англ. `statement`) — *структурный элемент CSS*, который *начинается* с *любых непробельных символов* и *заканчивается первой закрывающей фигурной скобкой* или *точкой с запятой*.

Есть два *типа утверждений*:
* **Набор правил**, или проще: **правило** (англ. `ruleset`, `rule`).
* **At-правило** (англ. `at-rule`).

*Любое другое утверждение* считается *недопустимым* и *игнорируется*.

### Правило и его составляющие

**Правило** (англ. `rule`) — *CSS-утверждение*, состоящее из *селектора* (или *группы селекторов*) и *блока объявлений*.  

```css
selector1, selector2 /*, ...*/ {
   property1: value1;
   property2: value2;
   /* ... */
}
```

*Cелектор* (англ. `selector`) используется для *выбора элементов страницы*. Ниже приведены *примеры пяти селекторов*:
```css
#menu
.title
div
.parent .child
button:hover
```

*Два и более селекторов отделяются* друг от друга *запятыми* и образуют **группу селекторов**.
```css
p, span, h1
.sister, .brother
```

**Блоком объявлений** (англ. `declaration block`) называется *всё*, что находится *внутри фигурных скобок*. Обычно там находятся *объявления*.
```css
{
  property1: value1;
  property2: value2;
}
```
Например,
```css
{
  font-size: 14px;
  color: red;
}
```

**Объявлением** (англ. `declaration`) называется пара *свойство-значение* (англ. `property-value`).  
```css
propetry: value;
```

*Объявления отделяются* друг от друга *точками с запятой*. Если *пропущена точка с запятой*, то стили могут *примениться неправильно* или *не примениться* вовсе.
```css
{
  width: 50% /* отсутствие ";" - синтаксическая ошибка */
  height: 100%; /* данное правило и все последующие не будут применены, поскольку внутри этого блока объявлений выше имеется синтаксическая ошибка */
}
```

```css
.article-title, .header-title {
  color: #ccc;
  font-size: 18px;
}
```

*Блоки объявлений* могут быть *пустыми*.
```css
div {}
```

*Блоки объявлений* могут быть *вложенными*, если используются некоторые *at-rules* (например, `@media() {}`.
```css
@media (/*...*/) {
  div {
    /*...*/
  }
}
```

### At-правило

**At-правило** (англ. `at-rule`) — *CSS-утверждение*, *указывающее CSS*, *как себя вести*.  
Начинается с *символа @* (`at`, `at sign`) и *включает* в себя *весь следующий блок объявлений* или *весь код до* `;`.
```scss
@charset "utf-8"; /* определение набора символов */
@import 'custom.css'; /* импорт таблицы стилей (дополнительный запрос к серверу) */

/* медиазапросы, объявление CSS-правил в зависимости от девайса */
@media screen and (max-width: 1080px) {
  /* ... */
} 

/* объявление CSS-правил в зависимости от поддержки браузером */
@supports (display: grid) {
  /* ... */
} 

/* объявление и использование шрифта */
@font-face { 
  font-family: "Open Sans";
  src: url("/* ... */") format("/* ... */");
}
.className {
  font-family: "Open Sans";
}

/* объявление и использование анимации */
@keyframes fadeIn { 
  from {
    opacity: 0;
  }
  to {
     opacity: 1;
  }
}
.className {
  animation-name: fadeIn;
  animation-duration: 200ms;
}
```
*At-правила* могут быть *вложенны друг в друга*.
```scss
@supports (display: grid) {
  @media screen { /* ... */ }
}
```

## Типы элементов в CSS

### Блочные и строчные элементы

**Блочные элементы** — элементы высшего уровня (визуально выглядят как блоки), располагающиеся на странице *вертикально* и задающие её структуру. Они создают разрыв строки перед элементом и после него, образуя прямоугольную область, по ширине занимающую всю ширину блока-родителя. 

*Блочные элементы* могут содержать как *строчные*, так и другие *блочные* элементы.  
*Блочный* элемент `<p>` является *иключением* *не должен содержать* внутри себя другие *блочные* элементы (в том числе и *p*).

**Строчные (встроенные) элементы** используются для *форматирования текстовых фрагментов* (кроме `<area>`, `<img>`). Они *не формируют новые блоки контента*, *не создают разрыв строки* вокруг себя. Многие строчные элементы *не контролируют* поля, отступы, ширину и высоту. 

*Строчные элементы* могут содержать *только данные* и другие *строчные* элементы, они *не могут* содержать *блочные* элементы. (кроме *a*)

*Блочные* и *строчные* элементы являются *основными* элементами страницы. С них всё начиналось, и можно сделать практически что угодно, используя только их. 

Также есть **блочно-строчные** элементы, которые обладают смешанной характеристикой: являются встроенным, но могут задавать поля, отступы, ширину и высоту.

Сделать элемент *блочным*, *строчным* или *блочно-строчным* в *CSS* можно с помощью:
```css
.block { display: block; }
.inline { display: inline; }
.inline-block { display: inline-block; }
```

На данный момент существует много других типов элементов, все из которых отражает свойство `display` (`table`, `flex`, `grid` и прочие).

### Замещаемые и незамещаемые элементы

**Замещаемый** (replaced) является элемент, *представление* которого *выходит за рамки CSS*. *Стили CSS не могут влиять* на *содержимое замещаемых элементов* — *только* на их *позиционирование*. 

*Замещаемые элементы*.
* `<iframe>` — *содержит веб-страницу*, *не зависящую* от *родительской* страницы и её *стилей*.
* `<video>`
* `<img>`
* `<embed>`
* `<input type="image">`

*В некоторых случаях* являются *замещаемыми*.
* `<canvas>`
* `<audio>`
* `<option>`
* `<applet>`
* `<object>`

*Внешний вид* и *размеры замещаемых* элементов могут *определяться извне*.  
Например, *img без заданных* свойств `width` и `height`, *занимает* свой *естественный размер*.  
При *изменении* одного из этих свойств, второе высчитается *автоматически*, с учётом *пропорций*.

*Остальные элементы* можно отнести к **незамещаемым**.

## CSS Box Model

*Каждому HTML-элементу* соответствует *прямоугольная область*, называемая **боксом** (box).  
*Структура* этой области называется **блочной**, **боксовой моделью** (box model).
* **content** — *контент*, *содержимое*; *внутренняя область* элемента, содержащая *текст* или *другие элементы*.
* **padding** — *поля* элемента, *окружающие контент*. 
* **border** — *рамки* элемента, *окружающие поля* элемента.
* **margin** — *внешние отступы* (*пустое пространство вокруг* элемента), определяющие *расстояние* до *соседних* элементов.

*Каждая* из этих *составляющих* имеет какую-то *область* (area) и *границу* (edge).  

![css-box-model](/assets/css-box-model.png)

*Каждый бокс*, являясь *прямоугольником*, должен иметь *ширину* и *высоту*. 

Существует *два способа задать бокс*, от которых *зависят* его *ширина* и *высота*.
* `border-box` — `padding` и `border` входят в указанные в свойствах `width` и `height` ширину и высоту.
* `content-box` — `width` и `height` определяют *ширину* и *высоту* *только для контента* (content width, content height), `padding` и `border` *дополнительно увеличивают* размер *бокса*.
```css
/*
  высота бокса: 100px,
  высота контента: 100px - (16 * 2)px - (4 * 2)px = 60px
*/
.border-box {
  box-sizing: border-box;
  height: 100px;
  padding: 16px;
  border: 4px solid;
}
```
```css
/*
  высота контента: 100px,
  высота бокса: 100px + (16 * 2)px + (4 * 2)px = 140px
*/
.content-box {
  box-sizing: content-box;
  height: 100px;
  padding: 16px;
  border: 4px solid;
}
```

*Величина внешних отступов* может быть *отрицательной*, *величина остальных составляющих* блочной модели — *не может*.
```css
.class {
  margin-top: -16px;
}
```
*Внешние отступы прозрачны*, *остальные составляющие* могут быть *закрашены* в какой-то *цвет* (`border-color` для *рамок*, `background-color` для *полей* и *контента*.

## Типы отношений элементов в CSS

Подраздел относится скорее к *DOM* (представлению документа в виде дерева). Просто краткое напоминание, что даже у элементов есть отношения:
* **Элемент-предок (ancestor) и элемент-потомок  (descendant)**.  Элемент-потомок находится в элементе-предке, но при этом предок может не быть его родителем (потомок может быть вложен в другой элемент, также являющийся потомком для рассматриваемого предка). В примере ниже такая связь наблюдается только между `div` и `span`, `div` и `img`.

* **Родительский (parent) и дочерний элементы (child)**.  Дочерний элемент находится в родительском и других уровней вложенности между ними нет. Ниже такая связь только между  `div` и `p`, `div` и `a`, `p` и `span`, `a` и `img`.

* **Элементы-братья (sibling)**.  Должны иметь *одного* и *того же* *родителя*. Называются **смежными** (adjacent), если *следуют прямо друг за другом*. Ниже братьями (причём смежными) являются только `p` и `a`. Элемент `a` называется **следующим** (following) **за элементом** `p`, `p` — **предшествующий** (preceding) **элементу** `a` (понятия *следования* и *предшествия* определены *только* для *смежных* элементов).

* **Остальные элементы**. Хоть они и являются *дальними родственниками* (как *минимум один предок* — *корень дерева*), считаются *безотносительными друг к другу*. Ниже такими являются `span` и `img`, `p` и `img`, `span` и `a`.

```html
<div>
  <p>
    <span></span>
  </p>
  <a>
    <img />
  </a>
</div>
```

## Способы разметки

**Способ разметки в CSS** (CSS layout mode, layout) — алгоритм, определяющий позицию и размер блоков на основании того, как они взаимодействуют с их родственными блоками (ancestor, sibling).

**Содержащий блок** (containing block) — *предок рассматриваемого элемента*, который может *влиять* на его *размер* и *позицию*.  Чаще всего *содержащим блоком* является *элемент-родитель*, но *не всегда*.
  
**Начальный содержащий блок** (initial) — `<html>`. *Размеры начального блока зависят* от *viewport*.  

Типы разметки:
* **Нормальная, обычная** (normal flow) — блочная разметка для блочных элементов (`<div>`, `dispalay: block`) и линейная разметка (`<span>`, `display: inline`) для строчных элементов. Используется по умолчанию.
* **Табличная** (table) для построения таблиц (`<table>`, `<tr>`, `<th>`, `display: table`).
* **Float-разметка** для размещения элемента слева или справа при том, что весь остальной контент оборачивает этот элемент в порядке норамальной разметки. (`float: left`, `float: right`).
* **Позиционированная**.  
**Позиционированный элемент** (positioned) — элемент, имеющий вычисленное значение `relative`, `absolute`, `fixed` и прочие (но не `static`) в свойстве `position`.  
Любой элемент по умолчанию имеет `position: static`, не считается позиционированным и не реагирует на свойства `top`, `right`, `bottom`, `left`, `z-index`.  
**Относительно позиционированный элемент** (relatively) — элемент, значения свойств `top`, `bottom` и `left`, `right` которого определяют вертикальное и горизонтальное смещения (offset) от нормального позиционирования соответственно (`position: relative`).  
**Абсолютно позиционированный элемент** (absolutely) — элемент, значения свойств `top`, `right` `bottom`, `left` определяют смещения от границ содержащего блока (при `position: absolute` это ближайший позиционированный элемент-предок, при `position: fixed` — начальный содержащий блок). Абсолютно позиционированный элемент удаляется из нормальной разметки документа и не занимает в ней никакого места. Margins добавляются к смещению.
* **Flexbox-разметка** (flexible box).
* **Сеточная** (grid).

## Селекторы в СSS

*Селектор* (selector) используется для *выбора* (select) *элементов страницы*, к которым мы хотим *применить стили*.

Селекторы *подразделяются* на *простые* и *составные*. В них также включаются *псевдоклассы* и *псевдоэлементы*.

### Простые селекторы

**Селектор по типу** выбирает элементы по *типу* узла.
```css
div, p, span, a { }
```
**Селектор по классу** выбирает элементы, соответствующие *атрибуту  class*.
```css
.classname {}
```
**Селектор по id** выбирает элементы, соответствующие *атрибуту  id*.
```css
#idname {}
```
**Универсальные селекторы**  выбирают элементы *всех типов*.
```css
* {}
```
**Селекторы атрибутов**  выбирают элементы на основании *наличия* указанного *атрибута*, а также *соответствия значения атрибута*, если оно указано.
```css
[attr operator value i] {}

a[href] {} /* <a> элементы с атрибутом href */

a[href="https://qq.com"] {}  /* ...точно соответствующим "https://qq.com" */

a[class^="qq"] {} /* ...начинающимся с подстроки 'qq' */

a[href*="qq"] {} /* ...содержащим подстроку 'qq' */

a[href*="qq" i] {} /* ...содержащим подстроку 'qq' (case insensitive благодаря флагу 'i') */

a[href~="qq" i] {} /* ...содержащим целое слово 'qq' (значение атрибута состоит из слов и пробелов) */

a[href$=".com"] {} /* ...заканчивающимся подстрокой '.com' */
```

### Составные селекторы и комбинаторы

**Составной селектор** *состоит* из *двух* или *более простых селекторов*, между которыми могут быть *комбинирующие операторы* — **комбинаторы** (combinators).

В *комбинаторах приоритеты* и *правила группировки операторов* при выборке элементов *отсутствуют*, поэтому *составление выборки* осуществляется *строго справа налево*, от одного простого селектора к другому. 

**Комбинатор потомков (descendant) A B**  выбирает элементы, соответствующие *селектору B* и имеющие предка, соответствующего *селектору A*. 

*Комбинатор потомков* представлен *одним* или *несколькими пробельными символами* (возможно, с переходом на новую строку или даже с комментариями). 
```css
.grandfather .son {}
.father .son {}
```

*Комбинатор потомков сработает* лишь в том случае, если между селекторами *нет других комбинаторов*. В следующем *примере* он *не применяется*, поэтому строки *эквивалентны*.
```css
 .father>.son {}
 .father > .son {}
```

Случай, когда *селекторов больше двух* (например, *C A B*), означает лишь наличие *дополнительного условия*. Помимо сказанного выше про *A B*, у искомых элементов должен также имееться *элемент-предок*, соответствующий *селектору C*.
```css
.grandfather .father .son {}
```
Это точно *так же работает* как для *большего числа селекторов* (по индукции), так и для *комбинаторов других типов*.

**Комбинатор детей (child) A > B** является *частным случаем комбинатора потомков*. Он выбирает *элементы*, соответствующие *селектору B* и имеющие *родительский элемент*, соответствующий *селектору A*.
```css
.grandfather > .father > .son {}
.father > .son {}
```
**Комбинатор смежных братьев (adjacent sibling, next sibling) A + B** выбирает элементы, соответствующие *селектору B*, являющиеся *смежными братьями* с элементами, соответсвующими *селектору A*, и *следующие прямо за ними*.


**Комбинатор общих братьев (general sibling, subsequent sibling) A ~ B** выбирает элементы, соответствующие *селектору B*, являющиеся *братьями* с элементами, соответсвующими *селектору A*, и *следующие за ними* (*не обязательно прямо*, *между ними* могут быть и *другие братья*).

Рассмотрим несколько *примеров*, чтобы понять, *как* работают *комбинаторы братьев*:
```html
<div class="row">
   <div class="cell white"></div>
   <div class="cell"></div>
   <div class="cell white"></div>
   <div class="cell"></div>
</div>
<style>
  .row { display: flex; width: fit-content; border: 1px solid; }
  .cell { width: 50px; height: 50px; }
</style>
<style>
  .white + .cell { background: black; } /* Рис. 1 */
  .cell + .cell { background: black; } /* Рис. 2 */
  .white + .white { background: black; } /* Рис. 3 */

  .white ~ .cell { background: black; } /* Рис. 4 */
  .cell ~ .cell { background: black; } /* Рис. 5 */
  .white ~ .white { background: black; } /* Рис. 6 */
</style>
```

![sibling-combinators](/assets/sibling-combinators.png)

*Рис. 1* Каждое *поле*, *следующее* за *белым* (*.white*), окрашивается в *чёрный*.  
*Рис. 2* Каждое *поле*, *следующее* за *другим полем*, окрашивается в *чёрный* (все, кроме первого).  
*Рис. 3* *Не найдено белых полей*, *следующих* за *белыми*.  
*Рис. 4* Каждое *поле*, если *ранее* встречалось хоть *одно белое*, окрашивается в *чёрный*.  
*Рис. 5* Каждое *поле*, если *ранее* встречалось хоть *одно поле*, окрашивается в *чёрный*.  
*Рис. 6* Каждое *белое поле*, если *ранее* встречалось *хоть одно белое поле*, окрашивается в *чёрный*.  
Всё это *работает* так, потому что *все поля имеют* один *класс* `cell` и у них *нет братьев другого класса*.  

Стоит отметить, что *комбинаторы* (не считая *комбинатора потомков*) используются *крайне редко*, так как могут стать *причиной* *довольно непредсказуемого поведения* кода, поскольку при их использовании *добавление новых блоков* будет *сказываеться* на стилях *других блоков*, что может быть особенно заметно, когда в ход вступает *специфичность*.

Если между селекторами *ничего не стоит* **AB**, то происходит их **объединение**: выбираются элементы, *соответствующие обоим* селекторам *одновременно*.
```css
div.white {}
.white.cell {}
div#body {}
p.title {}
```

Если *хотя бы одна часть селектора* написана *неправильно*, то этот *селектор полностью игнорируется*, на другие селекторы это никак не влияет.

Если между селекторами *стоит запятая* **A, B** то они *не связаны* между собой *выбором элементов* (в этом случае выборка осуществляется для каждого селектора в отдельности), но ко всем найденным элементам применется *один набор стилей*. Такой тип связи называется **группой селекторов**.

### Псевдоклассы и псевдоэлементы

**Псевдоэлемент** — *ключевое слово*, добавляемое к селектору, позволяющее *стилизовать определённую часть* выбранного *элемента* (например, первоя строка). 

*Псевдоэлементы* также позволяют контролировать элементы, находящиеся *за пределами документа*; позволяют ссылаться на *недоступную* для получения *иным путём* информацию (например, полоса прокрутки). Они должны всегда *начинаться с двух двоеточий*, хоть в большинство браузеров их разрешено использовать и с одним для обратной совместимости:
```css
.field::placeholder { color: #8ab7e5 } /* изменение цвета замещающего текста для input и textarea */
p::before, p::after { content: "♥"; } /* вставка сердечка перед контентом каждого блока p и после него */
p::first-line { color: #8ab7e5 } /* изменение первой строки блочного элемента (в данном случае p) */
p::first-letter { font-size: 24px; } /* изменение первой буквы блочного элемента (в данном случае p) */
*::selection { background: #8ab7e5 } /* изменение цвета выделения текста */
*::-webkit-scrollbar-thumb { background: #8ab7e5 } /* изменение цвета ползунка полосы прокрутки в браузерах на движке webkit */
```

**Псевдокласс** — *ключевое слово*, добавленное к селектору и *определяющее его состояние*. 

*Псевдоклассы* всегда *начинаются с двоеточия*. Некоторые из них могут принимать *параметры*.
```css
input:focus {} /* любое поле ввода, находящееся в фокусе */
option:checked {} /* каждая отмеченная опция */
button:disabled {} /* каждая недоступная кнопка */
link:not(:visited) {} /* каждая непосещённая ссылка */
*:hover {} /* любой элемент при наведении на него */
```

Если *псевдокласс* или *псевдоэлемент* применяются *ко всем элементам*, то не рекомендуется пропускать *универсальный селектор* **\***, иначе может возникнуть путаница.  
Селекторы ниже *похожи*, но выбирают *совершенно разные* элементы:  
```css
 div:hover {} /* любой блок div, на который наведён курсор */
 div :hover {} /* ~ div *:hover (любой элемент, на который наведён курсор, располагающийся в блоке div) */
```

С помощью *псевдоклассов* также можно работать с *родительскими и дочерними* элементами:
```css
div:empty {} /* каждый блок без дочерних элементов */
div:not(:empty) {} /* каждый блок, имеющий дочерние элементы */
div:first-child {} /* каждый блок, являющийся первым ребёнком */
div:nth-child(3) {} /* каждый блок, являющийся третьим по счёту ребёнком */
div:last-child {} /* каждый блок, являющийся последним ребёнком */
div:only-child {} /* каждый блок, являющийся единственным ребёнком */
```

*Псевдоклассы* могут идти *последовательно* друг за другом:
```css
div:first-child:last-child /* эквивалентно :only-child */
```

Стоит также отметить, что некоторые *псевдоклассы* совпадают с *атрибутами* элементов, поэтому их можно заменить *селектором атрибутов*. Следующие строки *эквивалентны*:
```css
button:disabled {}
button[disabled] {}
```

### Применение CSS-селекторов в JavaScript (Selectors API)

*Selectors API* предоставляет методы, с помощью которых можно быстро и просто получить список узлов документа путем сопоставления с *группой селекторов*.
```javascript
// возвращает первый найденный Element или null, если совпадений не найдено
const element = parentNode.querySelector('selectors'); 
// возвращает NodeList, содержащий все найденные элементы, или пустой NodeList
const elementList = parentNode.querySelectorAll('selectors'); 

const root = document.querySelector('#root');
const articleTitles = root.querySelectorAll('.article > .title');
```
Параметр *selectors*  всегда  должен быть *валидной строкой CSS-селекторов*, в противном случае выдаётся исключение *SyntaxError*.

## Специфичность селекторов
**Специфичность селекторов** (selector's specificity) определяет *приоритетность селекторов* в таблице стилей. Чем *специфичнее* селектор, тем *выше* его *приоритет*. 

Eсли в выборку *несколькими селекторами* попадают одни и *те же элементы* и затрагиваются одни и *те же свойства в блоках объявлений*, в конечном счёте *применяются значения свойств* из объявлений *более специфичного селектора*.  

*Специфичность* представлена *четырьмя числами*, записанными *через запятую* `A,B,C,D`.

### Правила специфичности
* *Атрибут style* (*inline-стиль*). Специфичность: `1,0,0,0`.
* *Атбрибут id*. Специфичность: `0,1,0,0`.
* *Атбрибут class*, *все остальные атрибуты* (кроме `id`) и *псевдоклассы* (кроме `:not`). Специфичность: `0,0,1,0`.
* *Типы элементов* и *псевдоэлементы*. Специфичность: `0,0,0,1`.
* *Универсальные селекторы* и *псевдокласс* `:not`. Специфичность: `0,0,0,0` (*нулевая специфичность*).

В *составных селекторах* *каждое* из *четырёх чисел* специфичности *суммируется отдельно*. 

*Комбинирующие операторы не влияют* на *специфичность*.
```css
 * body #root .container ul > li:last-child::after { content: "."; }
/* 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 */
```

В *группе селекторов специфичность* для *каждого селектора* считается *отдельно*.
```css
.cell.white, /* 0,0,2,0 = 0,0,1,0 + 0,0,1,0 */
div /* 0,0,0,1 */
{ background: #000; } 
```

Стоит отметить, что *порядок расположения стилей в class-атрибуте не важен*. В примере ниже *результат одинаковый*:
```html
<p class="title text"></p>
<p class="text title"></p>
```

### Почему специфичность нельзя представить одним числом

В *примере* ниже *элемент* `div` *выбирается двумя селекторами*, *первых* из которых представлен *одним атрибутом* `id`, а *второй* — *11 атрибутами* `class`. 
```html
<div id="identifier" class="a b c d e f g h i j k"></div>
```
```css
div {
  width: 100px;
  height: 100px;
}

#id {
  background: red; /* 0, 1, 0, 0 (применится это правило) */
}

.a.b.c.d.e.f.g.h.i.j.k {
  background: green; /* 0, 0, 11, 0 */
}
```
*Если бы специфичность суммировалась* в *одно число*, то *11 атрибутов* `class` *переопределяли* бы стили *одного* `id` (`11 * 10 > 100`), *11 атбитуров* `id` *переопределяли* бы *inline-стиль* (`11 * 100 > 1000`), *11 элементов* переопределяли бы один `class`, но на самом деле *этого не происходит* и *не должно происходить*. Поэтому *числа специфичности должны суммируются отдельно*.

## Наследование

**Наследование** (Inheritance) — *автоматическая передача некоторых свойств (объявлений) элемента-предка* его *потомкам*. В этом случае *потомок наследует некоторые свойства предка*.

Механизм *наследования* позволяет разработчикам писать *меньше кода*. *Наследуются не все свойства*, поскольку *иначе наследование* бы только *усложнило написание CSS*.

*Наследуются свойства*, задающие *параметры отображения текста* (все `font-*`, большинство `text-*`), *произношения текста вслух* (`speak-*`) и *отображения списка* (`list-*`), также *наследуются* `color`, `visibility`, `cursor`, `letter-spacing`, `line-height`, `white-space`, `border-collapse`, `border-spacing` и другие, менее популярные свойства.

*Почти все остальные* свойства *не наследуются*. Среди них: *свойста блочной модели* (`border-*`, `margin-*`, `padding-*`, `*-width`, `*-height`), *позиционирования* (`position`, `top`, `left`,`right`, `bottom`, `float`), `background-*`, `transform`, `display`, `z-index`, `text-decoration` и другие. 

```html
<div>
  <p>Notes</p>
</div>
```
```css
div {
  font-size: 24px;
  color: red;
}
```
Текст в примере выше, располагающийся в элементе `<p>`, будет *красным*, поскольку *свойства* `font-size` и `color` *наследуются* от `div`.

*Наследуемое объявление не имеет специфичности* (specificity) и *важности* (importance), о которой будет рассказываться дальше, поэтому его может *перекрыть* даже *правило* с *универсальным селектором* `*`, имеющим *нулевую специфичность*, или *любое объявление*, *по умолчанию* указанное в *стилях браузера* (user-agent declaration).

Если добавить *следующее правило* в примере выше, то текст станет *чёрным* несмотря на то, что *специфичность* селектора `*` — `0,0,0,0`, а селектора `div` — `0,0,0,1`.
```css
* {
  color: #000;
}
```

*Наследуются не объявленные значения* (declared values), а *вычисленные значения* (computed values).
```css
.parent {
  font-size: 24px;
  line-height: 2em; /* 48px (считается от font-size текущего элемента) */
}

.child {
  font-size: 36px;
  /* в line-height унаследуется не объявленное значение 2em,  
  а вычисленное 2em от 24px = 48px */
}
```

*Наследуемое свойство* от *ближнего предка перекрывает* наследуемое от *дальнего*. Если бы мы вместо `*` использовали `body`, то цвет текста остался бы *красным*, поскольку `div` находится ближе к `p`, чем `body`, и тоже определяет свойство `color`.
```css
body {
  color: #000;
}
```

*Наследование тесно связано* с *древовидной структурой* *объектной модели CSS*.

*Наследования обусловлено внутренней реализацией языка*. Оно заложено *не конкретными свойствами*, заданными *по умолчанию* в стилях, а самим *поведением наследуемых свойств*.

Чтобы *явно унаследовать значение родительского свойства*, можно использовать *значение* `inherit`.
```css
p {
  color: inherit;
}
```
Также есть *значения* `initial` и `unset`. *Первое* устанавливает *значение*, *по умолчанию* заданное *браузером*, а в случае его *отсутствия* работает как `inherit`, если *свойство наследуемое* (naturally inherited). *Второе* — *наоборот*: *наследует*, если *свойство наследуемое*, в противном случае устанавливает *значение браузера*.

Использование `unset` в примере выше установит *красный* цвет, `initial` — *чёрный*.

## Каскад

*Cascading Style Sheets* (CSS) переводится как *каскадные таблицы стилей*. *Ключевым словом* выступает *каскад*.

Если несколько селекторов ссылаются на один и тот же DOM-элемент, а в CSS-правилах, привязанных к этим селекторам, затрагивается одно и то же свойство (например, `width`, `color`), то возникает конфликт: браузеру нужно решить, какое из правил следует применить к элементу, какое из них важнее (приоритетнее). Для этого существует специальный алгоритм, с которым мы познакомимся далее.

Пример конфликта свойства `color`:
```html
<div id="foo" class="bar" />
```
```css
.bar {
  color: white;
}
#foo {
  color: red;
}
div {
  color: white;
}
```
Конкретно в данном примере после всех рассчётов стилей будет применён цвет `red`. Будем разбираться, почему так.

**Каскадом** (англ. `cascade`) или **каскадированием** (англ. `cascading`) называют *процесс группировки всех *таблиц стилей*, полученных из разных источников и применяемых к элементу, и *разрешения конфликтов* между *CSS-правилами* (как в примере выше.)


Все объявления *трёх селекторов* ниже будут применены к блоку с классом `element`.
```html
<div class="element"></div>
<div class="another-element"></div>
```
```css
div.element {
  width: 200px;
  background: #000;
}
.element {
  width: 100px;
}
.another-element, .element {
  color: #fff;
}
```
Объявления `width: 100px` и `width: 200px` *конфликтуют* друг с другом. *Решение* о том, какое из них *применится* в конечном счёте, зависит от *приоритета*, об *определении* которого рассказывается *ниже*.

### Возможные источники стилей (браузер, автор и пользователь)

*Таблицы стилей* (англ. `style sheets`) могут иметь *3* разных **источника** (англ. `origins`): *стили браузера*, *автора* и *пользователя*.

#### Стили браузера

**Стили браузера**, **объявления браузера** (англ. `user-agent declarations`, `browser declarations`) — это *стили*, которые *задаются по умолчанию браузером*.

Вы можете воспринимать *стили браузера* как *стили по умолчанию*, поскольку они *работают даже тогда*, когда *ваш HTML-документ не содержит CSS-объявлений*.

![image](https://user-images.githubusercontent.com/22237384/164483287-d34c5695-09ab-4483-9494-8f27df1e1f85.png)

Эти *стили невозможно отключить*, но их очень *просто перезаписать*.


Некоторые *стили браузера* можно найти в *папке* с *файлами браузера*.
![image](https://user-images.githubusercontent.com/22237384/164482291-c97f48cb-69eb-4bd7-9d5c-c0621030946a.png)

<!-- Что чаще всего отличается стилями между браузерами, так это псевдоэлементы по типу `::scrollbar`, `::selection` и так далее. -->

Пример *стилей браузера*: *ссылки* `<a>` имеют *синий цвет* и *подчёркнуты* (англ. `underlined`).

#### Как добиться консистентности в разных браузерах

*Каждый браузер* задаёт *свои стандартные стили*, поэтому *страница* может *отображаться* в браузерах *по-разному* и разработчики часто *подключают файлы* вроде `reset.css` или `normalize.css`, чтобы *сбросить стили браузера*.  

#### Стили автора

**Стили автора**, **объявления автора** (англ. `author declarations`) — стили, *подключенные к HTML-документу* через тэг `<style>` и указанные в этом тэге либо напрямую, либо по ссылке на CSS-файл.

Очевидно, что в данном случае под *автором* подразумевается *разработчик* (англ. `developer`) данного *сайта*, который писал код *HTML-документа*.

#### Самописные стили в Chrome Dev Tools

*Стили*, которые вы самостоятельно *добавляете* в *Chrome Dev Tools*, также относятся к *стилям автора* и *имеют одинаковый приортитет* с теми стилями, что написал *разработчик HTML-документа*. 
*Ваши рукописные стили добавляются* в *новый файл* `inspector-stylesheet`, который *создаёт* сам *Chrome*.
![image](https://user-images.githubusercontent.com/22237384/164485009-991603ce-d03c-49e8-9316-8a7dabe21700.png)

*Стили*, *добавленные* в *блок* `element.style {}` в *Chrome Dev Tools*, также считаются *стилями автора* и *задаются напрямую* в *атрибуте* `style` *выбранного HTML-элемента*.  
![image](https://user-images.githubusercontent.com/22237384/164488974-8ec1189f-01e4-41e7-bdf2-c2ff47a7e2ff.png)


#### Стили пользователя

**Стили пользователя**, **объявления пользователя** (англ. `user declarations`, `user styles`) — стили, которые *пользователь* указывает в *настройках браузера*.

Ранее *пользовательские стили* можно было объявить в файле `Custom.css`, который можно было найти в папке с файлами браузера, но разработчики решили отключить эту возможность. 
![image](https://user-images.githubusercontent.com/22237384/164477719-f222881e-adba-45c7-ad10-c6d98d2ee7fb.png)

В наше время в качетве *альтернативы файлу Custom.css* сейчас могут *использоваться* различные *расширения Chrome* (англ. `Chrome extensions`). Например, [*расширение User CSS*](https://chrome.google.com/webstore/detail/user-css/okpjlejfhacmgjkmknjhadmkdbcldfcb?hl=en).

<!-- Поскольку *стили пользователя *настраивались** довольно *редко*, к тому же делать это было *не* слишком *удобно*, *разработчики Chrome отключили эту возможность* очень много лет назад. -->

Раньше же пользовательские стили можно было объявить следующим образом:
1) Заходим сюда: chrome://version/
2) Копируем `Путь к профилю` и открываем в проводнике.
![image](https://user-images.githubusercontent.com/22237384/164478524-130fceff-7982-444d-9fb0-0f8b8c978be9.png)
3) Находим папку `User StyleSheets` и в ней файл `Custom.css` (сейчас вы его уже не найдёте, а если добавите, то он учитываться не будет).
4) Изменяем или добавляем новые CSS-объявления в файл.

*Пример файла* [Custom.css](https://gist.github.com/colinangusmackay/5273896)


### Алгоритм (порядок) каскада
- [1) Важность (Importance)](#1-важность-importance)
- [2) Специфичность (Specificity)](#2-специфичность-specificity)
- [3) Порядок источника (Source Order)](#3-порядок-источника-source-order)

Будем *рассматривать каскад* относительно *одного HTML-элемента*.

1) Сперва *находятся все CSS-объявления* (англ. `declarations`), принадлежащие *рассматриваемому элементу*.
2) Если *несколько правил конфликтуют* друг с другом, то *сравнивается* их *источник* (*важность*). *Применяется объявление из более приоритетного источника*.
3) Если *важность совпадает*, то *сравнивается специфичность*. *Применяется объявление с более специфичным селектором*.
4) Если *важность* и *специфичность совпадают*, то *сравнивается порядок источника*. *Применяется объявление*, которое *объявлено позже* в *коде*.



#### 1) Важность (Importance)
Производится *упорядочивание объявлений* по **важности** (англ. `importance`), зависящей от *источника* (origin).
Чем *ниже по списку ниже*, тем *выше приоритет*.
  1) Объявления *браузера* (user-agent declarations).
  2) Объявления *пользователя* (user normal declarations).
  3) Объявления *автора* (author normal declarations).
  4) *CSS Animations*
  5) Объявления *автора* с добавлением *ключевого слова* `!important` (author important declarations).
  6) Объявления *пользователя* с добавлением *ключевого слова* `!important` (user important declarations).
  7) Объявления *браузера* с добавлением *ключевого слова* `!important` (user-agent important declarations).
  8) *CSS Transitions*  

*Важность повышается* за счёт *ключевого слова* `!important`.
```css
* {
  box-sizing: border-box !important;
}
```
Если *два конфликтующих правила* содержат `!important`, то *применится более специфичное* из них.
```css
#article { padding: 24px !important; } /* применится это правило: id специфичнее класса */
.article { padding: 16px !important; } 
```

Если к одному правилу ключевое слово `!important` применен дважды (`!important!important`), то важность данного правила не только не изменится, но произойдёт синтаксическая ошибка и правило просто не применится.
```css
#article { padding: 24px !important; }
#article { padding: 24px !important!important; } // синтаксическая ошибка "semi-colon expected" (ожидается ";")
```

#### 2) Специфичность (Specificity)
Если *важность* и *источник совпадают*, то сравнивается **специфичность селекторов** (selector's specificity), которым принадлежат *объявления*.  
В примере ниже *применится* объявление `width: 200px`, поскольку его *селектор специфичнее* (подробнее об этом будет дальше).
```css
div.element { /* специфичность: 0,0,1,1 */
  width: 200px; /* применится это объявление */
  background: #000;
}
.element { /* специфичность: 0,0,1,0 */
  width: 100px;
}
```

#### 3) Порядок источника (Source Order)
Если *важность*, *источник* и *специфичность совпадают*, приоритет объявлений зависит от их *расположения в коде*, **порядка источника** (source order). *Объявленный позже блок кода* (правило, объявление) имеет *больший приоритет*. Это *работает так же*, если *правила* находятся в *разных CSS-файлах*: *приортитетнее* считается то *правило*, чей *файл подключен позже*.
```css
/* два правила */
p {
  color: green;
}

p {
  color: red; /* применится это объявление (важность, источник, специфичность совпадают и объявлено позже) */
}
```
```css
/* два объявления */
.article {
  color: green;
  color: red; /* применится это объявление */
}
```
```html
<!-- два файла -->
<link href="styles.css" rel="stylesheet" />
<!-- правила (и их объявления) в файле ниже приоритетнее -->
<link href="styles2.css" rel="stylesheet />
```

*Значение*, полученное в *результате каскада*, называется **каскадным значением** (англ. `cascaded value`).


#### Хороший пример проверки знаний каскада

При вычислении каскада сохраняется не только последнее значение, но вся цепочка значений по их приоритету. Таким образом, если самое приоритетное правило будет отключено (например, при помощи JavaScript или Dev Tools в браузере), то вместо него применяется второе по значимости правило. Сейчас разберём большой пример и отсортируем правила в порядке их приоритетности для браузера.
```html
<div id="a" class="a" style="color: pink">
  Notes
</div>
```
```css
/* файл со стилями автора */
#a {
  color: red;
}

.a {
 color: orange;
}

#a#a#a {
  color: yellow;
}

#a#a#a {
  color: green !important!important;
}

#a {
  color: lightblue !important;
}

.a {
  color: blue !important;
}

#a {
  color: purple !important;
}

div {
  color: blue;
}

html {
  color: yellow;
}
```
Начнём с атрибута `style`, далее пойдём по порядку. Будем записывать селекторы в порядке их приоритетов в массив и сортировать его по убыванию приоритета правил.

Для начала предположим, что атрибут `style` и весь `css` в примере располагаются в коде программы, то есть они написаны программистом (автором) и имеют одинаковый источник, если к правилам не применяется ключевое слово `!important`. 

1) Атрибут `style` имеет специфичность `1, 0, 0, 0`. Пока не с чем его сравнивать, просто добавляем в массив: [`style`].
2) Селектор `#a` имеет тот же источник, что и `style`. Переходим к рассмотрению (вычислению) специфичности (второй этап алгоритма): `0, 1, 0, 0`. Специфичность ниже, чем у `style`. Значит массив приоритетов: [`#a`, `style`].
3) Селектор `.a` имеет тот же источник, а его специфичность: `0, 0, 1, 0`, что ещё ниже, значит имеем массив: [`style`, `#a`, `.a`].
4) Селектор `#a#a#a` имеет тот же источник, а его специфичность: `0, 0, 3, 0`, что ещё выше `#a` и ниже `style`: [`style`, `#a#a#a`, `#a`, `.a`].
5) Селектор `#a#a#a!important!important` имеет неправильный синтаксис для правила (поскольку нельзя ключевое слово `!important` применить дважды к одному правилу), поэтому правило просто не применится и в массив его добавлять нет смысла. 
6) Селектор `#a!important` имеет источник `author + important`, что выше всех предыдуших правил, его специфичность: `0, 1, 0, 0`. Имеем: [`#a!important`, `style`, `#a#a#a`, `#a`, `.a`].
7) Селектор `.a!important` имеет источник `author + important`, поэтому он приоритетнее, чем 1)-5). При этом его специфичность: `0, 0, 1, 0`, что меньше, чем у 6), а значит массив примет вид: [`#a!important`, `.a!important`, `style`, `#a#a#a`, `#a`, `.a`].
8) Селектор `#a!important-2` совпадает с селектором 6), поэтому имеет тот же источник и ту же специфичность, то при этом позже объявлен в коде, а значит считается более приоритетным. Тогда имеем: [`#a!important-2`, `#a!important`, `.a!important`, `style`, `#a#a#a`, `#a`, `.a`].
9) Селектор `div` имеет источник автора и его специфичность `0, 0, 0, 1`, значит он ниже по приоритету, чем все предыдущие селекторы: [`#a!important-2`, `#a!important`, `.a!important`, `style`, `#a#a#a`, `#a`, `.a`, `div`]
10) Селектор `html` является родителем всех элементов, а значит и элемента, который мы рассматриваем, при этом свойство `color` относится к свойствам текстовых полей, а значит является наследуемым. Наследование не имеет специфичности, поэтому любой из селекторов выше его перебивает по приоритету. Унаследование свойства происходит лишь в том случае, если нет ниодного правила, которое задаёт это свойство напрямую элементу. Таким образом, наследование можно добавить в самый конец нашего массива: [`#a!important-2`, `#a!important`, `.a!important`, `style`, `#a#a#a`, `#a`, `.a`, `div`, `html`].

Как можно увидеть, к элементу применится правило `#a!important-2`, то есть фиолетовый цвет (`color: purple`).


### Скорость выполнения селекторов браузерами

Обработка некоторых селекторов занимает в несколько раз больше (в 5-6 раз) времени, чем других селекторов.  
Тем не менее, сейчас в браузерах настолько всё оптимизировано, что данная проблема является одной из наименьших из существующих.  

В интернете можно найти множество тестов, которые показывают, что при тысячах элементов на странице даже достаточно сложные селекторы обрабатываются считанные миллисекунды.

# Подходы к написанию CSS

- [Проблемы CSS](#проблемы-css)
- [Некоторые рекомендации](#некоторые-рекомендации)
- [БЭМ](#бэм)
- [OOCSS](#oocss)
- [SMACSS](#smacss)
- [Atomic CSS](#atomic-css)
- [AMCSS](#amcss)
- [Enduring CSS](#enduring-css)

## Проблемы CSS

*CSS* изначально создавался для стилизации элементов HTML-разметки и ни для чего более. Он обладает огромной гибкостью, позволяя решить любую задачу множеством способов. 

* Обращение к элементам происходит при помощи селекторов. Существует *множество способов обратиться* к одному и *тому же элементу*.
```html
<div class="header">
  <p id="header-title" class="title"></p>
</div>
```
```css
#header-title {}
.title {}
[class="title"]
p {}
* {}
```
* Многие из этих *способов* можно *комбинировать*.
```css
.title#header-title
p[class="title"]
p.title {}
.header > * {}
.header #header-title {}
```
* Один селектор может выбирать несколько различных элементов на странице.
```css
<div class="header">
  <p class="title"></p>
</div>

<div class="article"></div>
  <span class="title article-title"></span>
</div> 
```
```css
.title {} /* выберет оба элемента span и p */
div {} /* выберет и header, и article */
```
* *CSS-правила* могут *конфликтовать* друг с другом и эти *конфликты решаются каскадом*, но *разработчик* всё время должен *знать важность*, *специфичность* и *место в коде* для *каждого объявления*.

* Всё это усугубляется *отсутствием модульности кода*. Имеется *только глобальная область видимости*. 

Учитывая всё сказанное выше, возможны случаи, когда *стили нового* элемента страницы *влияют* на *стили* уже *существующего* элемента, или, наоборот, *новый* элемент *зависит* от стилей *существующего*. 

Такой код *трудно поддерживать, расширять* и над ним *практически невозможно работать* в *команде*. 

Всё это стало основной *предпосылкой к появлению методологий* в CSS. Каждая из них по-своему *накладывает* некоторые *ограничения*, *уменьшая допустимую сложность селекторов*, вносит *подобие модульности* или *разделения ответственности* (Separation of Concerns).

## Некоторые рекомендации

*Правила специфичности* не так сложны при *конкретных* селекторах. Но когда селекторов *сотни* в одном файле, *трудно* слёту *рассчитать* (а потом держать это в уме), *какой стиль* будет *применён в итоге* или *как* отельное *правило* (rule) *повлияет* на *элементы* страницы.

Есть некоторые *рекомендации*, соблюдение которых поможет не только значительно *улучшить читабильность* кода, но иногда тем или иным образом *оптимизировать* его:
* Стараться использовать *только самые распространённые простые селекторы*, покрывающие практически любые случаи - *селекторы по классу*.
```css
.content p {} /* не очень хорошо */
.content #title {} /* лучше, но есть недочёты (будет сказано дальше) */
.content .title {} /* хорошо */
```
* То же самое касается и *комбинаторов*: в большинстве случаев лучше *ограничиться комбинатором потомков*.
```css
.content ~ .title {} /* результат не очень то предсказуем */
.content > .title {} /* можно иногда использовать, но не все о нём помнят, что сказывается на читаемости кода */
.content .title {} /* хорошо */
```
* *Малая вложенность селекторов*. Желательно, *не более двух селекторов* по классу (`.parent .child`). Это *упрощает разбор селектора* и *ускоряет поиск* соответствующих ему *элементов* как для разработчика, так и для браузера.
```css
.page .content .text-block .title {} /* плохо */
.text-block .title {} /* хорошо */
.text-block .title:hover {} /* тоже хорошо */
```
* Как можно *меньше использовать inline-стили* (ведь их практически *невозможно переопределить*). Вместо этого *лучше выносить CSS-код* в *классы*.
```css
<p class="title" style="color:red">Text</p> /* плохо */
<p class="title title--red">Test</p> /* хорошо */
```
* Стараться *избегать* использования *ключевого слова* `!important`, предпочитая ему решение проблемы перекрытия стилей при помощи *специфичности*. Лишь иногда (но очень редко) `!important` является единственным решением проблемы (например, если перекрываются чрезвычайно специфичные стили сторонней библиотеки).

Одной из интересных особенностей CSS является то, что простой селектор можно использовать дважды и специфичность увеличится, а выборка элементов не изменится. Это может помочь, если стили сторонней библиотеки по какой-то причине импортируются позже, чем стили проекта.
```html
<div class="article"></div>
```
```css
/* перекрыть стили сторонней библиотеки ниже можно специфичностью дублированием селектора: */
.article.article {/* ... */} /* специфичность: 0,0,2,0 */
/* можно и так: */
div.article {/* ... */} /* специфичность: 0,0,1,1 */
/* но лучше не делать вот так: */
div.article {/* ... */ !important} /* важность: author important declaration */

/* стили из сторонней библиотеки */
.article {/* ... */} /* специфичность: 0,0,1,0 */
```

* При *переопределении CSS-правил* в *своём коде* следует *больше полагаться на специфичность* и *меньше на порядок в коде*. Тогда приоритет стилей не изменится, если блоки кода в одном файле поменяются местами или CSS-файлы подключатся не в том порядке. 

В *примере* ниже *специфичность двух селекторов одинакова* и *цвет* текста *зависит* от *порядка объявления правил* в *коде*. Если *поменять* правила *местами*, то применится *другой цвет*.
```html
<div class="page">
  <div class="header">
    <p class="title">Notes</p> 
  </div>
</div>
```
```css
.page .title {
  color: red;
}

.header .title {
  color: green; /* применится этот цвет */
}
```
* При использовании *сторонних библиотек* стоит *обращать внимание* именно на *порядок подключения CSS-файлов*: нужно *подключать свою таблицу стилей последней*.
```html
<link rel="stylesheet" href="external-lib.min.css>
<link rel="stylesheet" href="styles.css>
```

* Удалять неиспользуемые стили и не подключать большие библиотеки стилей (по типу bootstrap) целиком, если планируется использование лишь малой части (меньше 20%) их функциональности. Это уменьшит время загрузки CSS-файла, его парсинга и построения CSSOM.

### Почему лучше избегать использования селекторов по id и использовать селекторы по классу

* Один *элемент не может иметь два идентификатора*, но *может иметь сколь угодно классов*.
```html
<!-- это не будет работать! -->
<p id="title header-title"></p>
<!-- это тем более! игнорирование или ошибка --> 
<p id="title" id="header-title"></p> 
<!-- а это работает -->
<p class="title header-title"></p>
```

* *Селектор по id* выбирает *лишь первый элемент* на *странице*.
```html
<div>
    <p id="title"></p>
    <!-- игнорирование или ошибка (идентификатор должен быть уникален) -->
    <p id="title"></p>
</div>
```
* Если есть *только селекторы по классу*, *специфичность вычисляется проще*.
```css
.page {} /* 0,0,1,0 */
.content .title {} /* 0,0,2,0 */
.content .title:hover {} /* 0,0,3,0 */
```

В случае же использования атрибутов `id` и `class`, количество возможных комбинаций возрастает. Больше комбинаций — больше сложность.
```css
#page #title {} /* 0,2,0,0 (может перезаписать все правила ниже) */
.page #title {} /* 0,1,1,0 */
 #page .title {} /* 0,1,1,0 */
.page .title {} /* 0,0,2,0 */
```

## БЭМ

**Блок-Элемент-Модификатор** (БЭМ, BEM) — самая популярная *методология CSS* на данный момент.

* **Блок** (Block) — *переиспользуемый элемент* сайта.
* **Элемент** (Element) — некоторая *часть блока*, *не* имеющая *функционального смысла вне* блока.
* **Модификатор** (Modifier) — *свойство блока* или *элемента*, меняющие его *внешний вид* или *поведение*.

Возможный *синтаксис БЭМ*
```css
.block_element-modifier {}
/* или */
.block__element--modifier {}
```

### БЭМ в CSS
```css
.page {} /* блок */
.page__content {} /* элемент */

.content {} /* блок */

.articles {} /* блок */

.article {} /* блок */
.article__title {} /* элемент */
.article__title--bold {} /* модификатор */
.article__title--red {} /* модификатор */
.article__image {} /* элемент */
.article__image--small {} /* модификатор */
.article__image--large {} /* модификатор */
```
### БЭМ в SCSS
```scss
.page {
  /* any css for .page */
  
  &__content {
    /* any css for .page__content */
  }
}

.content {}

.articles {}

.article {
  /* any css for .article */

  &__title {
    /* any css for .article__title */

    &--bold {
      /* any css for .article__title--bold */
    }

    &--red {
      /* any css for .article__title--red */
    }
  }
}
```

Стоит отметить, что *любой DOM-элемент* в рамках БЭМ может быть и *блоком*, и *элементом* *одновременно*.  
В примере ниже *div* является *блоком article* и *элементом item блока content*
```html
<div class="content">
  <div class="content__item article">
    <p class="article__title"></p>
  </div>
</div>
```

### Преимущества и недостатки БЭМ
*Преимущества БЭМ*
* *Модульность* кода и *изолированность модулей* друг от друга.  
* *Простая специфичность*.  

*Недостатки БЭМ*
* Слишком *длинные названия классов*.

### Пример использования *БЭМ* с *React*
```js
import React from 'react';

const articles = [{ title: 'BEM' }, { title: 'React' }];

const renderArticle = (item, index) => (
  <div className="article" key={index}>
    <p className="article__title">
      <span className="article__title--red">{`Article #${index}:`}</span>
      {item.title}
    </p>
    <img className="article__image" />
  </div>
);

const Articles = () => (
  <div className="articles">
    {articles.map(renderArticle)}
  </div>
);

const render = () => (
  <div className="page">
    <div className="page__content content">
      <Articles />
    </div>
  </div>
);
```

## OOCSS

**Объектно-Ориентированный CSS** (Object Oriented CSS, OOCSS) — *подход*, использующий *преимущества ООП* в *CSS*.  

Основная *идея* заключается в *переиспользуемости кода*: *принцип Don't Repeat Yourself* (DRY).

**Объектом** в этом подходе выступает *любой визуально повторяющийся шаблон*, который можно выделить как *фрагмент кода*.

*Элементы* страницы задаются *объектными классами*, являющиеся *отдельными объектами* в таблицах стилей.

В отличие от многих, подход *OOCSS не устанавливает правил наименования* классов; *не запрещает* использовать тэги, id и прочее, что позволяет *сочетать* его с *другими подходами*.

### Первое правило подхода OOCSS: разделение Структуры и Оформления

**Структура** (Structure) — совокупность *невидимых* для пользователя *свойств* элемента.  
Пример *структурных свойств*: height, width, margin, padding, overflow (размер, позиционирование и прочее).

**Оформление** (Skin) — совокупность *видимых свойств* элемента.  
Пример *свойств оформления*: color, font, shadow, gradient.  

Другими словами: *структура* состоит из инструкций о том, *как* все *расположено*, а *оформление* определяет, *как выглядит* макет.

Такое *разделение* позволяет *размещать* копию объекта в *любое место* сайта, *не переопределяя* при этом *существующие стили*.  
Эта копия *расширяется дополнительными стилями* через *class-атрибут* (часто сразу *несколькими классами*).

### Второе правило подхода OOCSS: разделение Контейнера и Контента

**Контентом** является любой элемент, расположенный в каком-то другом элементе — **контейнере**.  
```css
.container .content
```
Суть заключается в том, чтобы *использовать комбинатор потомков* как можно *реже*.  
Если *контент не зависит* от конкретного *контейнера*, мы можем *переиспользовать код чаще*.

Например, вместо
```css
.sidebar {}
.sidebar .menu {}
.sidebar .menu .menu-item {}
```
можно *разделить контейнер и контент* следующим образом
```css
.sidebar {}
.menu {}
.menu-item {}
```
что даёт возможность использовать *меню* где-нибудь ещё.

### OOCSS в CSS
Ищем *повторяющиеся блоки кода*:
```css
.button {
  width: 100px;
  height: 40px;
  padding: 4px 8px;
  background-color: #000;
  border-radius: 4px;
  color: #fff;
}

.button-wide {
  width: 200px;
  height: 40px;
  padding: 4px 8px;
  background-color: #000;
  border-radius: 4px;
  color: #fff;
}
```
```html
<div class="button"></div>
<div class="button-wide"></div>
```
*Выносим* их в *классы*:
```css
.skin-button {
  background-color: #000;
  border-radius: 4px;
  color: #fff;
}

.structure-button {
  height: 40px;
  padding: 4px 8px;
}

.button {
  width: 100px;
}

.button-wide {
  width: 200px;
}
```
```html
<div class="button skin-button structure-button"></div>
<div class="button-wide skin-button structure-button"></div>
```
### OOCSS в SCSS
При использовании *OOCSS* с *чистым CSS*, *атрибут class* у DOM-элементов сильно *разрастается*, но что, если...
```scss
@mixin skin-button {
  background-color: #000;
  border-radius: 4px;
  color: #fff;
}

@mixin structure-button {
  height: 40px;
  padding: 4px 8px;
}

.button {
  @include skin-button;
  @include structure-button;
  width: 100px;
}

.button-wide {
  @include skin-button;
  @include structure-button;
  width: 200px;
}
```
```html
<div class="button"></div>
<div class="button-wide"></div>
```
Хотя, имея в арсенале *миксины*, можно сделать *ещё лучше* (не совсем OOCSS) в этом примере:
```scss
@mixin button-defaults {
  background-color: #000;
  border-radius: 4px;
  color: #fff;
  height: 40px;
  padding: 4px 8px;
}

.button {
  @include button-defaults;
  width: 100px;
}

.button {
  @include button-defaults;
  width: 200px;
}
```

### Преимущества и недостатки OOCSS
*Преимущества OOCSS*
* Хорошая *переиспользуемость кода*.  

*Недостатки OOCSS*
* *Сильная связанность кода ухудшает* его *поддержку*: *классы* достаточно *общие*, могут использоваться *повсеместно* (нельзя просто изменить их, скорее всего придётся менять разметку).

## SMACSS

**SMACSS** — *масштабируемая и модульная архитектура для CSS* (Scalable and Modular Architecture for CSS).

Идея заключается в *разбиении стилей на слои* и тесно связана с *принципом разделения ответственности* (SoC). Каждый слой выполняет *только свои обязательства*. Это позволяет улучшить поддержку кода.

*Стили разбиваются* на *5 слоёв*:
* **Базовые стили** (base rules) — стили *основных элементов* страницы. Обычно *тэги* (body, div, input, ...), *псевдоклассы* и *псевдоэлементы*, *атрибуты кроме class и id* (изредка class: например, стилизация custom select).
```css
/* базовые стили */
input {}
input:focus {}
*:hover {}
*::selection {}
```
* **Стили макета** (layout rules) — стили *глобальных элементов* страницы (header, footer, sidebar, ...), *разбивающих страницу* на *блоки с контентом*, состоящие из *модулей*. Автор подхода предлагает *использовать id*, чтобы подчеркнуть *уникальность* элементов, но можно этого *не делать*. *Префикс*: `layout-`, `l-` или `grid-`.
```css
#main { width: 40%; } /* глобальный элемент */
#header { width: 100%; } /* глобальный элемент */

/* стили макета ниже навешивается на предков глобальных элементов (например, на body) */
.l-fixed #main { width: 320px; }
.l-fixed #header { width: 320px; }
```
* **Стили модулей** (modules rules) — стили *переиспользуемых блоков* страницы. Автор подхода советует *избегать* здесь *тэгов* и *id*. *Префикс*: `module-`, где *module* — *название модуля*.
```css
/* стили модуля */
.article {}
.article-title {}
.article-img
```
```html
<div class="article">
  <p class="article-title"></p>
  <img class="article-img" />
  <div></div>
</div>
```
* **Стили состояния** (state rules) — различные *состояния модулей* и *структуры* сайта. Автор *допускает* использование `!important` только в этом разделе. *Префикс*: `is-`.
```css
/* стили состояния */
.button.is-disabled {}
.tab.is-active {}
#sidebar.is-mobile {}
.is-hidden {}
```
* **Стили темы** (theme rules) — некоторые *дополнительные стили*, описывающие, *как модули* и *макет* могут *выглядеть*. Обычно *меняются* время от времени. *Необязательная категория* (стили могут быть уже учтены в других категориях). 
```css
/* article.css */
.article-title { color: #000; }

/* theme.css */
.article-title { color: #ff0000; } /* меняем цвет с чёрного на красный в честь какого-то события */
```
Обычно на *каждый слой* отводится *отдельный файл*: `base.scss`, `layout.scss`, `module-name.scss`, `state.scss`, `theme.scss`.

## Atomic CSS

**Атом** является *мельчайшей частицей вещества*.

**Атомный CSS** (Atomic CSS) — подход, похожий на *OOCSS*, где в качестве объекта выступает *одно объявление* (свойство: значение), *отражающееся в названии класса*.
```css
.Mt-4 { margin-top: 8px; }
.Fs-16 { font-size: 16px; }
.W-320 { width: 320px; }
```
```html
<p class="mt-4 fs-16 w-320"></p>
```
*Атомный CSS* хорошо подходит для тех, кто хочет *писать макет* и *стили в одном месте*.

Тем не менее, *вручную* писать код с таким подходом *не* очень *удобно*, поэтому существует *инструмент* [Atomizer](https://github.com/acss-io/atomizer), который *рекурсивно обходит html* файлы и *генерирует* весь *необходимый css*.

*Классы*: [reference](https://acss.io/reference)  
*Псевдоклассы*: `a` — `:active`, `c` — `:checked`, `f` — `:focus`, `h` — `:hover`.  
*Псевдоэлементы*: `a` — `::after`, `b` — `::before`, `fl` — `::first-letter`, `fli` — `::first-line`, `ph` — `::placeholder`.  
*Комбинаторы*: `_` — комбинатор *потомков*, `>` — комбинатор *детей*, `+` — комбинатор *братьев*.  

### Atomic CSS и CSS

*Синтаксис html* в случае использования *Atomizer* следующий
```html
<div class="D(f) Jc(c) Op(0.8):h"></div>
<div class="article">
    <p class="article_C(red)"></p>
</div>
```
*Atomizer* при запуске просмотрит html и автоматически *сгенерирует* css
```css
.D\(f\) {
  display: flex;
}

.Jc\(c\) {
  justify-content: center;
}

.Op\(0\.8\)\:h:hover {
  opacity: 0.8;
}

.article .C\(red\) {
  color: red;
}
```

### Преимущества и недостатки Atomic CSS
*Преимущества OOCSS*
* Относительно хорошая *переиспользуемость кода* (изменил значение в одном месте, изменилось везде).  
* *Специфичность*: лучше, чем использование inline стилей, поскольку здесь стили хранятся в сгенерированных css файлах.
* Можно настроить *пользовательские переменные* в Atomizer:
```JSON
{
  "1": "1px solid #000",
  "foo": "2px dotted #f00",
}
```


*Недостатки OOCSS*
* *Названия классов* содержат *описание объявления*, а не *суть элемента*, что усложняет разработку.
* К пункту выше можно добавить, что атрибуты *class* могут достигать *невероятных размеров* с *увеличением количества объявлений*.
* *Управление отображением* элемента находится в *HTML* (отвечающим за разметку), а должно оставаться в *CSS*.
* *Нет встроенной поддержки* некоторых вещей, в том числе и *grid* (нужно либо подключать дополнение или писать самому).

## AMCSS

**Модули атрибутов для CSS** (Attribute Modules for CSS, AMCSS) — подход, использующий *атрибуты и их значения вместо классов*.

Как и в многих других методологиях, идея AMCSS заключается в *логической группировке CSS* кода.  
* **Модули** (Modules) — замена классам; описываются атрибутами; похожи на блоки и элементы в БЭМ.
* **Вариации** (Variations) — различные состояния модулей; представлены значениями атрибутов; похожи на модификаторы в БЭМ. 
* **Черты** (Traits) — коллекция значений, имеющих одну цель; похожи на SUIT utils.

Одной из *целей* подхода является исправление проблемы БЭМ со слишком длинными названиями классов.
```html
<!-- /* БЭМ * / -->
<div class="button button--large button--primary"></div>
<!-- /* AMCSS * / -->
<div am-Button="large primary"></div>
```
Префикс `am-` добавляется для того, чтобы не было конфликтов с другими атрибутами.

### Синтаксис AMCSS

*Черты* пишутся в *camelCase*, *модули* — в *PascalCase*.

Отношение *родитель-ребёнок* обозначаются *дефисом*.

В качестве *селектора* используется *селектор по атрибуту* `~=`, выбирающий элементы, *содержащие* необходимый *атрибут* и *указанные слова* (разделённые пробелами) в *значении атрибута*.  
Это позволяет создать поведение, аналогичное классам.

*Значения атрибутов*, подобно классам, *разделяются пробелами*, но при этом имеют *более широкий спектр допустимых символов*.

```html
<div am-traitName="name name2 mobile:name3"></div>
<div am-ModuleName></div>
<div am-ModuleName-ChildElement></div>
<div am-ModuleName="variation"></div>
```
```css
[am-traitName~="name"] {}
[am-traitName~="name2"] {}
[am-traitName~="name3"], .breakpoint-mobile [am-traitName~="mobile:name3"] {}

[am-ModuleName] {
  /* Стили модуля (блока) */
}
[am-ModuleName~="variation"] {
  /* Стили вариации модуля ModuleName */
}

[am-ModuleName-ChildElement] {
  /* Стили дочернего элемента модуля ModuleName */
}
```

Стоит обратить внимание, что *вариация не может существовать без базовых стилей модуля*.  
*Любой* элемент, удовлетворающий второму селектору в примере ниже, удовлетворяет и первому тоже.
```css
[am-ModuleName] {} /* базовый атрибут и его стили */
[am-ModuleName~="variation"] {}
```
*Черта* же *не имеет стилей в базовом атрибуте*, но зато *черты* можно *смешивать* и *сочетать* в *любом* месте кода.
```css
[am-traitName~="name"] {}
```

Существует так же *другой синтаксис AMCSS*, более *близкий к БЭМ*, но *теряющий* некоторые *преимущества*:
```html
<div am-ModuleName am-ModuleName-variation></div>
```
```css
[am-ModuleName] {}
[am-ModuleName-variation] {}
```
### AMCSS и CSS
Модули и вариации
```html
<div am-Article>
  <p am-Article-Title="red translucent"></p>
</div>
```
```css
[am-Article] {
  display: flex;
}

[am-Article-Title] {
  margin: 0;
}

[am-Article-Title~="red"] {
  color: #ff0000;
}

[am-Article-Title~="translucent"] {
  opacity: 0.5;
}
```
Черты
```html
<p am-font="primary" am-color="red"></p>
```
```css
[am-font~="primary"] {
  font-size: 18px;
  font-weight: bold;
}

[am-color~="red"] {
  color: #c20606;
}
```
### Преимущества и недостатки AMCSS
Подход *похож на БЭМ*, поэтому обладает теми же *преимуществами*:

*Преимущества AMCSS*
* *Модульность* кода и *изолированность модулей* друг от друга.  
* *Простая специфичность*. (*специфичность* у *всех атрибутов*, в том числе и классов, одинаковая: *0,0,1,0*).  
* Решена проблема с *длинными названиями* классов, свойственная БЭМ.
```html
<div class="select__option select__option--first select__option--selected">
<div am-Select-Option="first selected">
```
* Информация о *разнородных стилях* не хранится в *одной строке*, теперь она *разбита по атрибутам*:
```html
<div class="list__item list__item--even article">
<div
  am-List-Item="even"
  am-Article
>
```
* Более *широкий спектр допустимых символов* в названиях вариаций.

*Недостатки AMCSS*
* Большая часть всего, что создавалось *ранее*, делалось на *классах*.  
Принципиально новый подход требует *других методов работы* не только с *CSS*, но и с *DOM* тоже.  
Может *пострадать поддержка* некоторых *библиотек*.  
* В случае использования *валидатора* для *проверки корректности кода*, нужно также добавлять *приставку* `data-`, что делает код длиннее.
```html
<div data-am-Article>
```

Если *рост количества классов* элемента *расширяет HTML в ширину*, то *рост количества атрибутов расширяет* его *в высоту*.  
Это *не является однозначным минусом или плюсом*, но код может сильно разрастить в случае активного использования черт, что может быть не просто исправить.  

## Enduring CSS

**Enduring CSS** (eCSS) — *выносливый CSS*.

Основная концепция: **изоляция**. 

Код *Enduring CSS* состоит из *компонент*. 

**Компонента** — изолированная переиспользуемая единица кода, не имеющая зависимостей и контекста. Её можно удалить без риска утечки стилей.

Если *нужен компонент*, *похожий* на уже *существующий*, то всё равно *создаётся абсолютно новый компонент*. *Написанный* для одного компонента *код* *не может быть переиспользован* в другом, даже если различия компонент незначительны.

# Динамический CSS

Давно прошло то время, когда страницы были статическими.  
Сейчас на большинстве сайтов элементы появляются и исчезают, меняют свои цвета, размеры и прочие характеристики, реагируя на действия пользователя.  

Чтобы добиться динамичности, нужно менять стили.  
Существует несколько способов это делать.

## Добавление и удаление классов по условию

Многие *элементы DOM* могут иметь *больше одного класса*, причём некоторые из этих классов могут быть *динамическими* (появляются и исчезают из атрибутов *по условию*).  

### Чистый JavaScript (работа с DOM)

В примере ниже чёрная кнопка становится красной при наведении мыши (поведение, аналогичное `:hover`).
```html
<button class="button"></div>
<style>
  .button {
    width: 100px;
    height: 40px;
    background: #000;
  }
  .button--red {
    background: #f00;
  }
</style>
<script>
  const button = document.querySelector(".button");
  button.onmouseenter = () => {
    button.classList.add('button--red');
  }

  button.onmouseleave = () => {
    button.classList.remove('button--red');
  }
</script>
```

### React и библиотека classnames

В React мы не работаем с HTML и DOM напрямую, их заменяют JSX и Virtual DOM.  
Это даёт нам возможность проводить вычисления атрибутов, в том числе и атрибута className.  
```jsx
<div className={`article__image ${isLarge ? 'article__image--large' : ''} ${isInverted ? 'article__image--inverted' : ''}`}>
```
В примере выше картинка статьи может становиться больше или переворачиваться в зависимости от значения переменных `isLarge` и `isInverted`.  
Такой код писать не только не удобно, но нужно ещё и не допусктить появления лишних значений, пробельных символов и переносов строк.

В таких случаях можно прибегнуть к использованию *[библиотеки classnames](https://github.com/JedWatson/classnames)*
```js
import cx from 'classnames';
/* ... */
const articleImageClasses = cx({
  article__image: true,
  'article__image--large': isLarge,
  'article__image--inverted': isInverted,
});
/* или */
const articleImageClasses = cx('article__image', {
  'article__image--large': isLarge,
  'article__image--inverted': isInverted,
});
```
```jsx
<div className={articleImageClasses} />
```
*Функция cx* принимает *сколь угодно параметров*.  
Если в качестве *параметра* выступает *объект*, рассматриваются *его ключи и их значения*.  
*Ложные* (falsy) значения *игнорируются*.  
*Лишние пробельные символы убираются*.

## CSS-in-JS

CSS-in-JS — подход написания CSS при помощи JavaScript, имеющий множество реализаций.  
Как и другие подходы, он вносит идеи о решении проблем в CSS.  

В большинстве CSS-in-JS библиотеках есть две функции:
* **css** — функция, которая принимает CSS в виде текста, генерирует название класса, создаёт новое правило на основании сгенерированного названия и переданных ей стилей, добавляет правило в `<head/>`, возвращает название класса.
* **styled** — функция, генерирующая конкретные DOM-элементы с переданными в неё стилями (стили добавляются при помощи функции `css`).


### Встроенные стили

Встроенные стили (inline styles) — стили, которые явно указываются в HTML или JSX.

Встроенные стили в HTML (не относятся к CSS-in-JS, поскольку не могут быть вычислены с помощью JavaScript).
```html
<p style="color:#000;">
```

Встроенные стили в React (атрибут style).
```jsx
/* isHovered, activeColor - переменные */
<p style={{ color: isHovered ? '#f00' : '#000' }} />
<p style={{ color: activeColor }} />
```

Встроенные стили во Vue (атрибут style с привязкой данных):
```jsx
/* isHovered, activeColor - переменные */
<p v-bind:style="{ color: isHovered ? '#f00' : '#000' }" />
/* или короче */
<p :style="{ color: isHovered ? '#f00' : '#000' }" />
<p :style="{ color: activeColor }" />
```

### Преимущества и недостатки CSS-in-JS

Изначально в JavaScript и в CSS не было модулей. Со временем в них появилась необходимость, тогда в JavaScript появилась первая модульная система CommonJS, а вслед за ней и стандартизированная версия ECMAScript Modules.  

CSS изначально создан для стилизации документов, поэтому в нём так и не появились встроенные модули.  
Одна глоабльная область видимости (scope): любой класс на сайте может быть применён к любому элементу.  
По мере роста приложения это приводит к проблемам, поэтому и появились методологии.

С помощью этой функциональности решается проблема с названиями.  

*Преимущества CSS-in-JS*:
* Модульность кода. 
* Внедрение области видимости в CSS (нужные стили импортируются).
* Явные зависимости.

### Как функции css и styled работают внутри

Замечание: в JavaScript есть возможность *вызвать функцию* следующим образом.  
```js
const css = (strings, ...vars) => `{ ${strings} }`;
css`magic` // вернёт '{ magic }'
```
Это называется **тэговым шаблоном** и работает *только с шаблонными строками* \`\`.  
*Первым параметром* приходит *массив из подстрок*, который получается в результате *разбиения* шаблонной строки её *переменными*, *остальные параметры* — сами *переменные*.
```js
`magic` // 0 переменных, массив подстрок: ['magic'], массив переменных: []
`${2*2} > ${+true}` // 2 переменные, массив подстрок: ['', ' > ', ''], массов переменных: [4, 1]
```
Сделано это для того, чтобы можно было *валидировать* и *заменять переменные*, а затем собрать и *вернуть новую строку*.

Как работает функция `css`:
```jsx
// произвольная функция генерации хэша
const generateRandomHash = () => Math.random().toString(36).substring(7).slice(0, 5); 

// функция создания правила
const createRuleset = (className = '', styles = '') => `
  .${className} {
    ${styles}
  }
`;

// получаем строку стилей из параметров тэгового шаблона
const getStyles = (strings = [], vars = []) =>
  strings.map((item, index) => `${item}${vars[index] || ''}`).join('');
  
const css = (strings, ...vars) => {
  // название класса создаётся на основании хэша, обеспечивая уникальность правила
  const className = `css-${generateRandomHash()}`;
  // объединяем параметры тэгового шаблона в строку без изменений (здесь могла быть валидация и правка значений)
  const styles = getStyles(strings, vars);
  // генерируется правило
  const ruleset = createRuleset(className, styles);

  // создаётся <style /> и помещается в <head>
  const styleElement = document.createElement('style');
  styleElement.textContent = ruleset;
  document.head.appendChild(styleElement);

  // возвращиется сгенерированное название класса
  return className;
};

// пример использования функции css
const className = css`
  display: flex;
  justify-content: center;
  align-items: center;
`;
const renderBlock = () => (<div className={className} />);
```
Как работает функция `styled`.
```jsx
const styled = {
  div: (...cssParams) => props => (<div className={css(...cssParams)} {...props} />),
  span: (...cssParams) => props => (<span className={css(...cssParams)} {...props} />),
  /* ... */
};

// пример использования функции styled.div
const Button = styled.div`
  width: 50px;
  height: 50px;
  background: red;
`;
const renderRedButton = props => (<Button {...props} />);
```

# Вёрстка под различные девайсы

## Viewport

**Виртуальное окно** (virtual window), **вьюпорт** (viewport) — *область окна*, в которой *пользователю виден контент*.  
Обычно она *не совпадает* с *отрендеренной страницей*, поэтому появляются *полосы прокрутки* (scrollbars).
```html
<meta name="viewport" content="width=device-width, initial-scale=1">
```

*Свойство* `width` контролирует *размер вьюпорта*.  
*Значение свойства* может быть как *фиксированным* `width=700` (от 200 до 10000), так и *специальным* `width=device-width`, которое *адаптируется под экран пользователя*.  
По умолчанию, если *метатег отсутствует*, *браузеры* устанавливают *своё фиксированное значение* (например, `width=980`). Это характерно только для мобильных устройств. 

В случае фиксированного вьюпорта, медиазапросы `@media` перестают работать.
```css
@media (max-width: 320px) {} /* никогда не сработает, если вьюпорт больше 320px  */
```

Свойство `initial-scale` отвечает за уровень масштабирования (zoom level), когда страница загружена впервые.  
Значение `initial-scale=1` означает, что один CSS-пиксель (px) равен одному вьюпорт-пикселю.

Свойства `minimum-scale` (0.0 - 10.0), `maximum-scale` (0.0 - 10.0), `user-scalable` (yes, no) отвечают за возможности масштабирования страницы пользователем. Их рекомендуется не использовать.

## Виды вёрстки

### Фиксированная, статическая

**Фиксированная, статическая** (fixed, static) вёрстка *постоянна вне зависимости от размеров устройства*.  

*Все элементы* занимают строго *определённую величину пикселей* на странице.  
Если элементы *не вмещаются*, то появлявляются *полосы прокрутки*.  
Если страница *слишком велика*, то она *заполняется элементами не полностью*.  

Для *фиксированной вёрстки* используются **абсолютные единицы измерения** (absolute length units): `px` - пиксель (для вёрстки веб-страниц обычно используются они), `cm` - сантиметр, `in` - дюйм, и другие.
```css
.
Download .txt
gitextract_2bqs7fmq/

├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       ├── custom.md
│       └── feature_request.md
├── Architecture-Design.md
├── Auth.md
├── Browsers.md
├── Bundling.md
├── CSS.md
├── CyberSecurityFundamentals.md
├── Data.md
├── DataModels-Databases.md
├── DataStructures.md
├── DataTypes.md
├── Development.md
├── DiscreteMath.md
├── Docker.md
├── Elasticsearch.md
├── Encoding.md
├── Features.md
├── Flux-Redux-Vuex-Mobx.md
├── FunctionalProgramming.md
├── Git.md
├── GraphQL-REST.md
├── HTML.md
├── InterviewQuestions.md
├── JavaScript.md
├── JavaScriptDOM.md
├── NodeJS.md
├── ProductQuality.md
├── Programming.md
├── ProgrammingLanguageCharacteristics.md
├── README.md
├── React.md
├── Testing.md
├── TypeScript.md
├── _config.yml
├── in-progress/
│   ├── Algorithms-Structures.md
│   ├── C++.md
│   ├── Colors.md
│   ├── English.md
│   ├── Protocols.md
│   ├── ReactNative.md
│   ├── VSCode.md
│   └── npm.md
└── tech/
    ├── ApacheVelocity.md
    ├── Chai-Mocha.md
    ├── ESlint-TSlint-Prettier.md
    ├── ErrorHandling.md
    ├── Files.md
    ├── Firebase.md
    ├── Heroku.md
    ├── Jest.md
    ├── Klaviyo.md
    ├── Kubernetes.md
    ├── Microservices.md
    ├── MongoDB.md
    ├── Parcing-Preprocessing-StaticChecking.md
    ├── PostCSS.md
    ├── Postman.md
    ├── Python.md
    ├── Redis.md
    ├── SQL.md
    ├── Scala.md
    ├── Shell.md
    ├── Shortcuts.md
    ├── Sisense.md
    ├── Snowflake.md
    ├── Vim.md
    ├── Webpack.md
    ├── jQuery.md
    ├── nvm.md
    └── pip.md
Condensed preview — 72 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,756K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 834,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "chars": 126,
    "preview": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 595,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
  },
  {
    "path": "Architecture-Design.md",
    "chars": 95706,
    "preview": "- [Определения](#определения)\n  - [Архитектурный стиль](#архитектурный-стиль)\n  - [Архитектурный паттерн](#архитектурный"
  },
  {
    "path": "Auth.md",
    "chars": 7850,
    "preview": "- [Аутентификация, Авторизация и Идентификация](#аутентификация-авторизация-и-идентификация)\n- [Разновидности идентифика"
  },
  {
    "path": "Browsers.md",
    "chars": 17142,
    "preview": "- [Файлы](#файлы)\n- [Как всё устроено](#как-всё-устроено)\n\t- [Адресная строка браузера](#адресная-строка-браузера)\n\t- [О"
  },
  {
    "path": "Bundling.md",
    "chars": 2760,
    "preview": "# Сборка проекта (Bundling)\n\n## Встряска дерева (Three Shaking)\n\n**Встряска дерева** (англ. `three shaking`) - подход к "
  },
  {
    "path": "CSS.md",
    "chars": 87987,
    "preview": "- [Основы CSS](#основы-css)\n  - [Утверждения](#утверждения)\n  - [Правило и его составляющие](#правило-и-его-составляющие"
  },
  {
    "path": "CyberSecurityFundamentals.md",
    "chars": 7919,
    "preview": "- [Переферия и устройства ввода-вывода](#переферия-и-устройства-ввода-вывода)\n- [Компьютерная сеть](#компьютерная-сеть)\n"
  },
  {
    "path": "Data.md",
    "chars": 23179,
    "preview": "# Оглавление\n- [Информация и данные](#информация-и-данные)\n- [Кодирование информации](#кодирование-информации)\n- [Метада"
  },
  {
    "path": "DataModels-Databases.md",
    "chars": 96104,
    "preview": "# Оглавление\n- [Общие понятия баз данных](#общие-понятия-баз-данных)\n- [Реляционная модель данных](#реляционная-модель-д"
  },
  {
    "path": "DataStructures.md",
    "chars": 258,
    "preview": "- [Структуры данных](#структуры-данных)\n\n# Структуры данных\n\n**Структура данных** (англ. data structure) - это *набор зн"
  },
  {
    "path": "DataTypes.md",
    "chars": 43612,
    "preview": "\n# Оглавление\n- [О типе данных](#о-типе-данных)\n- [Примитивные типы данных](#примитивные-типы-данных)\n- [Логический тип]"
  },
  {
    "path": "Development.md",
    "chars": 8211,
    "preview": "# Оглавление\n- [SCRUM теория и жестокая реальность](#scrum-теория-и-жестокая-реальность)\n\n# SCRUM теория vs жестокая реа"
  },
  {
    "path": "DiscreteMath.md",
    "chars": 120631,
    "preview": "# Оглавление\n- [Введение](#введение)\n- [Теория множеств](#теория-множеств)\n- [Теория мультимножеств](#теория-мультимноже"
  },
  {
    "path": "Docker.md",
    "chars": 20754,
    "preview": "- [Контейнеры и контейнеризация](#контейнеры-и-контейнеризация)\n  - [Сравнение контейнера и виртуальной машины](#сравнен"
  },
  {
    "path": "Elasticsearch.md",
    "chars": 48076,
    "preview": "- [Базовые понятия Elasticsearch](#базовые-понятия-elasticsearch)\n  - [Индекс, тип, документ](#индекс-тип-документ)\n  - "
  },
  {
    "path": "Encoding.md",
    "chars": 13306,
    "preview": "- [Cистемы счисления](#системы-счисления)\n  - [Представление чисел в различных с/с](#представление-чисел-в-различных-с-с"
  },
  {
    "path": "Features.md",
    "chars": 3233,
    "preview": "# Типы веб-сайтов\n\nОнлайн-магазин, новостной портал, социальная сеть, информационная страница (информационный портал) че"
  },
  {
    "path": "Flux-Redux-Vuex-Mobx.md",
    "chars": 33564,
    "preview": "\n* [Flux](#flux)\n  * [Особенности Flux](#особенности-flux)\n  * [Структура Flux](#структура-flux)\n* [Redux](#redux)\n  * ["
  },
  {
    "path": "FunctionalProgramming.md",
    "chars": 16950,
    "preview": "- [Объекты и функции первого класса](#объекты-и-функции-первого-класса)\n- [Функции высшего порядка](#функции-высшего-пор"
  },
  {
    "path": "Git.md",
    "chars": 43723,
    "preview": "- [Основные понятия Git](#основные-понятия-git)\n\t- [Как работает Git](#как-работает-git)\n\t- [Состояния файлов](#состояни"
  },
  {
    "path": "GraphQL-REST.md",
    "chars": 6307,
    "preview": "- [REST](#rest)\n- [GraphQL](#graphql)\n  - [Преимущества GraphQL](преимущества-graphql)\n- [REST на практике](#rest-на-пра"
  },
  {
    "path": "HTML.md",
    "chars": 17262,
    "preview": "- [Основы HTML](#основы-html)\n  - [Теги](#теги)\n  - [Типы HTML-тегов](#типы-html-тегов)\n  - [Фреймы и встроенные фреймы]"
  },
  {
    "path": "InterviewQuestions.md",
    "chars": 11637,
    "preview": "# Бэкграунд, проверка общих знаний, фундамента\n\n## Математика\n- Что такое множество?\n- Что такое кортеж?\n- Что такое дек"
  },
  {
    "path": "JavaScript.md",
    "chars": 81874,
    "preview": "- [Основы JavaScript](#основы-javascript)\n  - [Типизация](#типизация)\n  - [Типы данных и переменные](#типы-данных-и-пере"
  },
  {
    "path": "JavaScriptDOM.md",
    "chars": 978,
    "preview": "\n# DOM\n\n# Всплытие и погружение\n\n# Обработка пользовательских событий\n\n## Обработка данных формы\n```html\n<form name=\"val"
  },
  {
    "path": "NodeJS.md",
    "chars": 23293,
    "preview": "- [Основные определения](#основные-определения)\n  - [Что такое NodeJS](#что-такое-nodejs)\n  - [Событийно-ориентированное"
  },
  {
    "path": "ProductQuality.md",
    "chars": 8456,
    "preview": "## О чистоте кода\n\n## О логировании\n\n**Лог** (англ. `log`, дословно *переводится* как *бревно*) - это *информация* о нек"
  },
  {
    "path": "Programming.md",
    "chars": 5712,
    "preview": "\n- [Работа со строками](#работа-со-строками)\n- [Регулярные выражения](#регулярные-выражения)\n- [Семиотика, синтаксис и с"
  },
  {
    "path": "ProgrammingLanguageCharacteristics.md",
    "chars": 17322,
    "preview": "# Оглавление\n- [О характеристиках языков программирования](#о-характеристиках-языков-программирования)\n- [Типизация](#ти"
  },
  {
    "path": "README.md",
    "chars": 11680,
    "preview": "# Заметки программиста\n<!--\nMy simple notes about everything related to programming.\n-->\n\nМои *конспекты* обо *всём*, чт"
  },
  {
    "path": "React.md",
    "chars": 30647,
    "preview": "- [Что такое React и зачем он нужен](#что-такое-react-и-зачем-он-нужен)\n- [Чем отличаются фреймворк и библиотека](#чем-о"
  },
  {
    "path": "Testing.md",
    "chars": 23845,
    "preview": "- [Зачем тестировать приложение](#зачем-тестировать-приложение)\n- [Тестирование и его разновидности](#тестирование-и-его"
  },
  {
    "path": "TypeScript.md",
    "chars": 31069,
    "preview": "- [О TypeScript](#о-typescript)\n- [Типы данных (Data Types)](#типы-данных)\n- [Класс (Class)](#класс-class)\n- [Интерфейс "
  },
  {
    "path": "_config.yml",
    "chars": 26,
    "preview": "theme: jekyll-theme-hacker"
  },
  {
    "path": "in-progress/Algorithms-Structures.md",
    "chars": 1020,
    "preview": "# Трудоёмкость алгоритмов\n\n# Алгоритмы сортировки\n\n## Сортировка пузырьком (bubble sort)\n\n*Cравниваются соседние элемент"
  },
  {
    "path": "in-progress/C++.md",
    "chars": 1892,
    "preview": "\n## Указатели\n\n**Указатель** (pointer) — переменная, значением которой является адрес ячейки памяти, то есть указатель с"
  },
  {
    "path": "in-progress/Colors.md",
    "chars": 158,
    "preview": "* `rgba(255, 255, 255, 1)` - red-green-blue-alpha\n* `hsl(360, 100%, 100%)` - hue-saturation-lightness (оттенок-насыщенно"
  },
  {
    "path": "in-progress/English.md",
    "chars": 41800,
    "preview": "- [Времена (`Tenses`)](#времена-tenses)\n- [Модальные глаголы (`Modal verbs`)](#модальные-глаголы-modal-verbs)\n- [Вспомог"
  },
  {
    "path": "in-progress/Protocols.md",
    "chars": 12479,
    "preview": "- [HTTP](#http)\n  - [О протоколе](#о-протоколе)\n  - [Связь между URI, URL и URN](#связь-между-uri-url-и-urn)\n  - [Формат"
  },
  {
    "path": "in-progress/ReactNative.md",
    "chars": 59,
    "preview": "# Основы React Native\n\n**React Native** — фреймворк, TODO\n\n"
  },
  {
    "path": "in-progress/VSCode.md",
    "chars": 285,
    "preview": "## 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 "
  },
  {
    "path": "in-progress/npm.md",
    "chars": 7303,
    "preview": "- [Зависимости в `package.json`](#зависимости-в-packagejson)\n  - [Нормальные зависимости `dependencies`](#нормальные-зав"
  },
  {
    "path": "tech/ApacheVelocity.md",
    "chars": 1214,
    "preview": "\n## Установка значения переменной\n\n```js\n#set($size = 10)\n```\n```js\n#set($title = \"Notes\")\n```\n\n## Использование перемен"
  },
  {
    "path": "tech/Chai-Mocha.md",
    "chars": 4769,
    "preview": "## Get started\n\n* Install packages  \n```npm\nnpm install --save-dev chai\nnpm install --save-dev mocha\n```\n* Set up the te"
  },
  {
    "path": "tech/ESlint-TSlint-Prettier.md",
    "chars": 3983,
    "preview": "## О линтерах\n\n**Линтер** (англ. `linter`) в программировании — это программа (инструмент), которая анализирует код прил"
  },
  {
    "path": "tech/ErrorHandling.md",
    "chars": 5277,
    "preview": "# Обработка ошибок в Node.js\n\nОбработка ошибок в приложении на Node.js — одна из важнейших частей разработки, ведь она п"
  },
  {
    "path": "tech/Files.md",
    "chars": 617,
    "preview": "\n It is available in standard text format and used for saving and transporting the data. JSON helps to transmit data in "
  },
  {
    "path": "tech/Firebase.md",
    "chars": 5015,
    "preview": "# Firebase Deployment\n\n## Get Started\n* Install Firebase tools:\n```yarn\nnpm i -g firebase-tools\n```\n* Init hosting proje"
  },
  {
    "path": "tech/Heroku.md",
    "chars": 540,
    "preview": "# Heroku Deployment\n\n## Get Started\n* Install Heroku CLI:\n```yarn\nnpm i -g heroku\n```\n* Login with:\n```yarn\nheroku login"
  },
  {
    "path": "tech/Jest.md",
    "chars": 1067,
    "preview": "# Concurrency params\n\n`--maxConcurrency=<num>`\nPrevents Jest from executing more than the specified amount of tests at t"
  },
  {
    "path": "tech/Klaviyo.md",
    "chars": 3133,
    "preview": "# Klavio\n- [Переменные и их методы](#переменные-и-их-методы)\n- [Циклы](#циклы)\n- [Условные операторы](#условные-оператор"
  },
  {
    "path": "tech/Kubernetes.md",
    "chars": 906,
    "preview": "\n# Компоненты\n\n## Кластер `Cluster`, узлы `Nodes`, модули `Pods`, `Workload`\nДля начала работы с Kubernetes необходимо с"
  },
  {
    "path": "tech/Microservices.md",
    "chars": 4661,
    "preview": "\n\n# Принципы построения микросервисов\n\nПри разделении приложения на микросервисы важно следовать определённым принципам,"
  },
  {
    "path": "tech/MongoDB.md",
    "chars": 10244,
    "preview": "- [О MongoDB](#о-mongodb)\n- [Основные понятия](#основные-понятия)\n  - [Документ](#документ)\n  - [Коллекция](#коллекция)\n"
  },
  {
    "path": "tech/Parcing-Preprocessing-StaticChecking.md",
    "chars": 1167,
    "preview": "\n## Препроцессинг\n\n**Препроцессинг** (англ. `Preprocessing`) подразумевает приведение кода одного формата к другому форм"
  },
  {
    "path": "tech/PostCSS.md",
    "chars": 1997,
    "preview": "# PostCSS Setup\n## NPM\n```\nnpm install postcss-cli stylelint stylelint-config-standard postcss-cssnext precss postcss-cs"
  },
  {
    "path": "tech/Postman.md",
    "chars": 3818,
    "preview": "## Понятия в Postman\n\nЕсть рабочее пространство (англ. `Workspace`), по умолчанию используется `My workspace`.\nВ рабочем"
  },
  {
    "path": "tech/Python.md",
    "chars": 14467,
    "preview": "\n# Types\n\n## String\n\n### Concat string\nBinary `+` operator is used for *string concatenation*.\n```py\nhi = \"Hi\"\nthere = \""
  },
  {
    "path": "tech/Redis.md",
    "chars": 755,
    "preview": "# Redis for Windows\n\n**Redis** is an in-memory data structure store, used as a database, cache and message broker. It su"
  },
  {
    "path": "tech/SQL.md",
    "chars": 25241,
    "preview": "- [Основы SQL](#основы-sql)\n  - [Типы команд в SQL: `DDL`, `DQL`, `DML`, `DCL`, `TCL`](#типы-команд-в-sql-ddl-dql-dml-dc"
  },
  {
    "path": "tech/Scala.md",
    "chars": 2,
    "preview": "\n\n"
  },
  {
    "path": "tech/Shell.md",
    "chars": 2803,
    "preview": "# Linux\n\n## Команды\n* `echo <данные>` - вывод данных в консоль.\n* `echo $USER`, `whoami` - вывод имени текущего пользова"
  },
  {
    "path": "tech/Shortcuts.md",
    "chars": 797,
    "preview": "# Сочетания клавиш\n\n## Chrome\n* `Ctrl + Tab`, `Ctrl + Shift + Tab` — переключение между вкладками вправо и влево.\n* `Ctr"
  },
  {
    "path": "tech/Sisense.md",
    "chars": 1661,
    "preview": "\n# Основные понятия\n#\n![image](https://user-images.githubusercontent.com/22237384/220296705-a06e8dca-9103-4e90-9bce-48ea"
  },
  {
    "path": "tech/Snowflake.md",
    "chars": 4179,
    "preview": "\n# Хранение больших объёмов информации\n\nОба понятие \"озеро\" и \"склад данных\" связаны с хранением огромного количества да"
  },
  {
    "path": "tech/Vim.md",
    "chars": 202,
    "preview": "\n## Команды\n* `i` - режим вставки.\n* `ESC` - режим просмотра.\n* `:q` - выход.\n* `:q!` - выход без сохранения.\n* `u`, `:u"
  },
  {
    "path": "tech/Webpack.md",
    "chars": 429,
    "preview": "\n## Плагины\n\n**DefinePlugin** позвоняет созначать глобальные константы, которые могут быть заданы во время компиляции. Э"
  },
  {
    "path": "tech/jQuery.md",
    "chars": 666,
    "preview": "\n## Обращение к элементу\nОбращение к элементу при помощи `jQuery` работает аналогино методу `document.querySelector`:\n``"
  },
  {
    "path": "tech/nvm.md",
    "chars": 714,
    "preview": "# NVM for Windows\n## Get started\n* Open [nvm-windows releases](https://github.com/coreybutler/nvm-windows/releases)\n* Do"
  },
  {
    "path": "tech/pip.md",
    "chars": 2320,
    "preview": "## О репозитории Python Package Index\n**Python Package Index (PyPI)** - это *репозиторий*, *хранящий программное обеспеч"
  }
]

About this extraction

This page contains the full source code of the Max-Starling/Notes GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 72 files (1.1 MB), approximately 341.6k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!