Финальный срез
Финальный срез
Work with backend (AJAX, HTTP requests, headers, etc)
- What is https
- What is status codes
- What is AJAX
- What is JSON
- Basic usage of XHR (how to make simple request and handle answer)
- Basic usage of fetch() (how to make simple request and handle answer
- CRUD HTTP methods
- What is REST
- CORS what is it, why do we need them
- Difference between PUT, PATCH, POST
- Work with json responce
- Set headers in request
- How to get around CORS
- Идемпотентность и кэширование
- Clone objects with JSON methods
- How to add request body
- How to send requests with different content types and what is it for
- Read the response that came from the back-end and process it (how to save the file, how to filter out unnecessary data before using it in the target function)
- Протокол JSONP
- Использование скриптов в fetch
Development methodologies (agile and beyond)
- Definition of agile
- Definition of scrum and kanban. Base concept
- Understanding of the purpose of using Jira systems
- Understanding of primary SDLC models
- Agile vs scrum/kanban
- Providing estimates of efforts required for implementing the tasks
- Fundamental concepts of group work and version control
- Basic operations
- Gitflow
- Branching, tagging, merging changes from specific branch
- Blaming (annotate)
- Reseting vs reverting changes
- Git reset
- Git merge, git rebase
- Git cherry pick
- Git stash
Unit-tests, testing pyramid, integration tests
- Basic concepts: Test Plan, Test Suite, Test Case
- Testing Pyramid
- Properties of a good unit test
- F.I.R.S.T
- JS Unit basics
- Test first development techniques (TDD and BDD)
Common (functions, hoisting, work with Objects, etc)
- Types of functions and their differences, hoisting function declaration
- Destructing assigments, spread operator
- Optional mark. Object properties optional chain
- Work with array (map, filter, reduce, find, includes, some etc.)
- What is garbage collector? Why do we need it?
- Keys, values, fromEntries, entries methods
- Flat, flatMap, includes, Array.from() methods
- Object cloning
- Array cloning
- What is RegExp
- Two methods of RegExp object
- Two ways to create RegExp
- Basic RegExp pattern (character classes)
- Квантификаторы
Methods of Object, Array, Function.prototype
- Object.keys
- Object.values
- for/in
- Map vs forEach
- Find, filter, some, includes
- Reduse
- Clone object
- Sort
- Object freeze, seal
- Object defined writable enumerable, definenProperty, For in в прототип
- Promises vs callbacks
- Examples of async functions
- States of promises
- Finally
- Exceptions/errors handling
- How to use then to catch errors
- Проваливание промисов (Promise Chaining)
- DOM events
- Handling (add/remove event handlers)
- Basic types of DOM events
- Ways to prevent DOM event
- Events propagation (bubbling/capturing)
- Events delegation
- Стадии погружение-target-всплытие
Semantic, Critical Rendering path, block/inline elements
- Basic scheme for HTML document
- HTML symbols usage
- Text formatting, paragraphs
- HTML links. Link target
- HTML tables
- Adding of scripts
- Basic html elements (block, input, nav, select, p, li,button, forms, etc.
- Difference between block and inline elements
- Media (basic understanding what is it ond for what)
- Critical path of rendering
Selector types, Selector weight, styles that are inherited
- Frameworks layout technique
- Selectors and their weight
- Positioning
- Margings vs paddings
- Fonts adding
- Element visibility. Ways to hide element
- Inline/block/block-inline elements, difference
- Z-index
- Types of position
- Types of display
- Flexbox layout technique
- Responsive design (concept understanding)
- CSS Box Model
- !important (> + ~)
- What are hooks in React?
- The ability to explain the benefits of hooks in yourown words
- Good understanding of hooks
- Why you can't use hooks inside conditions, loopsand nested functions?
- How to implement branching in hooks?
- Optimize hooks
- Writing custom hooks
- useImperativeHandle, useTransition, useDefferedValue, useMemo, useCallback
- useId
- Зачем возвращать функцию в useEffect?
- Understanding the concept of React.Context
- Ability to apply contexts
- Для чего нужен Concumer, contextType в классах
- Understanding how component bubbling works
- Working with bubbling in components
- Understanding How Portal Bubbling Works (Basic understanding)
- Bubbling through portals (good understanding)
- Возможна ли обработка событий в реакте на погружении?
- Understanding what Reselect and Recompose are used for
- Ability to describe in your own words
- createSelector в RTK
- What are jest and enzyme for
- Basic understanding of writing tests
- Good understanding of how to test various entities: components, utilities, HOCs, modals
- Basic application of test patterns
- Working with TTD approach is possible
- Knows how to work with react-router
- Can explain in his own words how it works
- Hooks routing
- React-router API
- Good knowledge of what Route, Switch, Link and Redirect are for
- Что добавили в 6 версии react-router
useState/useReducer/useContext
Common
kiss, yagni
KISS (Keep It Simple, Stupid) - это принцип разработки программного обеспечения, который призывает к написанию простого, понятного и минимального кода для решения задачи. Он подчеркивает важность сохранения простоты и избегания излишней сложности в процессе разработки.
Принцип KISS можно применить к различным аспектам программирования:
- Простота кода: Код должен быть понятным и легко читаемым. Избегайте излишней сложности, ненужных усложнений и излишней абстракции. Старайтесь использовать простые и понятные алгоритмы и структуры данных.
- Простота проектирования: Архитектура и дизайн программы должны быть простыми и легко понятными. Избегайте переусложненных и избыточных архитектурных решений. Придерживайтесь принципа единственной ответственности (Single Responsibility Principle) и разделяйте функциональность на небольшие, независимые компоненты.
- Простота интерфейса: Интерфейсы и API должны быть простыми и интуитивно понятными для использования. Избегайте излишней сложности и запутанности взаимодействия с вашим программным обеспечением.
YAGNI (You Ain't Gonna Need It) - это принцип разработки программного обеспечения, который предлагает избегать реализации функциональности, которая в данный момент не требуется. Он подчеркивает важность минимизации избыточного и излишнего кода.
Принцип YAGNI можно применить в следующих случаях:
- Функциональность: Не реализуйте функциональность, которая в данный момент не требуется. Часто при разработке появляется соблазн добавить "запасные" функции, дополнительные возможности или обработку случаев, которые могут возникнуть в будущем. Однако, если эта функциональность не требуется прямо сейчас, лучше отложить ее реализацию до тех пор, пока она не станет действительно необходимой.
- Архитектурные решения: Избегайте переусложненных архитектурных решений, если они не требуются на текущем этапе разработки. Не стройте сложные и гибкие системы, если у вас нет явных требований на их функциональность. Простота архитектуры позволяет избежать излишней сложности, связанной с управлением и поддержкой избыточных модулей и компонентов.
- Общие решения: Не создавайте общие решения для задач, которые возникают в вашем конкретном проекте. Если вы не планируете использовать код или компоненты в других проектах или контекстах, избегайте избыточного обобщения и создания общих решений. Сосредоточьтесь на решении конкретных задач в текущем проекте.
Clean code from Martin
"Clean Code" (чистый код) - это концепция разработки программного обеспечения, предложенная Робертом Мартином (также известным как Uncle Bob). Он описал принципы и практики, которые помогают создавать читаемый, понятный и поддерживаемый код. Вот некоторые основные принципы "чистого кода", описанные Робертом Мартином:
- Именование: Имена переменных, функций, классов и других элементов кода должны быть понятными, описательными и соответствовать своему назначению. Избегайте сокращений, неясных аббревиатур и неинформативных имён. Хорошие имена помогают легче понимать код и делают его более самодокументируемым.
- Маленькие функции: Функции должны быть короткими, содержать только одну операцию и выполнять конкретную задачу. Маленькие функции облегчают чтение и понимание кода, а также упрощают повторное использование и тестирование.
- Комментарии: Избегайте излишних комментариев, которые повторяют то, что уже ясно из самого кода. Комментарии следует использовать для пояснения сложных алгоритмов, важных замечаний или документирования публичного API. Чистый код должен быть самодокументируемым и легко понятным без необходимости читать комментарии.
- Форматирование: Отформатируйте код таким образом, чтобы он выглядел аккуратно и однородно. Используйте правила отступов, пробелов, размещения фигурных скобок и других элементов форматирования, чтобы сделать код более читаемым и последовательным.
- Устранение дублирования: Избегайте повторения кода, поскольку это может привести к избыточности, ошибкам и затруднить его поддержку. Вместо этого, выносите общую функциональность в отдельные методы или классы и используйте их повторно.
- Тестирование: Пишите юнит-тесты для своего кода, чтобы обеспечить его работоспособность и устойчивость к изменениям. Чистый код должен быть легко тестируемым, и тесты должны быть понятными и покрывать основные сценарии использования.
Good understanding of nessessary of coments
Комментарии в программировании используются для документирования кода и предоставления дополнительной информации для разработчиков и других людей, работающих с кодом. Вот несколько основных причин, по которым комментарии полезны:
- Пояснение сложных участков кода: Комментарии могут помочь объяснить сложные алгоритмы, сложные логические условия или нетривиальные решения, которые могут быть трудными для понимания без дополнительных пояснений. Они помогают разработчикам лучше понять цель и намерения кода.
- Документация публичного API: Комментарии могут служить для документирования публичного API, чтобы другие разработчики могли легче использовать вашу библиотеку, классы или функции. Хорошая документация API помогает пользователям понять, как правильно использовать код, какие аргументы ожидаются и какие значения возвращаются.
- Замечания и предупреждения: Комментарии могут использоваться для указания на проблемы, потенциальные ошибки или важные замечания, связанные с кодом. Например, вы можете использовать комментарий, чтобы предупредить о возможных побочных эффектах или ограничениях при использовании определенного кода.
- История изменений: Комментарии могут помочь отслеживать изменения в коде и служить в качестве истории разработки. Вы можете использовать комментарии, чтобы указать, когда и кем был внесен определенный код или изменение, и объяснить причины для этого изменения.
- Улучшение читаемости: Хорошо размещенные комментарии могут значительно улучшить читаемость кода. Они могут помочь выделить структуру программы, разделить ее на логические блоки и сделать код более понятным для других разработчиков.
Antipatterns
Antipatterns (антипаттерны) в программировании — это негативные и нежелательные практики, которые противоречат принципам разработки программного обеспечения. Они описывают типичные ошибки, которые разработчики могут совершать при проектировании, написании и поддержке кода. Рассмотрим несколько распространенных антипаттернов:
- God Object (божественный объект): В этом антипаттерне один объект или класс становится центром всей функциональности приложения. Он накапливает множество различных обязанностей и становится огромным и трудным для понимания и поддержки. Рекомендуется разделять функциональность на небольшие и независимые классы с ясно определенными ответственностями.
- Spaghetti Code (спагетти-код): Этот антипаттерн описывает запутанный, неструктурированный и трудно читаемый код с множеством взаимосвязей. В результате такого кода сложно понять его логику, добавлять новую функциональность или вносить изменения. Рекомендуется использовать модульность, разделение ответственности и хорошую структуру кода для избегания спагетти-кода.
- Copy-Paste Programming (копипаст-программирование): Этот антипаттерн описывает ситуацию, когда разработчик многократно копирует и вставляет код в разных частях программы, вместо его выноса в повторно используемые компоненты или функции. Это приводит к дублированию кода, усложняет его поддержку и увеличивает вероятность ошибок. Рекомендуется выделять общую функциональность в отдельные модули и использовать ее повторно.
- Magic Numbers (магические числа): Этот антипаттерн относится к использованию непонятных числовых значений в коде, без соответствующего комментария или объяснения. Это делает код трудным для понимания и поддержки. Рекомендуется заменять магические числа на константы или переменные с понятными именами, что делает код более ясным и самодокументируемым.
- Large Class (большой класс): Этот антипаттерн описывает классы, которые содержат слишком много полей, методов и ответственностей. Большие классы становятся сложными для понимания, тестирования и поддержки. Рекомендуется разбивать большие классы на более мелкие и сфокусированные классы с одной ответственностью (принцип единственной ответственности).
SOLID
SOLID - это аббревиатура, которая представляет пять основных принципов объектно-ориентированного программирования и проектирования. Эти принципы были введены Робертом Мартином (также известным как Uncle Bob) и помогают создавать гибкий, расширяемый и поддерживаемый код. Вот обзор каждого принципа SOLID:
- Принцип единственной ответственности (Single Responsibility Principle, SRP): Каждый класс или модуль должен быть ответственным только за одну задачу. Это означает, что класс должен иметь только одну причину для изменения. Разделение ответственности облегчает понимание и поддержку кода, а также позволяет легко вносить изменения.
Пример:
Представим, у нас есть классCar
, который отвечает за управление автомобилем и одновременно отображает информацию на панели приборов. Это нарушает принцип единственной ответственности, так как класс должен отвечать только за управление автомобилем, а отображение информации следует выделить в отдельный классDashboard
. - Принцип открытости/закрытости (Open/Closed Principle, OCP): Код должен быть открыт для расширения и закрыт для изменения. Это означает, что новую функциональность следует добавлять через расширение существующих классов, а не изменяя их. Использование абстракций, интерфейсов и наследования помогает создавать гибкий код.
Пример:
Допустим, у нас есть базовый классShape
и его подклассыCircle
иRectangle
. Если мы хотим добавить новую фигуру, например,Triangle
, то мы не должны изменять код в классахShape
,Circle
иRectangle
. Вместо этого мы создаем новый подклассTriangle
, который наследует базовый классShape
, и добавляем новую функциональность только в этом новом классе. - Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP): Объекты должны быть заменяемыми своими подтипами без изменения свойств корректности системы. Это означает, что классы-наследники должны быть взаимозаменяемыми с базовыми классами, и их использование не должно нарушать ожидаемое поведение системы. Это позволяет использовать полиморфизм и улучшает переиспользование кода.
Пример:
Рассмотрим классBird
, который имеет методfly()
. ПодклассPenguin
наследует классBird
, но пингвины не умеют летать. Если код обрабатывает объекты типаBird
, то вместо подклассаPenguin
должен быть создан отдельный классNonFlyingBird
, который также наследует классBird
. - Принцип разделения интерфейса (Interface Segregation Principle, ISP): Клиенты не должны зависеть от интерфейсов, которые они не используют. Это означает, что интерфейсы должны быть маленькими, специфичными и адаптированными под нужды конкретных клиентов. Это помогает избежать избыточной связности и снижает эффект "раздувания" интерфейсов.
Пример:
Предположим, у нас есть интерфейсAnimal
, который содержит методыeat()
иfly()
. Однако не все животные умеют летать. Чтобы следовать принципу разделения интерфейса, мы должны разделить интерфейс на два:FlyingAnimal
с методомfly()
иNonFlyingAnimal
с методомeat()
. Теперь классы животных могут реализовывать только нужные им интерфейсы. - Принцип инверсии зависимостей (Dependency Inversion Principle, DIP): Модули верхнего уровня не должны зависеть от модулей нижнего уровня, а оба должны зависеть от абстракций. Это означает, что зависимости между классами и модулями должны основываться на абстракциях, а не на конкретных реализациях. Использование инверсии зависимостей позволяет создавать слабо связанный код и упрощает тестирование и замену компонентов.
Пример:
Представим, что у нас есть классNotificationService
, который зависит от классаEmailSender
. Вместо того, чтобы классNotificationService
напрямую создавать экземплярEmailSender
, мы можем внедрить зависимость через интерфейс. Создадим интерфейсMessageSender
, который будет реализовываться классомEmailSender
. Теперь классNotificationService
зависит от абстракции (интерфейсаMessageSender
), а не от конкретной реализации. Это облегчает заменуEmailSender
на другой класс-отправитель без изменения кодаNotificationService
.
Functional programming
What is FP
Функциональное программирование (Functional Programming) - это парадигма программирования, в которой основной упор делается на использование функций как основных строительных блоков программы. Основная идея функционального программирования заключается в том, что программа строится путем комбинирования и применения функций без изменения состояния и мутации данных.
Вот некоторые ключевые особенности функционального программирования:
- Функции являются первоклассными объектами: В функциональном программировании функции могут быть присвоены переменным, переданы в качестве аргументов другим функциям и возвращены из функций как результат.
- Безымянные функции (лямбда-функции): Функциональное программирование поддерживает использование безымянных функций, которые могут быть определены на месте и переданы в качестве аргументов или сохранены в переменных.
- Неизменяемость данных: В функциональном программировании данные считаются неизменяемыми, то есть после создания данные не могут быть изменены. Вместо этого функции создают новые данные на основе существующих.
- Отсутствие побочных эффектов: Функции в функциональном программировании должны быть чистыми, то есть они не должны иметь побочных эффектов, таких как изменение глобального состояния или ввод-вывод. Функции должны быть полностью детерминированными, и их результат должен зависеть только от входных аргументов.
- Рекурсия: В функциональном программировании рекурсия является одним из основных методов итерации. Циклы, как в императивных языках, заменяются рекурсивными вызовами функций.
- Композиция функций: Функциональное программирование поощряет композицию функций, то есть создание новых функций путем комбинирования существующих функций.
Work with backend
What is https
HTTP (HyperText Transfer Protocol) и HTTPS (HTTP Secure) - это протоколы передачи данных в сети Интернет.
HTTP является протоколом, который определяет, как клиент (например, веб-браузер) и сервер взаимодействуют для передачи данных. Он обеспечивает средства для запроса и получения данных, таких как веб-страницы, из сервера. HTTP работает поверх протокола TCP (Transmission Control Protocol) и использует порт 80.
HTTPS - это расширение протокола HTTP, которое обеспечивает защищенную передачу данных путем шифрования. Он добавляет слой безопасности к HTTP с помощью использования протокола SSL/TLS (Secure Sockets Layer/Transport Layer Security) для защиты данных от несанкционированного доступа или изменений в процессе передачи. HTTPS использует порт 443 вместо порта 80, который используется HTTP.
Основное отличие между HTTP и HTTPS заключается в том, что данные, передаваемые по HTTPS, шифруются с использованием SSL/TLS, что обеспечивает конфиденциальность и целостность данных. Это особенно важно при передаче чувствительной информации, такой как логины, пароли, банковские данные и т.д. HTTPS защищает данные от перехвата и несанкционированного чтения или модификации.
Веб-сайты, использующие HTTPS, обычно имеют SSL-сертификат, который удостоверяет подлинность сервера и устанавливает защищенное соединение с клиентом. Браузеры обычно отображают замок в адресной строке для указания, что соединение защищено.
What is status codes
Status codes (коды состояния) являются числовыми значениями, возвращаемыми веб-сервером в ответ на HTTP-запросы, отправленные клиентским приложением. Они предоставляют информацию о состоянии выполнения запроса и позволяют клиенту и серверу взаимодействовать и обмениваться информацией.
Коды состояния состоят из трех цифр и разделяются на пять различных классов:
1xx: Информационные коды состояния. Эти коды указывают на то, что сервер получил запрос, и процесс обработки продолжается. Например, 100 (Continue) сообщает клиенту, что он может продолжать отправлять оставшуюся часть запроса.
2xx: Коды состояния успешного выполнения. Они указывают, что запрос клиента был успешно принят и обработан сервером. Например, 200 (OK) означает успешное выполнение запроса.
3xx: Коды состояния перенаправления. Они указывают, что клиент должен выполнить дополнительные действия для завершения запроса. Например, 301 (Moved Permanently) сообщает клиенту, что запрашиваемый ресурс был перемещен на другой URL.
4xx: Коды состояния ошибок клиента. Они указывают на ошибку, произошедшую на стороне клиента, и требуют дополнительных действий для корректной обработки запроса. Например, 404 (Not Found) сообщает, что запрашиваемый ресурс не найден на сервере.
5xx: Коды состояния ошибок сервера. Они указывают на ошибку, произошедшую на стороне сервера при обработке запроса. Например, 500 (Internal Server Error) означает внутреннюю ошибку сервера.
Клиентские приложения используют коды состояния для понимания результата своих запросов и принятия соответствующих действий. Например, при получении кода состояния 200 клиент может обработать полученные данные, а при получении кода состояния 404 клиент может отобразить сообщение об ошибке или предпринять другие меры.
What is AJAX
AJAX (Asynchronous JavaScript and XML) - это набор технологий, которые позволяют обновлять содержимое веб-страницы без перезагрузки всей страницы. Он позволяет асинхронно обмениваться данными между клиентским браузером и сервером, что позволяет создавать более динамические и отзывчивые пользовательские интерфейсы.
- JavaScript: AJAX использует язык программирования JavaScript для выполнения асинхронных запросов к серверу и обработки полученных данных на стороне клиента.
- XML или JSON: Обычно данные передаются в формате XML или JSON. XML (eXtensible Markup Language) и JSON (JavaScript Object Notation) представляют структурированные форматы данных, которые легко читать и обрабатывать как на клиентской, так и на серверной стороне.
- XMLHttpRequest (XHR): Это объект, предоставляемый браузером, который позволяет выполнять асинхронные HTTP-запросы к серверу без перезагрузки страницы. Он обеспечивает средства для отправки запросов, получения ответов и обработки данных на стороне клиента.
- Callback-функции: AJAX использует callback-функции для обработки полученных данных и выполнения действий после завершения запроса. Callback-функции вызываются после получения ответа от сервера и могут обновлять содержимое страницы, взаимодействовать с DOM (Document Object Model) и выполнять другие действия.
- Более отзывчивый пользовательский интерфейс: AJAX позволяет обновлять только необходимую часть страницы, не перезагружая всю страницу. Это создает более плавные и быстрые взаимодействия с пользователем.
- Уменьшение нагрузки на сервер: Поскольку AJAX позволяет передавать только необходимые данные, серверу требуется меньше ресурсов для обработки запросов и передачи данных.
- Повышение производительности: Использование AJAX позволяет загружать данные асинхронно, параллельно с выполнением других операций на странице. Это улучшает общую производительность веб-приложений.
- Улучшенная пользовательская навигация: AJAX позволяет создавать динамические веб-страницы, которые могут загружать данные по мере необходимости, обновлять информацию и предоставлять более интуитивные функции навигации.
What is JSON
JSON (JavaScript Object Notation) - это легкий формат обмена данными, основанный на синтаксисе JavaScript. Он широко используется для передачи данных между клиентскими приложениями и серверами в веб-разработке.
- Простота чтения и записи: JSON представляет данные в виде пар "ключ-значение" и использует понятный для человека синтаксис, который легко читать и писать.
- Независимость от языка: JSON является независимым от языка форматом данных, что означает, что его можно использовать с любым языком программирования, способным обрабатывать текстовые данные.
- Поддержка различных типов данных: JSON поддерживает базовые типы данных, такие как строки, числа, логические значения (true/false), массивы и объекты. Это позволяет представлять структурированные данные.
{ "name": "John", "age": 30, "city": "New York" }
[ "apple", "banana", "orange" ]
- Легкость чтения и записи: JSON использует простой и понятный синтаксис, что упрощает его чтение и создание как людьми, так и компьютерами.
- Широкая поддержка: Поддержка JSON встроена во многие языки программирования, что делает его универсальным форматом данных для обмена информацией между различными системами и платформами.
- Компактность: JSON является компактным форматом, который требует меньше места для хранения и передачи данных по сети, чем некоторые другие форматы, такие как XML.
- Простота парсинга: JSON легко парсится и преобразуется в структуры данных, поддерживаемые в различных языках программирования. Это делает его удобным для обработки данных на стороне сервера и клиента.
Basic usage of XHR (how to make simple request and handle answer)
javascript // Создание нового объекта XMLHttpRequest var xhr = new XMLHttpRequest(); // Установка метода запроса и URL xhr.open('GET', 'https://api.example.com/data', true); // Установка заголовков, если необходимо // xhr.setRequestHeader('Content-Type', 'application/json'); // Обработка события загрузки xhr.onload = function() { if (xhr.status === 200) { // Обработка успешного ответа var response = JSON.parse(xhr.responseText); console.log(response); } else { // Обработка ошибки console.log('Ошибка: ' + xhr.status); } }; // Обработка события ошибки xhr.onerror = function() { console.log('Ошибка сети'); }; // Отправка запроса xhr.send();
В этом примере создается новый объект XMLHttpRequest с помощью new XMLHttpRequest()
. Затем вызывается метод open()
для установки метода запроса (GET, POST, и т. д.) и URL-адреса, к которому будет выполнен запрос.
Если необходимо, можно установить дополнительные заголовки запроса с помощью метода setRequestHeader()
. Затем определяются обработчики событий onload
и onerror
. Обработчик onload
вызывается, когда запрос успешно завершается с HTTP-статусом 200 (ОК), и в этом обработчике можно обработать полученные данные.
Внутри обработчика onload
можно выполнить различные операции с ответом. В примере ответ предполагается в формате JSON, поэтому с помощью JSON.parse()
он преобразуется в объект JavaScript для дальнейшей обработки.
Если запрос не удалось выполнить, будет вызван обработчик onerror
, который сообщит об ошибке сети.
Наконец, вызывается метод send()
для отправки запроса на сервер.
Обратите внимание, что в примере используется асинхронный режим (true
передается в xhr.open()
). Если вы хотите выполнить запрос синхронно, установите false
, но помните, что это может блокировать выполнение другого кода до завершения запроса.
Basic usage of fetch() (how to make simple request and handle answer
fetch('https://api.example.com/data') .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { // Обработка данных, полученных из ответа console.log(data); }) .catch(error => { // Обработка ошибок console.error('Error:', error); });
В этом примере мы делаем GET-запрос к https://api.example.com/data
. Если ответ сервера содержит код состояния, отличный от успешного (200 OK
), будет выброшено исключение. В противном случае, мы предполагаем, что ответ представлен в формате JSON и вызываем метод response.json()
, чтобы преобразовать его в объект JavaScript.
Затем во втором блоке .then()
мы можем обрабатывать данные, полученные из ответа, например, выводить их в консоль.
Если возникает ошибка во время выполнения запроса, она будет перехвачена в блоке .catch()
и можно будет обработать ошибку, например, вывести ее в консоль.
Убедитесь, что вы замените 'https://api.example.com/data'
на фактический URL вашего API.
CRUD HTTP methods
CRUD (Create, Read, Update, Delete) - это основные операции, которые можно выполнить над ресурсами при использовании протокола HTTP. Каждая операция имеет соответствующий HTTP-метод, который указывает на тип операции, которую нужно выполнить.
Вот описание каждого из CRUD-методов:
- CREATE (Создание) - создание нового ресурса. Для этого используется HTTP-метод
POST
. При выполнении POST-запроса данные отправляются на сервер для создания нового ресурса. Обычно созданный ресурс возвращается в ответе от сервера, и может быть присвоен новый уникальный идентификатор. - READ (Чтение) - получение информации о ресурсе или наборе ресурсов. Для этого используется HTTP-метод
GET
. GET-запросы используются для получения данных с сервера. Вы можете указать определенный ресурс, который вам нужен, в URL-адресе запроса, или использовать параметры запроса для настройки результатов. - UPDATE (Обновление) - обновление существующего ресурса. Для этого используется HTTP-метод
PUT
илиPATCH
. PUT-запросы используются для полного обновления ресурса, в то время как PATCH-запросы используются для частичного обновления ресурса. В обоих случаях вы отправляете данные на сервер, которые будут использованы для обновления ресурса. - DELETE (Удаление) - удаление существующего ресурса. Для этого используется HTTP-метод
DELETE
. DELETE-запросы отправляются на сервер для удаления указанного ресурса. После успешного выполнения запроса ресурс будет удален.
What is REST
REST (Representational State Transfer) - это архитектурный стиль, используемый при проектировании распределенных систем, таких как веб-сервисы. Он представляет собой набор принципов и ограничений, которые помогают создавать масштабируемые, гибкие и расширяемые системы.
- Клиент-серверная архитектура: Взаимодействие между клиентом и сервером происходит через явное разделение обязанностей. Клиент отвечает за пользовательский интерфейс и взаимодействие с пользователем, в то время как сервер отвечает за обработку запросов и предоставление данных.
- Без состояния (Stateless): Каждый запрос от клиента должен содержать всю необходимую информацию для его обработки, без использования сохраняемого состояния на сервере. Состояние клиента, если необходимо, может быть сохранено и передано серверу вместе с каждым запросом.
- Кэширование: Клиенты и промежуточные серверы могут кэшировать ответы сервера для повторного использования. Это позволяет снизить нагрузку на сервер и улучшить производительность.
- Единообразный интерфейс: REST определяет единообразный набор ограничений для взаимодействия с сервером. Он включает в себя использование уникальных идентификаторов ресурсов (URL), стандартные методы HTTP (GET, POST, PUT, DELETE) для операций над ресурсами, представление данных (например, JSON или XML) и самоописывающиеся сообщения.
- Слои: REST позволяет включать промежуточные серверы (прокси, кэш и другие) между клиентом и сервером для улучшения масштабируемости, без влияния на саму архитектуру.
CORS what is it, why do we need them
CORS (Cross-Origin Resource Sharing) - это механизм веб-безопасности, который позволяет веб-страницам запрашивать ресурсы с других источников (доменов) в браузере. Стандарт Same-Origin Policy запрещает такие запросы из соображений безопасности, но с помощью CORS сервер может предоставить разрешение на доступ к своим ресурсам с определенных доменов.
При выполнении AJAX-запросов или других запросов с помощью JavaScript с веб-страницы на домен A к ресурсам на домене B, браузер будет делать предварительный запрос (preflight request) с методом OPTIONS на домен B для проверки разрешено ли совершать такие запросы. Затем, если сервер на домене B возвращает соответствующий заголовок CORS в ответе, браузер разрешает запрос и веб-страница получает доступ к ресурсам на другом домене.
Заголовки CORS, которые используются для управления доступом к ресурсам, включают:
- Access-Control-Allow-Origin: Заголовок указывает список разрешенных доменов, которые могут делать запросы к ресурсам. Например,
Access-Control-Allow-Origin: https://www.example.com
разрешает доступ только с указанного домена. - Access-Control-Allow-Methods: Заголовок указывает разрешенные методы HTTP, которые можно использовать для запроса. Например,
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
разрешает использование этих методов. - Access-Control-Allow-Headers: Заголовок указывает разрешенные заголовки, которые могут быть использованы в запросе. Например,
Access-Control-Allow-Headers: Content-Type, Authorization
разрешает использование заголовковContent-Type
иAuthorization
. - Access-Control-Allow-Credentials: Заголовок указывает, разрешено ли передавать учетные данные (как куки, аутентификационные токены) при выполнении запросов. Значение заголовка может быть
true
илиfalse
.
Difference between PUT, PATCH, POST
POST, PUT и PATCH - это методы HTTP, используемые для взаимодействия с сервером и выполнения операций изменения данных. Вот их основные отличия:
- POST (Создание): HTTP-метод POST используется для создания новых ресурсов на сервере. Когда вы отправляете POST-запрос, вы отправляете данные на сервер для создания нового ресурса. Сервер обычно генерирует новый уникальный идентификатор для созданного ресурса и возвращает его в ответе. Повторные запросы с использованием того же POST-запроса могут создавать дубликаты ресурсов.
- PUT (Замена): HTTP-метод PUT используется для полного замены существующего ресурса на сервере или создания нового ресурса с определенным идентификатором. При выполнении PUT-запроса вы отправляете данные, которые полностью заменят указанный ресурс на сервере. Если ресурс с указанным идентификатором уже существует, он будет заменен на новый. Если ресурс не существует, сервер может создать его с указанным идентификатором.
- PATCH (Частичное обновление): HTTP-метод PATCH используется для частичного обновления существующего ресурса на сервере. При выполнении PATCH-запроса вы отправляете только измененные данные или инструкции об обновлении конкретных полей ресурса. Это позволяет обновлять только необходимые части ресурса, минимизируя передаваемые данные и снижая потенциальные конфликты при одновременном обновлении ресурса.
Work with json responce
Работа с JSON-ответами в JavaScript довольно проста. Вот несколько шагов, которые могут помочь вам в этом процессе:
- Парсинг JSON: Сначала вам нужно преобразовать JSON-строку в объект JavaScript. Для этого используйте функцию
JSON.parse()
. Например:
var jsonString = '{"name":"John", "age":30, "city":"New York"}'; var obj = JSON.parse(jsonString); console.log(obj.name); // Выведет "John" console.log(obj.age); // Выведет 30 console.log(obj.city); // Выведет "New York"
- Обращение к данным в объекте: После преобразования JSON в объект, вы можете получить доступ к его свойствам с помощью точечной нотации или квадратных скобок. Например:
console.log(obj.name); // Выведет "John" console.log(obj["age"]); // Выведет 30
- Преобразование объекта в JSON: Если вы хотите преобразовать объект JavaScript в JSON-строку, используйте функцию
JSON.stringify()
. Например:
var obj = { name: "John", age: 30, city: "New York" }; var jsonString = JSON.stringify(obj); console.log(jsonString); // Выведет '{"name":"John","age":30,"city":"New York"}'
- Обработка асинхронных JSON-запросов: Если вы получаете JSON-ответ через асинхронный запрос, например, с использованием XMLHttpRequest или Fetch API, вы можете использовать метод
.json()
для преобразования ответа в объект JSON. Например:
javascript fetch('example.com/api/data') .then(response => response.json()) .then(data => { console.log(data); // Работайте с данными JSON-ответа здесь }) .catch(error => { console.error('Ошибка:', error); });
Set headers in request
Для установки заголовков в Fetch API вы можете использовать второй параметр функции fetch()
, который является объектом с настройками запроса. В этом объекте вы можете указать заголовки с помощью свойства headers
. Вот пример:
javascript fetch('example.com/api/data', { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your_token' } }) .then(response => response.json()) .then(data => { // Обработка полученных данных }) .catch(error => { console.error('Ошибка:', error); });
В приведенном примере установлены два заголовка: Content-Type
и Authorization
. Вы можете добавить свои заголовки, указав их в объекте headers
. Обратите внимание, что значения заголовков должны быть строками.
Заголовок Content-Type
указывает тип контента тела запроса и обычно используется при отправке данных. В примере он установлен в application/json
, чтобы указать, что тело запроса является JSON-данными.
Заголовок Authorization
используется для аутентификации и может содержать токен доступа или другие данные, необходимые для проверки подлинности запроса.
Вы можете добавить сколько угодно заголовков, просто добавьте их в объект headers
.
How to get around CORS
Cross-Origin Resource Sharing (CORS) - это механизм безопасности веб-браузера, который ограничивает запросы JavaScript на чтение ответов с сервера, находящегося на другом домене, порту или протоколе. Из соображений безопасности браузеры не позволяют обойти политику CORS с помощью JavaScript в стандартных условиях.
Однако, есть несколько способов обойти политику CORS, в зависимости от контекста:
- Использование прокси-сервера: Вы можете настроить прокси-сервер на своем собственном домене, который перенаправляет запросы на внешний сервер и добавляет необходимые заголовки CORS. Таким образом, ваш JavaScript-код будет делать запросы на ваш прокси-сервер, который затем отправит запрос на внешний сервер и вернет ответ. При этом браузер будет рассматривать запросы как локальные и не будет применять политику CORS.
- Использование JSONP: JSONP (JSON with Padding) - это техника, позволяющая обойти политику CORS путем включения скрипта с внешнего домена в вашу HTML-страницу. Вместо отправки запроса на сервер напрямую, вы добавляете
<script>
тег на вашу страницу, указывая URL-адрес с параметром колбэка. Сервер возвращает JSON-данные, обернутые в вызов указанной функции. Однако, JSONP может быть ограничен и не поддерживаться всеми серверами. - Использование серверных техник: Если у вас есть доступ и контроль над серверной стороной, вы можете настроить сервер таким образом, чтобы он отправлял заголовки CORS, разрешая запросы с вашего домена. Это может включать добавление заголовков
Access-Control-Allow-Origin
и других необходимых заголовков, чтобы указать разрешенные домены, методы и заголовки.
Идемпотентность и кэширование
Идемпотентность и кэширование являются важными концепциями в области компьютерных наук, особенно в контексте разработки веб-приложений и архитектуры сетей. Давайте рассмотрим каждую из них по отдельности.
Идемпотентность описывает свойство операции, которая может быть применена несколько раз, но результат будет таким же, как при ее единичном применении. Другими словами, повторное применение идемпотентной операции не вызовет изменений в системе после первого применения.
Примеры идемпотентных операций включают чтение данных из базы данных, удаление файла, отправку электронной почты и др. Если мы повторим эти операции несколько раз, состояние системы не изменится после первого применения операции.
Идемпотентность особенно важна в распределенных системах и сетевых протоколах, где возможны повторные запросы из-за сбоев в сети или повторных попыток пользователя. Она позволяет гарантировать, что повторные запросы не приведут к нежелательным или непредсказуемым изменениям в системе.
Кэширование - это процесс сохранения копии данных или результата операции для последующего быстрого доступа. Кэш - это временное хранилище данных, которое обычно располагается ближе к потребителю данных или операции, чем источник данных.
Когда приложение или система выполняет операцию, результат может быть сохранен в кэше. Если та же операция будет запрошена позже, система может использовать сохраненный результат из кэша вместо выполнения операции снова. Это улучшает производительность, так как доступ к данным в кэше обычно быстрее, чем к источнику данных.
Кэширование широко применяется в веб-приложениях для ускорения загрузки страниц, уменьшения нагрузки на сервер и снижения времени отклика. Примеры кэширования включают кэширование веб-страниц, кэширование запросов к базе данных, кэширование изображений и др.
Однако кэширование может стать проблемой, если сохраненные данные устаревают или если кэш не синхронизирован с источником данных. В таких случаях возникает проблема согласованности данных, и механизмы управления кэшем, такие как проверка актуальности данных (cache validation) и инвалидация кэша (cache invalidation), становятся важными для поддержания целостности и актуальности данных.
Request parameters and JSON
Clone objects with JSON methods
Методы JSON (JavaScript Object Notation) в языке программирования JavaScript позволяют преобразовывать объекты в строковое представление JSON и обратно. Однако преобразование объекта в JSON и обратно не создает полную клонированную копию объекта, а лишь создает новый объект с теми же свойствами и значениями.
Чтобы клонировать объект с использованием методов JSON, вы можете использовать следующий подход:
var originalObj = { /* ваш исходный объект */ }; var jsonString = JSON.stringify(originalObj);
var clonedObj = JSON.parse(jsonString);
Теперь clonedObj
будет содержать клонированную версию исходного объекта originalObj
.
Однако стоит отметить, что этот метод клонирования имеет некоторые ограничения:
- Свойства, которые не являются сериализуемыми в JSON (например, функции или символы), будут потеряны при клонировании.
- Свойства, содержащие ссылки на другие объекты, также не будут глубоко клонированы, а будут просто скопированы ссылки на них. Это означает, что изменения в одном из клонированных объектов могут повлиять на другие объекты.
Если вам нужно создать полную глубокую копию объекта, включая все вложенные объекты, функции и прочие типы данных, вам потребуется использовать другие методы, такие как рекурсивное клонирование или использование сторонних библиотек.
How to add request body
В JavaScript с использованием Fetch API вы можете добавить тело запроса с помощью параметра body
. Вот пример:
fetch(url, { method: 'POST', // или другой метод, например, 'GET', 'PUT', 'DELETE' headers: { 'Content-Type': 'application/json' // Укажите тип содержимого тела запроса }, body: JSON.stringify(data) // Преобразуйте данные в JSON-строку }) .then(response => { // Обработка ответа от сервера }) .catch(error => { // Обработка ошибок });
В этом примере мы отправляем POST-запрос на указанный url
. В тело запроса добавлены данные data
, которые были преобразованы в JSON-строку с помощью JSON.stringify()
.
Обратите внимание, что вы также можете использовать другие методы запроса, такие как 'GET', 'PUT', 'DELETE', в зависимости от ваших потребностей.
How to send requests with different content types and what is it for
Отправка запросов с различными типами содержимого (Content-Type) позволяет указать серверу, какой формат данных вы отправляете в теле запроса. Это важно для правильного понимания и обработки данных сервером. Вот некоторые распространенные типы содержимого и их назначение:
- application/json: Используется для отправки данных в формате JSON (JavaScript Object Notation). Этот тип содержимого широко применяется в веб-разработке для передачи структурированных данных между клиентом и сервером.
- application/x-www-form-urlencoded: Используется для отправки данных, закодированных в URL-формате. Этот тип содержимого часто используется при отправке данных формы HTML.
- multipart/form-data: Используется для отправки файлов и данных формы, которые могут содержать бинарные данные. Этот тип содержимого позволяет отправлять файлы через HTTP-запросы.
- text/plain: Используется для отправки простого текста без форматирования или структурирования.
Когда вы отправляете запрос на сервер, правильное указание типа содержимого позволяет серверу правильно интерпретировать и обработать данные. Например, если вы отправляете данные в формате JSON, сервер ожидает получить JSON-объект и может легко распарсить их для дальнейшей обработки.
Чтобы указать тип содержимого в запросе, вы можете использовать заголовок Content-Type
. Например:
fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' // Указываем тип содержимого как application/json }, body: JSON.stringify(data) })
В этом примере мы указываем тип содержимого как application/json
, чтобы сервер знал, что мы отправляем данные в формате JSON.
Read the response that came from the back-end and process it (how to save the file, how to filter out unnecessary data before using it in the target function)
fetch(url) .then(response => response.json()) .then(data => { // Фильтрация данных перед использованием const filteredData = data.filter(item => item.active === true); // Использование отфильтрованных данных в целевой функции targetFunction(filteredData); }) .catch(error => { // Обработка ошибок });
обработке и фильтрации данных, полученных от сервера:
- Получение ответа: После отправки запроса на сервер и получения ответа, вы можете использовать методы предоставленные вашей платформой для чтения ответа. Например, в JavaScript с использованием Fetch API, вы можете использовать метод
.json()
для преобразования ответа в объект JSON. - Сохранение ответа: Если вы хотите сохранить ответ для дальнейшего использования, вы можете сохранить его в переменной или структуре данных, которая наиболее удобна для вашего приложения или сценария.
- Фильтрация данных: Если вам нужно отфильтровать данные перед их использованием в целевой функции, вы можете применить необходимые операции фильтрации или преобразования на полученных данных. Например, вы можете использовать методы массива (Array) или объекта (Object) для фильтрации, сортировки или преобразования данных.
Протокол JSONP
Протокол JSONP (JSON with Padding) является способом обмена данными между веб-страницами, расположенными на разных доменах (Cross-Domain). Он был разработан для обхода ограничений безопасности, связанных с политикой одного источника (Same Origin Policy) браузеров.
При использовании стандартного AJAX-запроса (XMLHttpRequest) браузеры применяют политику одного источника, которая запрещает AJAX-запросы на другие домены. Однако с помощью JSONP можно обойти это ограничение и обмениваться данными между доменами.
Вот как работает протокол JSONP:
- Веб-страница, находящаяся на одном домене (назовем ее "Домен A"), создает скриптовый тег
<script>
с указанием URL запроса на другой домен (назовем его "Домен B"). В URL запроса обычно включается колбэк-функция, которую сервер на Домене B будет вызывать с данными. - Сервер на Домене B обрабатывает запрос и возвращает данные, обернутые в вызов колбэк-функции. Например, если колбэк-функция называется
callbackFunc
, сервер вернет что-то вродеcallbackFunc({ "data": "Some data" });
. - Браузер, находящийся на Домене A, загружает скрипт с Домена B, и когда он выполнится, вызовется колбэк-функция, передавая полученные данные из ответа сервера.
Преимущества использования протокола JSONP включают возможность обмена данными между разными доменами без нарушения политики одного источника и простоту реализации.
Однако стоит учесть, что JSONP имеет свои недостатки и ограничения:
- Он подвержен проблемам безопасности, так как загружаемый скрипт может быть вредоносным.
- Он ограничивается только использованием метода GET для запросов.
- Он не поддерживает обработку ошибок HTTP-статусов, так как загружается как скрипт.
В современной разработке веб-приложений более безопасные и гибкие методы, такие как Cross-Origin Resource Sharing (CORS) и использование серверных прокси, предпочтительнее по сравнению с протоколом JSONP.
Использование скриптов в fetch
В Fetch API существует возможность использования скриптов в запросах с помощью параметра mode
и значения script
. Это позволяет загружать и выполнить удаленные скрипты, возвращаемые сервером, аналогично использованию тега <script>
в HTML.
Вот пример использования скриптов в Fetch API:
javascriptCopy codefetch(url, { method: 'GET', mode: 'script' }) .then(response => { // Скрипт успешно загружен и выполнен }) .catch(error => { // Обработка ошибок });
В этом примере мы отправляем GET-запрос на указанный url
и указываем mode
как script
. Затем Fetch API загружает скрипт, возвращенный сервером, и выполняет его. Если скрипт загружен и выполнен успешно, выполнится блок .then()
. Если возникла ошибка при загрузке или выполнении скрипта, выполнится блок .catch()
.
- Использование скриптов в Fetch API доступно только для GET-запросов.
- При использовании режима
script
ответ от сервера должен быть действительным JavaScript-кодом. В противном случае, возникнет ошибка при выполнении скрипта. - Загруженные скрипты выполняются в глобальной области видимости, поэтому они могут воздействовать на вашу страницу и другие скрипты, загруженные на страницу. Будьте осторожны при загрузке скриптов из ненадежных источников.
Development methodologies (agile and beyond)
Definition of agile
Agile - это подход к разработке программного обеспечения, который акцентирует внимание на гибкости, адаптивности и коллаборации. Он представляет собой набор принципов и ценностей, созданных для улучшения процесса разработки и достижения более успешных результатов.
В традиционных методологиях разработки, таких как Waterfall (ватерфол), разработка происходит в строго определенных фазах, последовательно следующих друг за другом. Agile подходит для ситуаций, когда требуется гибкость и возможность быстро реагировать на изменения, так как разработка осуществляется в итеративном цикле, состоящем из коротких временных отрезков, называемых спринтами.
Некоторые из основных принципов Agile включают:
- Работающее программное обеспечение является главной мерой прогресса.
- Распределение работы между самоорганизующимися командами.
- Частая итерация и адаптация планов на основе обратной связи.
- Сотрудничество с заказчиками и заинтересованными сторонами на протяжении всего процесса разработки.
- Предпочтение разговора лицом к лицу перед обширной документацией.
- Готовность быстро изменить направление разработки для достижения наилучших результатов.
Agile использует различные методологии, такие как Scrum, Kanban, Extreme Programming (XP) и другие, чтобы помочь командам эффективно работать в соответствии с принципами Agile. Эти методологии предоставляют набор инструментов и практик для планирования, управления и выполнения разработки программного обеспечения в гибкой и коллаборативной среде.
Definition of scrum and kanban. Base concept
Канбан и Скрам (Scrum) - это два популярных подхода к управлению проектами и разработке программного обеспечения, используемые в методологии Agile.
Канбан: Канбан - это метод управления рабочим процессом, разработанный японской компанией Toyota для оптимизации производственных процессов. В контексте разработки программного обеспечения, Канбан используется для визуализации рабочего потока, контроля объема работ и повышения эффективности команды.
Основные концепции Канбан включают в себя использование доски Канбан, которая представляет рабочий процесс в виде колонок и карточек. Каждая карточка представляет отдельную задачу или работу. Карточки перемещаются по колонкам в зависимости от текущего состояния работы. Канбан также основан на принципе ограничения количества задач, которые можно выполнять одновременно, чтобы избежать перегрузки команды.
Скрам: Скрам - это фреймворк для управления и разработки программного обеспечения, который также относится к методологии Agile. Он основан на итеративном и инкрементальном подходе к разработке, где работа разбивается на короткие временные интервалы, называемые спринтами.
Основные концепции Скрама включают в себя использование спринтов, которые обычно имеют длительность от 1 до 4 недель, и инкрементальное развитие продукта. Каждый спринт начинается с планирования, где определяются цели и задачи. Затем команда выполняет работы в течение спринта, уделяя особое внимание коллективной работе и регулярному обратному общению. Спринт заканчивается демонстрацией выполненной работы и обзором спринта, где команда анализирует результаты и планирует следующий спринт.
Основное отличие между Канбан и Скрам заключается в подходе к управлению рабочим процессом. Канбан сконцентрирован на визуализации и оптимизации потока работ, в то время как Скрам ориентирован на итеративное развитие и быструю адаптацию к изменениям. Выбор между Канбан и Скрамом зависит от особенностей проекта и предпочтений команды разработчиков.
Understanding of the purpose of using Jira systems
Jira - это популярный инструмент управления проектами, который широко используется в различных отраслях и командами разработки программного обеспечения. Основные цели использования Jira включают:
- Управление задачами и проектами: Jira предоставляет мощные функции для создания, отслеживания и управления задачами и проектами. Он позволяет создавать задачи, назначать ответственных, определять сроки выполнения, отслеживать прогресс работы и управлять приоритетами.
- Организация рабочего процесса: Jira предлагает гибкие возможности настройки рабочего процесса в соответствии с потребностями команды. Вы можете определить этапы выполнения задачи, настроить поток работы, установить правила перехода между статусами и настроить автоматические уведомления и предупреждения.
- Отслеживание ошибок и управление проблемами: Jira является мощным инструментом для отслеживания ошибок (багов) и управления проблемами (issues). Вы можете создавать и отслеживать ошибки, устанавливать их приоритеты, назначать ответственных и отслеживать процесс их устранения.
- Управление продуктовым бэклогом: Jira позволяет создавать и управлять продуктовым бэклогом, то есть списком требований и функциональностей, которые должны быть реализованы. Вы можете приоритезировать требования, определять их статус, устанавливать сроки и управлять изменениями в бэклоге.
- Коллаборация и коммуникация: Jira предоставляет функциональность для коммуникации и сотрудничества внутри команды. Вы можете оставлять комментарии, обсуждать задачи, прикреплять файлы и обмениваться информацией с другими участниками проекта.
- Отчетность и аналитика: Jira предлагает возможности для создания отчетов и аналитики проектов. Вы можете отслеживать метрики выполнения, производительности команды, временных затрат, распределения задач и многое другое. Это позволяет вам получить представление о состоянии проекта и принимать информированные решения.
Цели использования Jira могут варьироваться в зависимости от конкретных потребностей и контекста команды или организации. Однако в целом Jira помогает в упорядочении работы, повышении прозрачности процессов и содействует более эффективному управлению проектами.
Understanding of primary SDLC models
SDLC (Software Development Life Cycle) - жизненный цикл разработки программного обеспечения - включает в себя несколько основных моделей, которые определяют этапы и процессы разработки. Вот некоторые из наиболее распространенных моделей SDLC:
- Каскадная модель (Waterfall): Это классическая линейная модель, в которой каждый этап разработки выполняется последовательно. Этапы включают определение требований, проектирование, разработку, тестирование, внедрение и поддержку. Каждый этап завершается, прежде чем начать следующий.
- Итеративная модель (Iterative): Эта модель основана на последовательном выполнении итераций, каждая из которых включает в себя все этапы разработки (такие, как анализ, проектирование, разработка, тестирование и внедрение). Каждая итерация дает улучшенную итерацию продукта, и команда получает обратную связь и корректирует процесс в следующей итерации.
- Спиральная модель (Spiral): Это итеративная модель, которая включает в себя применение спирали, вращающейся по часовой стрелке. Каждый оборот спирали представляет собой новую итерацию, включая планирование, определение требований, проектирование, разработку, тестирование и оценку рисков. Каждая итерация уточняет решения и оценивает риски, что помогает в принятии обоснованных решений.
- Прототипирование (Prototyping): В этой модели разработки создается прототип или предварительная версия продукта для получения обратной связи и оценки функциональности и эффективности. Прототип помогает выявить и уточнить требования, а затем используется для разработки окончательной версии продукта.
- Гибкая модель (Agile): Agile - это набор подходов к разработке, включающих в себя различные методологии, такие как Scrum, Kanban и Extreme Programming (XP). Гибкая модель акцентирует внимание на итеративной разработке, быстрой адаптации к изменениям и сотрудничестве внутри команды. Процессы Agile включают планирование, создание коротких итераций (спринтов), демонстрацию результатов и регулярное обратное общение.
Это только некоторые из основных моделей SDLC, и существует множество других подходов и вариаций, которые могут быть применены в зависимости от требований проекта и предпочтений команды разработчиков.
Agile vs scrum/kanban
Kanban, Agile и Scrum - это три различных понятия, которые связаны с управлением проектами и разработкой программного обеспечения, но они имеют разные аспекты и фокусы.
Kanban: Kanban - это метод управления рабочим процессом, который обеспечивает визуализацию потока работ и оптимизацию его эффективности. Главное внимание уделяется визуализации рабочего потока, ограничению количества одновременно выполняемых задач и непрерывному улучшению процесса работы. Канбан помогает команде видеть текущий статус задач, управлять приоритетами, устранять узкие места и повышать эффективность рабочего процесса.
Agile: Agile (гибкий подход) - это философия и набор подходов к разработке программного обеспечения, которые ставят акцент на сотрудничество, быструю адаптацию к изменениям и доставку ценности клиенту. Agile подразумевает гибкость и итеративный подход к разработке, где требования и решения могут меняться на протяжении проекта. Он акцентирует внимание на быстрой обратной связи, активном вовлечении заказчика и коллаборации внутри команды.
Scrum: Scrum - это одна из методологий Agile, которая представляет собой фреймворк для управления проектами и разработкой программного обеспечения. Он основан на итеративном и инкрементальном подходе, где проект разбивается на короткие временные интервалы, называемые спринтами. Скрам предоставляет роли (Product Owner, Scrum Master, разработчики) и церемонии (планирование спринта, демонстрация, обзор и заседание ретроспективы) для организации работы команды. Он ставит акцент на быстрое получение обратной связи, прозрачность, самоорганизацию и непрерывное улучшение процесса разработки.
В целом, Kanban - это метод управления рабочим процессом, Agile - философия и набор подходов к разработке, а Scrum - конкретный фреймворк, применяемый в рамках Agile для организации работы команды. Kanban и Scrum могут использоваться в контексте Agile, чтобы помочь командам достигать гибкости, прозрачности и эффективности в разработке программного обеспечения.
Providing estimates of efforts required for implementing the tasks
Оценка усилий, необходимых для решения технических задач, является важным аспектом планирования проектов и управления ресурсами. Вот несколько методов, которые могут помочь в оценке усилий:
- Экспертная оценка: При использовании этого метода команда разработчиков, включая технических специалистов, принимает участие в процессе оценки. Каждая задача анализируется, и участники команды делятся своим экспертным мнением о времени, необходимом для выполнения каждой задачи. Затем суммируются оценки и получается прогнозируемая общая оценка усилий.
- Точки сложности (Story Points): В этом методе используются единицы измерения, называемые "точками сложности". Команда оценивает сложность каждой задачи на основе совокупных факторов, таких как технические препятствия, объем работы, сложность решения и зависимости. Более сложные задачи получают большее количество точек сложности. Это позволяет оценить относительную сложность задачи и сравнивать их между собой.
- Планирование покера (Planning Poker): В этом методе команда разработчиков собирается вместе и каждый участник получает набор карт с числами, представляющими относительную оценку сложности задачи (например, числа Фибоначчи). Затем каждая задача обсуждается, и каждый участник одновременно выбирает карту с оценкой, которую он считает соответствующей сложности задачи. Затем карты оборачиваются и обсуждаются различия в оценках до достижения согласия на оценку.
- Исторические данные: Команда может использовать данные из предыдущих проектов или опыта для оценки усилий. Анализируются данные о времени, затраченном на ранее выполненные задачи с аналогичными характеристиками или сложностями. Эти исторические данные могут быть использованы для определения прогнозируемых усилий для новых задач.
Важно отметить, что оценка усилий является прогнозом и может быть подвержена погрешности. По мере выполнения задачи и накопления опыта, оценки могут быть скорректированы. Кроме того, команда может использовать комбинацию различных методов оценки в зависимости от контекста проекта и предпочтений команды.
Git
Fundamental concepts of group work and version control
Фундаментальные концепции групповой работы и контроля версий в Git включают в себя работу с репозиториями, ветвлением, слиянием и командами совместной работы. Вот их обзор:
- Репозитории: Репозиторий в Git представляет собой хранилище для вашего проекта, где сохраняются все файлы, история изменений и метаданные. Git поддерживает локальные и удаленные репозитории. Локальный репозиторий находится на вашем компьютере, а удаленный репозиторий располагается на сервере, обеспечивая совместную работу с другими участниками проекта.
- Ветвление (Branching): Ветвление в Git позволяет создавать отдельные ветки разработки, которые идут от основной (главной) ветки, называемой обычно "master" или "main". Каждая ветка представляет собой независимую линию разработки, где вы можете вносить изменения без влияния на другие ветки. Ветвление позволяет команде работать параллельно над различными функциональностями, исправлениями ошибок или экспериментами.
- Слияние (Merging): После завершения работы в отдельной ветке изменения можно внести обратно в основную ветку путем выполнения операции слияния. Слияние комбинирует изменения из одной ветки с другой, сохраняя историю изменений и объединяя различные ветки. Это позволяет объединять работу нескольких разработчиков в общий код.
- Команды совместной работы: Git предоставляет набор команд, которые облегчают совместную работу над проектом. Например, команда "clone" используется для создания локальной копии удаленного репозитория, "pull" для получения последних изменений с удаленного репозитория, "push" для отправки локальных изменений на удаленный репозиторий, "fetch" для получения изменений без автоматического слияния, "commit" для сохранения изменений в репозитории и многие другие.
- Контроль версий: Основная концепция Git - это контроль версий, что означает, что Git отслеживает все изменения в файлах и позволяет вам легко переходить между различными состояниями вашего проекта. Вы можете восстанавливаться к предыдущим версиям файлов, сравнивать изменения между версиями, откатывать изменения и управлять историей разработки.
Git предоставляет богатый набор возможностей для эффективной групповой работы и контроля версий в проектах разработки программного обеспечения. Он обеспечивает гибкость и контроль над процессом разработки, позволяя командам совместно работать над проектами и отслеживать изменения в проекте.
Basic operations
Git предоставляет набор базовых операторов (команд), которые позволяют вам управлять репозиториями, контролировать версии и работать с удаленными репозиториями. Вот некоторые из наиболее распространенных операторов Git:
git init
: Создает новый локальный репозиторий Git в текущей директории. Эта команда выполняется один раз в начале проекта.git clone
: Клонирует (копирует) удаленный репозиторий Git на ваш локальный компьютер. Командаgit clone
следует за URL удаленного репозитория и создает локальную копию проекта.git add
: Добавляет измененные или новые файлы в индекс Git, чтобы они были готовы к коммиту. Командаgit add
принимает список файлов или использование точки.
для добавления всех измененных файлов.git commit
: Создает новый коммит, сохраняя текущее состояние файлов в репозитории. Командаgit commit
фиксирует изменения вместе с сообщением коммита, которое описывает внесенные изменения.git push
: Отправляет локальные коммиты на удаленный репозиторий Git. Командаgit push
позволяет синхронизировать изменения, сделанные на вашем локальном репозитории, с удаленным репозиторием.git pull
: Получает (вытягивает) последние изменения с удаленного репозитория и объединяет их с вашим локальным репозиторием. Командаgit pull
выполняет комбинацию командgit fetch
иgit merge
.git branch
: Создает новую ветку или отображает список существующих веток. Командаgit branch
без аргументов показывает список веток, а с указанием имени создает новую ветку.git checkout
: Переключается между ветками или восстанавливает файлы из предыдущих коммитов. Командаgit checkout
используется для переключения на другую ветку или восстановления файлов из репозитория.git merge
: Объединяет изменения из одной ветки в другую. Командаgit merge
позволяет объединить изменения из другой ветки в текущую ветку.git log
: Показывает историю коммитов в репозитории. Командаgit log
отображает список коммитов, включая автора, дату и сообщение коммита.
Gitflow
Gitflow - это модель ветвления (branching model) в системе контроля версий Git, которая предлагает конкретную организацию веток и правила для управления разработкой проекта. Была разработана Vincent Driessen и стала популярной среди команд разработки программного обеспечения.
Основные концепции и компоненты Gitflow включают:
- Главные ветки (Main Branches):
- Master: Ветка
master
представляет собой стабильную и готовую к развертыванию версию проекта. Коммиты вmaster
обычно соответствуют релизным версиям программного обеспечения. - Develop: Ветка
develop
служит основной веткой для разработки. В нее вливаются ветки функциональностей (feature branches) и исправлений ошибок (bugfix branches). - Вспомогательные ветки (Supporting Branches):
- Feature branches: Каждая новая функциональность или задача разрабатывается в отдельной ветке
feature
от веткиdevelop
. После завершения разработки функциональности веткаfeature
сливается обратно вdevelop
. - Release branches: Подготовка к релизу выполняется в отдельной ветке
release
от веткиdevelop
. Здесь происходят завершающие работы, такие как тестирование, исправление ошибок и подготовка документации перед выпуском стабильной версии. Затем веткаrelease
сливается как вdevelop
, так и вmaster
. - Bugfix branches: Если в
master
обнаруживается ошибка, отчет о ней создается веткаbugfix
отmaster
. После исправления бага веткаbugfix
сливается обратно вdevelop
иmaster
. - Операции слияния (Merging Operations):
- Merge: Ветки
feature
,release
иbugfix
сливаются вdevelop
для интеграции изменений и тестирования. - Release to Master: После завершения тестирования ветка
release
сливается вmaster
для создания стабильной релизной версии. - Hotfixes: Если критическая ошибка обнаруживается в продакшене, создается ветка
hotfix
отmaster
для исправления. После исправления веткаhotfix
сливается обратно вdevelop
иmaster
.
Branching, tagging, merging changes from specific branch
Branching, tagging и merging - это важные операции в системе контроля версий Git, которые позволяют управлять изменениями и организовывать работу над проектами. Вот их обзор:
- Branching (Ветвление): Ветвление позволяет создавать отдельные линии разработки в Git. Каждая ветка представляет собой независимую линию изменений, где можно вносить и тестировать новые функциональности или исправления ошибок без влияния на другие ветки. Создание новой ветки происходит с помощью команды
git branch <имя_ветки>
. Для переключения на ветку используется командаgit checkout <имя_ветки>
. Ветвление позволяет команде эффективно работать над разными аспектами проекта параллельно. - Tagging (Тегирование): Тегирование в Git используется для маркировки определенных точек в истории разработки, обычно связанных с релизами или важными моментами. Теги могут помочь вам быстро ссылаться на конкретные коммиты или версии проекта. Существуют два типа тегов: аннотированные (annotated) и легкие (lightweight). Аннотированные теги хранят дополнительную информацию, такую как автор, дата и сообщение, в то время как легкие теги представляют собой просто ссылку на коммит. Для создания тега используется команда
git tag <имя_тега>
. - Merging (Слияние): Слияние в Git позволяет объединять изменения из одной ветки в другую. Это полезно, когда разработка определенной функциональности или исправление ошибки в одной ветке должны быть внесены в другую ветку. Для выполнения слияния веток, необходимо переключиться на целевую ветку (например,
git checkout <целевая_ветка>
) и выполнить командуgit merge <исходная_ветка>
. Git автоматически интегрирует изменения из исходной ветки в целевую. При слиянии могут возникать конфликты, которые требуют ручного разрешения.
Blaming (annotate)
Blaming (также известный как annotate или praise) - это команда в системе контроля версий Git, которая позволяет определить, кто и когда внес изменения в определенные строки файла. Она предоставляет информацию о последнем коммите, в котором произошли изменения в каждой строке файла.
Когда вы используете команду git blame
для определенного файла, Git покажет содержимое файла с указанием автора и коммита, в котором каждая строка была изменена. Каждая строка будет ассоциирована с SHA-1 хешем коммита, автором и датой коммита.
Команда git blame
может быть полезна в различных ситуациях, например:
- Идентификация автора изменений: Если вы работаете над проектом с другими людьми и хотите выяснить, кто внес изменения в определенные строки кода,
git blame
может помочь вам определить автора изменений и связанные с ними коммиты. - Отслеживание истории изменений:
git blame
позволяет просматривать историю изменений конкретных строк файла. Это полезно при изучении эволюции кода или при поиске, когда и почему определенные изменения были внесены. - Отслеживание ответственности: Используя
git blame
, можно определить, кто был ответственен за конкретные строки кода. Это может быть полезно для команд, которые хотят отслеживать, кто отвечает за определенные части проекта или для учета причин изменений.
Пример использования команды git blame
:
git blame filename.txt
Команда git blame
предоставляет полезную информацию о происхождении изменений в файлах, что помогает в анализе и отслеживании истории разработки в Git.
Reseting vs reverting changes
Resetting и reverting - это два различных способа отмены или отката изменений в системе контроля версий Git. Вот их основные различия:
Resetting (сброс): Resetting используется для перемещения HEAD и указателей веток на определенный коммит, что приводит к изменению истории коммитов. Когда вы выполняете сброс, Git удаляет коммиты и/или изменения, которые были внесены после указанного коммита.
Есть несколько различных вариантов сброса:
- Soft Reset: Soft reset сохраняет изменения, сделанные в коммитах, которые вы сбрасываете. Он перемещает HEAD и указатели веток на указанный коммит, но оставляет изменения в рабочем каталоге и индексе (staging area). Это позволяет вам пересоздать коммиты с новыми изменениями.
- Mixed Reset (по умолчанию): Mixed reset перемещает HEAD и указатели веток на указанный коммит и сбрасывает индекс (staging area), но не сохраняет изменения в рабочем каталоге. Вы можете снова проиндексировать файлы и создать новые коммиты.
- Hard Reset: Hard reset перемещает HEAD и указатели веток на указанный коммит, полностью отменяя изменения в индексе и рабочем каталоге. Все изменения, которые были сделаны после указанного коммита, будут потеряны без возможности их восстановления.
Reverting (откат): Reverting создает новый коммит, который отменяет изменения, внесенные в определенный коммит. В отличие от сброса, изменения не удаляются из истории коммитов. Вместо этого Git создает новый коммит, который отменяет эффекты предыдущего коммита.
Когда вы выполняете откат, Git создает коммит, который содержит противоположные изменения, отменяющие изменения предыдущего коммита. При этом в истории коммитов сохраняется информация о возвращении к предыдущему состоянию.
Различия между сбросом и откатом:
- Сброс изменяет историю коммитов, перемещая указатели веток и удаляя коммиты, тогда как откат создает новые коммиты, которые отменяют эффекты предыдущих коммитов.
- Сброс изменяет состояние индекса и рабочего каталога, в то время как откат создает новый коммит, который отменяет изменения.
- Сброс может быть опасен, если вы работаете с общим репозиторием, так как изменения в истории могут повлечь проблемы для других разработчиков. Откат более безопасен, так как он создает новый коммит без удаления истории.
Выбор между сбросом и откатом зависит от ситуации и требований проекта. Если вы хотите удалить изменения из истории коммитов и сделать новые коммиты, использование сброса может быть предпочтительным. Если же вы хотите сохранить историю изменений и создать коммит, который отменяет эффекты предыдущих изменений, то откат будет более подходящим вариантом.
Git reset
Git reset - это команда в системе контроля версий Git, которая позволяет отменить изменения и переместить указатель текущей ветки на другой коммит. Она позволяет отменить коммиты, изменения в индексе и рабочей директории, а также переместить указатель ветки.
Существует несколько вариантов использования команды git reset, включая:
- Soft reset:
git reset --soft <commit>
. При использовании этой команды указатель ветки перемещается на указанный коммит, при этом изменения в рабочей директории и индексе не отменяются. Это полезно, если вы хотите "отменить" коммиты и сделать новый коммит на основе предыдущего состояния. - Mixed reset:
git reset --mixed <commit>
. Это вариант по умолчанию, если не указывать режим явно. Он отменяет коммиты и сбрасывает индекс, но не изменяет рабочую директорию. Все изменения в коммите, на который указывает<commit>
, остаются в рабочей директории в неотслеживаемом состоянии. - Hard reset:
git reset --hard <commit>
. Этот вариант полностью отменяет коммиты, сбрасывает индекс и перезаписывает все изменения в рабочей директории. Все изменения после указанного коммита будут потеряны без возможности восстановления. Это может быть полезно, если вы хотите вернуться к предыдущему состоянию проекта.
Важно понимать, что команда git reset изменяет историю коммитов ветки, поэтому она должна использоваться осторожно и с пониманием последствий. Всегда рекомендуется создавать резервные копии перед использованием команды git reset, чтобы можно было восстановить данные в случае ошибки.
Также стоит отметить, что команда git reset не удаляет коммиты, а только изменяет указатель ветки. Если коммиты больше не доступны через другие ветки или ссылки, они могут быть удалены автоматически при очистке Git сборщиком мусора (garbage collector).
Git merge, git rebase
Git merge используется для объединения изменений из одной ветки в другую. При выполнении операции merge Git создает новый коммит, который объединяет изменения из исходной ветки (обычно называемой "source branch" или "feature branch") в целевую ветку (обычно называемую "target branch" или "main branch"). Этот новый коммит имеет двух родителей - последний коммит в целевой ветке и последний коммит в исходной ветке. Merge-коммит объединяет изменения из обеих веток, сохраняя историю изменений каждой из них.
Процесс слияния с помощью merge выглядит следующим образом:
- Вы переключаетесь на целевую ветку, в которую хотите объединить изменения.
- Вы запускаете команду
git merge source-branch
, гдеsource-branch
- это имя исходной ветки. - Git пытается автоматически объединить изменения из исходной ветки в целевую ветку.
- Если возникают конфликты - ситуации, когда одна и та же часть кода была изменена в обеих ветках, Git приостанавливает процесс слияния и указывает на конфликтные файлы. Вам нужно вручную разрешить конфликты, внести соответствующие изменения и добавить исправленные файлы в индекс. После этого вы запускаете команду
git merge --continue
, чтобы продолжить процесс слияния. - Если конфликтов нет или они были успешно разрешены, Git создает новый коммит с объединенными изменениями в целевой ветке.
Git rebase, с другой стороны, позволяет переместить коммиты из одной ветки в другую, применяя изменения на основе другой ветки. В результате история коммитов выглядит так, как будто вы делали коммиты непосредственно на целевую ветку, а не на отдельную ветку. В отличие от merge, git rebase изменяет историю коммитов.
Процесс перебазирования с помощью git rebase выглядит следующим образом:
- Вы переключаетесь на исходную ветку, которую хотите перебазировать на целевую ветку.
- Вы запускаете команду
git rebase target-branch
, гдеtarget-branch
- это имя целевой ветки. - Git находит коммиты, которые присутствуют только в исходной ветке, но отсутствуют в целевой ветке, и применяет их к целевой ветке.
- Если возникают конфликты, git rebase приостанавливается и указывает на конфликтные файлы. Вам нужно вручную разрешить конфликты, внести соответствующие изменения и добавить исправленные файлы в индекс. После этого вы запускаете команду
git rebase --continue
, чтобы продолжить процесс перебазирования. - После успешного завершения git rebase все коммиты из исходной ветки будут применены на целевую ветку в указанном порядке.
Git merge и git rebase имеют свои особенности и сценарии использования. Используйте git merge, если вам нужно сохранить историю изменений каждой ветки, и git rebase, если вы хотите иметь линейную историю коммитов без ветвления.
Git cherry pick
Git cherry-pick - это команда в системе контроля версий Git, которая позволяет выбирать и применять отдельные коммиты из одной ветки в другую. Эта команда позволяет скопировать изменения из одного коммита и применить их к текущей ветке.
Чтобы воспользоваться командой cherry-pick, вы должны находиться в целевой ветке, куда вы хотите применить выбранный коммит. Затем вы используете команду git cherry-pick, указывая идентификатор коммита, который вы хотите применить.
Пример использования команды cherry-pick:
- Убедитесь, что вы находитесь в целевой ветке (ветке, куда вы хотите применить выбранный коммит):phpCopy code
git checkout <целевая_ветка>
- Выполните команду cherry-pick, указав идентификатор коммита, который вы хотите применить:phpCopy code
git cherry-pick <идентификатор_коммита>
При выполнении команды Git попытается применить изменения указанного коммита к текущей ветке. Если нет конфликтов слияния, изменения будут применены автоматически. Если возникают конфликты, Git остановится и покажет вам файлы с конфликтами, которые нужно разрешить вручную. После разрешения конфликтов вы должны использовать команду git cherry-pick --continue
, чтобы продолжить процесс применения коммита.
Важно отметить, что при использовании cherry-pick создаются новые коммиты в целевой ветке с теми же изменениями, которые были в выбранных коммитах. История коммитов будет отличаться от исходной ветки, из которой вы выбираете коммиты.
Git cherry-pick может быть полезным инструментом, когда вы хотите применить определенные изменения из одной ветки в другую, например, когда вы хотите применить исправления ошибок или новые функции из отдельной ветки в стабильную ветку. Однако следует быть осторожным при использовании cherry-pick, чтобы не создать дублированные или некорректные изменения в истории коммитов.
Git stash
Git stash - это команда в системе контроля версий Git, которая позволяет временно сохранить незавершенные изменения в отдельном хранилище, называемом "stash" (тайник), чтобы вы могли переключиться на другую ветку или применить другие изменения без необходимости коммитить незавершенную работу.
Когда вы выполняете команду git stash, Git сохраняет все изменения, которые еще не были зафиксированы в коммит, в стэше и возвращает вашу рабочую директорию в состояние последнего коммита. Стэш может включать изменения в файлах, новые файлы и удаленные файлы.
Пример использования команды stash:
- Проверьте статус вашего репозитория, чтобы убедиться, что у вас есть незафиксированные изменения:luaCopy code
git status
- Сохраните незавершенные изменения в стэше:arduinoCopy code
git stash save "Название стэша"
Можно добавить опцию-u
или--include-untracked
, чтобы включить также непрослеженные файлы в стэше. - Переключитесь на другую ветку или выполните другие операции в репозитории.
- Вернитесь к сохраненным изменениям, применив стэш:Copy code
git stash apply
Если вы создали несколько стэшей, вы можете указать конкретный стэш, используя его идентификатор или название:kotlinCopy codegit stash apply stash@{2}
Обратите внимание, что команда apply применяет стэш, но не удаляет его. Если вы хотите применить и удалить стэш, используйте командуgit stash pop
вместоgit stash apply
.
Команда stash может быть полезной, когда вы работаете над одной задачей, но внезапно должны переключиться на другую ветку, чтобы исправить ошибку или реализовать другую функцию. Стэши позволяют вам временно сохранить незавершенную работу, чтобы не потерять ваши изменения и вернуться к ним позже, когда вы снова переключитесь на ветку, на которой работали ранее.
Unit-tests, testing pyramid, integration tests
Basic concepts: Test Plan, Test Suite, Test Case
- Тест-план (Test Plan): Тест-план — это документ, который описывает всю стратегию и подход, используемый для проведения тестирования определенного программного продукта или функциональности. Он включает в себя информацию о целях, охвате, расписании, ресурсах, методологии тестирования и критериях завершения тестирования. Тест-план определяет, какие виды тестирования будут применяться, какие роли и ответственности назначены для участников тестирования, и какие ресурсы требуются для его выполнения.
- Тест-набор (Test Suite): Тест-набор (или тест-сьют) — это коллекция тест-кейсов, которые объединены в логическую группу для выполнения определенного вида тестирования или тестирования определенной функциональности. Тест-набор помогает организовать и структурировать тестирование, группируя связанные тест-кейсы вместе. Например, у вас может быть тест-набор для тестирования авторизации, другой — для тестирования платежной системы и т.д.
- Тест-кейс (Test Case): Тест-кейс — это документ, который описывает шаги, условия и ожидаемые результаты для проведения конкретного теста. Он содержит информацию о предусловиях, шагах для выполнения теста, входных данных, ожидаемых результатах и фактических результатов. Каждый тест-кейс разрабатывается с целью проверить определенный аспект программного продукта или функциональности. Тест-кейсы являются основной единицей тестирования и помогают убедиться, что функциональность работает правильно и соответствует требованиям.
Вместе эти термины помогают организовать и выполнить тестирование программного обеспечения. Тест-план определяет общую стратегию, тест-наборы группируют связанные тест-кейсы для выполнения определенных видов тестирования, а тест-кейсы предоставляют конкретные инструкции для проведения отдельных тестов.
Testing Pyramid
Пирамида тестирования (Testing Pyramid) — это концепция, предлагающая оптимальное распределение различных типов тестов в соответствии с их количеством и уровнем абстракции. Она помогает оптимизировать процесс тестирования и обеспечить достаточный уровень качества при минимальных затратах.
Пирамида тестирования состоит из трех основных уровней:
- Unit Tests (Модульные тесты): На нижнем уровне пирамиды находятся модульные тесты. Они предназначены для проверки отдельных модулей или компонентов программного продукта на уровне их функциональности. Модульные тесты пишутся разработчиками и выполняются автоматически. Они должны быть быстрыми, изолированными и полностью автоматизированными. Модульные тесты помогают обнаруживать дефекты на ранних стадиях разработки и обеспечивают быструю обратную связь о работоспособности отдельных модулей.
- Integration Tests (Интеграционные тесты): На среднем уровне пирамиды располагаются интеграционные тесты. Они проверяют взаимодействие и корректность работы различных модулей или компонентов, объединенных вместе. Интеграционные тесты могут быть автоматизированы и выполняться на уровне API или пользовательского интерфейса. Целью таких тестов является проверка правильной интеграции компонентов и выявление возможных проблем на уровне взаимодействия.
- UI Tests (Тесты пользовательского интерфейса): На верхнем уровне пирамиды находятся тесты пользовательского интерфейса. Они выполняются через графический интерфейс и имитируют действия и взаимодействие реальных пользователей с программным продуктом. Тесты пользовательского интерфейса могут быть автоматизированы или выполняться вручную. Они проверяют работу приложения в реальных условиях, включая ввод данных, навигацию и отображение результатов. Такие тесты помогают убедиться, что продукт работает правильно с точки зрения пользовательского опыта.
Properties of a good unit test
Хорошо написанный unit-тест обладает следующими характеристиками:
- Независимость: Unit-тест должен быть независимым от других тестов и от внешних факторов. Это означает, что каждый тест должен быть способен выполняться самостоятельно, без зависимости от результатов или состояния других тестов или компонентов системы. Это обеспечивает предсказуемость и надежность тестов.
- Изолированность: Хороший unit-тест должен быть изолированным, то есть он должен проверять только конкретную функцию или модуль программы. Для этого может потребоваться использование заглушек (mock objects) или фиктивных данных. Изолированный тест позволяет точно определить, где возникают проблемы, и облегчает их исправление.
- Четкость и понятность: Unit-тест должен быть четким и понятным для других разработчиков. Читаемость кода теста должна быть на высоком уровне, чтобы другим было легко понять, что именно тестируется и какие ожидаемые результаты. Хороший тест должен быть легко читаемым и поддерживаемым.
- Покрытие: Хорошо написанный unit-тест должен обеспечивать достаточное покрытие кода, то есть тестировать все основные сценарии использования функции или модуля. Чем больше код покрывается тестами, тем больше вероятность обнаружить потенциальные ошибки и проблемы.
- Независимость от окружения: Unit-тесты должны быть независимыми от окружения выполнения, таких как базы данных, сетевые ресурсы или файловые системы. Они должны полагаться на фиктивные данные или заглушки для эмуляции зависимостей. Это обеспечивает стабильность и повторяемость тестов.
- Быстрота выполнения: Хороший unit-тест должен быть быстрым в выполнении. Быстрое выполнение позволяет запускать тесты часто, в том числе автоматически при каждой сборке проекта или перед коммитом кода. Быстрые тесты улучшают производительность разработки и обратную связь.
- Устойчивость к изменениям: Хороший unit-тест должен быть устойчивым к изменениям внутри кода, если сама функциональность не изменилась. Тесты не должны ломаться из-за незначительных изменений в реализации, только если они действительно затрагивают логику тестируемой функции или модуля.
F.I.R.S.T
- Fast (Быстрый): Хороший тест должен быть быстрым в выполнении. Быстрое выполнение позволяет запускать тесты часто и обеспечивает быструю обратную связь о работоспособности кода. Быстрые тесты также способствуют улучшению производительности разработки и интеграции непрерывной сборки (Continuous Integration).
- Independent (Независимый): Каждый тест должен быть независимым от других тестов и от внешних факторов. Независимые тесты могут выполняться в любом порядке и в любой комбинации, без влияния на результаты. Это помогает обнаруживать проблемы и изолировать их, обеспечивая предсказуемость и надежность тестов.
- Repeatable (Повторяемый): Хороший тест должен быть повторяемым. Это означает, что при повторном выполнении тест должен давать одинаковые результаты, если код и его окружение не изменились. Повторяемость тестов важна для детектирования ошибок и проверки стабильности системы.
- Self-Validating (Самопроверяемый): Тест должен быть самопроверяемым и давать ясный результат: «пройден» или «не пройден». Он должен автоматически анализировать результаты выполнения и определять, успешно ли прошел или нет. Это помогает быстро и надежно определить, есть ли проблемы с кодом.
- Timely (Своевременный): Хороший тест должен быть написан своевременно — до или вместе с разрабатываемым кодом. Он должен отражать требования и спецификации функциональности и служить надежной основой для проверки работоспособности кода. Своевременное написание тестов помогает выявить проблемы на ранних стадиях разработки и сокращает время исправления ошибок.
JS Unit basics
- Тестовый фреймворк: Выберите тестовый фреймворк, который наиболее соответствует вашим потребностям. Некоторые популярные фреймворки для тестирования в JavaScript включают Jest, Mocha, Jasmine и QUnit. Эти фреймворки предоставляют инструменты для организации, запуска и проверки результатов ваших тестов.
- Написание тестовых сценариев: Напишите тестовые сценарии (тест-кейсы), которые проверят ожидаемое поведение ваших функций или модулей. Каждый тест должен быть небольшим и сосредоточиться на проверке конкретного аспекта кода. Обычно тесты создаются с использованием функций-ассертов (assertions), которые сравнивают фактические результаты с ожидаемыми.
- Запуск тестов: Запустите ваши тесты с помощью выбранного тестового фреймворка. Это может быть выполнено с использованием командной строки или интеграцией в вашу среду разработки. Фреймворк обнаружит и выполнит все ваши тестовые сценарии, а затем выдаст результаты.
- Ассерты: Используйте ассерты для проверки фактических результатов ваших тестов. Ассерты предоставляют утверждения (assertions), которые проверяют, соответствуют ли ожидаемые значения фактическим значениям. Например, вы можете использовать ассерт
assertEquals(expected, actual)
для проверки, равны ли ожидаемое и фактическое значения. - Настройка и очистка: Если ваши тесты требуют предварительной настройки (например, создания фиктивных данных) или очистки после выполнения теста (например, удаление созданных объектов), используйте методы
beforeEach
,afterEach
,beforeAll
,afterAll
и другие, предоставленные тестовым фреймворком, для выполнения таких действий. - Моки и заглушки: В некоторых случаях вам может понадобиться заменить или имитировать внешние зависимости ваших модулей для изоляции тестируемого кода. Моки и заглушки (mocks и stubs) позволяют создавать фиктивные объекты или функции, которые эмулируют поведение внешних компонентов.
- Покрытие тестами: Старайтесь достичь хорошего покрытия кода тестами. Используйте инструменты для анализа покрытия, чтобы определить, какой процент вашего кода покрыт тестами. Целью является достижение высокого уровня покрытия, чтобы обнаруживать потенциальные проблемы и уверенно изменять код.
Test first development techniques (TDD and BDD)
Методы разработки, основанные на тестировании (Test-Driven Development, TDD, и Behavior-Driven Development, BDD), являются подходами к разработке программного обеспечения, которые акцентируют внимание на создании тестов на ранних стадиях процесса разработки. Оба метода ставят своей целью улучшение качества кода, увеличение стабильности и создание более понятных спецификаций.
- Test-Driven Development (TDD): TDD — это методология разработки, в которой тесты пишутся перед написанием реализации кода. Он включает в себя следующие шаги:
- Написание теста: Разработчик пишет тест, который определяет желаемое поведение или требования для новой функциональности или модуля.
- Запуск теста: Тест запускается и ожидается, что он не пройдет (потому что код еще не реализован).
- Написание минимального кода: Разработчик создает минимально необходимую реализацию, чтобы пройти тест.
- Запуск теста: Тест снова запускается, и ожидается, что он пройдет.
- Рефакторинг кода: Разработчик улучшает код, сохраняя тесты проходимыми, и повторяет цикл написания теста, написания кода и рефакторинга до достижения необходимого функционального требования.
TDD помогает повысить качество кода, обнаружить и исправить проблемы на ранних стадиях разработки и улучшить дизайн приложения. Тесты становятся спецификацией требований и служат документацией для кода.
- Behavior-Driven Development (BDD): BDD — это методология разработки, которая уделяет внимание бизнес-поведению программного обеспечения. В BDD тесты описываются на естественном языке, который понятен всем участникам проекта. Основные элементы BDD включают:
- Определение требований: Вместо написания тест-кейсов, требования формулируются в виде сценариев поведения приложения.
- Создание спецификаций: Сценарии описываются на уровне бизнес-поведения, используя структуру «Дано-Когда-Тогда» (Given-When-Then), которая помогает определить предусловия, действия и ожидаемые результаты.
- Автоматизированные тесты: На основе сценариев создаются автоматизированные тесты, которые проверяют соответствие реализации требованиям.
- Понятность и общность: Тесты должны быть понятными для всех участников проекта и служить коммуникационным инструментом между разработчиками, тестировщиками и бизнес-аналитиками.
BDD способствует улучшению сотрудничества и пониманию между различными ролями в проекте. Он помогает фокусироваться на бизнес-ценности и более эффективно выстраивать процесс разработки.
Common (functions, hoisting, work with Objects, etc)
Types of functions and their differences, hoisting function declaration
- Объявление функции (Function Declaration):
- Объявляется с использованием ключевого слова
function
. - Поднимается (hoisted) вверх текущей области видимости, поэтому можно вызывать до фактического объявления.
- Может быть использовано в любом месте внутри области видимости, включая перед его объявлением.
- Выражение функции (Function Expression):
- Создается в виде выражения и присваивается переменной или свойству объекта.
- Используется ключевое слово
function
, но без указания имени функции, если функция является анонимной. - Не поднимается вверх и может быть вызвано только после его объявления.
- Стрелочные функции (Arrow Function):
- Представляют синтаксически сокращенную форму функций с использованием стрелочного оператора
=>
. - Всегда анонимны и не имеют своего собственного контекста выполнения (
this
), наследуют его от окружающей области видимости. - Обычно используются для создания кратких и компактных функций.
- Конструкторы функций (Function Constructor):
Destructing assigments, spread operator
Деструктурирующее присваивание позволяет извлекать значения из массивов или свойств из объектов и присваивать их переменным за одну операцию. Это позволяет писать более компактный и выразительный код. Для использования деструктурирующего присваивания вам понадобится использовать фигурные скобки {}
для объектов или квадратные скобки []
для массивов.
const numbers = [1, 2, 3]; const [a, b, c] = numbers; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3
const person = { name: 'John', age: 30 }; const { name, age } = person; console.log(name); // 'John' console.log(age); // 30
Оператор расширения, известный также как spread operator, представляет собой троеточие (...
), которое позволяет расширить (spread) элементы массива или свойства объекта в другом месте кода. Это полезно, когда вы хотите передать все элементы массива или свойства объекта в качестве аргументов функции или объединить их с другими элементами.
const numbers = [1, 2, 3]; const newArray = [...numbers, 4, 5, 6]; console.log(newArray); // [1, 2, 3, 4, 5, 6]
const person = { name: 'John', age: 30 }; const newPerson = { ...person, city: 'London' }; console.log(newPerson); // { name: 'John', age: 30, city: 'London' }
Оператор расширения также может использоваться для объединения нескольких массивов или объектов:
const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const combinedArray = [...arr1, ...arr2]; console.log(combinedArray); // [1, 2, 3, 4, 5, 6] const obj1 = { a: 1, b: 2 }; const obj2 = { c: 3, d: 4 }; const combinedObject = { ...obj1, ...obj2 }; console.log(combinedObject); // { a: 1, b: 2, c: 3, d: 4 }
Деструктурирующее присваивание и оператор расширения являются мощными инструментами, которые позволяют более удобно работать с данными в JavaScript, делая код более читаемым и компактным.
Optional mark. Object properties optional chain
Optional chaining представляет собой оператор в виде вопросительного знака ?.
, который добавляется после имени объекта или свойства. Он позволяет обращаться к свойству объекта только в том случае, если предшествующий объект не является null
или undefined
. Если предшествующий объект неопределен, возвращается значение undefined
. Это помогает избежать ошибок типа "Cannot read property 'propertyName' of undefined".
const person = { name: 'John', age: 30, address: { city: 'London' } }; console.log(person.address.city); // 'London' console.log(person.address.country); // undefined console.log(person.address.country.name); // Uncaught TypeError: Cannot read property 'name' of undefined // Используя optional chaining: console.log(person.address?.city); // 'London' console.log(person.address?.country); // undefined console.log(person.address?.country?.name); // undefined
В приведенном примере, при использовании person.address.city
и person.address.country
, если address
был бы null
или undefined
, возникла бы ошибка. Однако, с использованием optional chaining person.address?.city
и person.address?.country
, вместо ошибки возвращается undefined
.
Optional chaining также может использоваться с методами объектов:
const person = { name: 'John', age: 30, sayHello: function() { console.log('Hello!'); } }; person.sayHello?.(); // 'Hello!' person.sayBye?.(); // undefined
В этом примере, если sayHello
был бы null
или undefined
, вызов person.sayHello?.()
не вызывает ошибку, а просто ничего не происходит.
Optional mark (опциональная метка) - это возможность указать, что свойство объекта является необязательным во время его объявления. Для этого используется символ вопросительного знака ?
после имени свойства. Это позволяет присвоить свойству значение undefined
или пропустить его объявление, если такое значение не определено.
const person = { name: 'John', age: 30, address?: { city: 'London' } };
В этом примере, свойство address
является необязательным, и его значение может быть либо объектом с полем city
, либо undefined
.
Optional chaining и optional mark являются мощными инструментами, которые делают код более устойчивым к ошибкам и упрощают работу с неопределенными значениями в JavaScript. Они помогают избежать возникновения ошибок и позволяют более безопасно обращаться к свойствам объектов.
Work with array (map, filter, reduce, find, includes, some etc.)
map
: Методmap
применяет заданную функцию ко всем элементам массива и создает новый массив с результатами. Он возвращает новый массив с тем же количеством элементов, что и исходный массив.
const numbers = [1, 2, 3, 4, 5]; const doubledNumbers = numbers.map(num => num * 2); console.log(doubledNumbers); // [2, 4, 6, 8, 10]
filter
: Методfilter
создает новый массив, содержащий все элементы исходного массива, для которых заданная функция возвращаетtrue
.
const numbers = [1, 2, 3, 4, 5]; const evenNumbers = numbers.filter(num => num % 2 === 0); console.log(evenNumbers); // [2, 4]
reduce
: Методreduce
применяет функцию к аккумулятору и каждому элементу массива (слева направо), сводя массив к одному значению.
const numbers = [1, 2, 3, 4, 5]; const sum = numbers.reduce((acc, num) => acc + num, 0); console.log(sum); // 15
find
: Методfind
возвращает первый элемент массива, для которого заданная функция возвращаетtrue
. Если такой элемент не найден, возвращаетсяundefined
.
const numbers = [1, 2, 3, 4, 5]; const foundNumber = numbers.find(num => num > 3); console.log(foundNumber); // 4
includes
: Методincludes
проверяет, содержит ли массив определенный элемент, и возвращает соответствующее логическое значение.
const numbers = [1, 2, 3, 4, 5]; console.log(numbers.includes(3)); // true console.log(numbers.includes(6)); // false
some
: Методsome
проверяет, удовлетворяет ли хотя бы один элемент массива условию, заданному в функции, и возвращает соответствующее логическое значение.
const numbers = [1, 2, 3, 4, 5]; console.log(numbers.some(num => num > 3)); // true console.log(numbers.some(num => num > 6)); // false
const numbers = [1, 2, 3, 4, 5]; numbers.forEach(num => console.log(num)); // Выводит: // 1 // 2 // 3 // 4 // 5
every
: Методevery
проверяет, удовлетворяют ли все элементы массива условию, заданному в функции, и возвращает соответствующее логическое значение.
const numbers = [1, 2, 3, 4, 5]; console.log(numbers.every(num => num > 0)); // true console.log(numbers.every(num => num > 2)); // false
const fruits = ['banana', 'apple', 'orange', 'kiwi']; fruits.sort(); console.log(fruits); // ['apple', 'banana', 'kiwi', 'orange']
slice
: Методslice
возвращает новый массив, содержащий копию части исходного массива. Можно указать начальный и конечный индексы для выбора нужной части.
const numbers = [1, 2, 3, 4, 5]; const slicedNumbers = numbers.slice(1, 4); console.log(slicedNumbers); // [2, 3, 4]
concat
: Методconcat
объединяет два или более массива, создавая новый массив, содержащий элементы из исходных массивов.
const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const combinedArray = arr1.concat(arr2); console.log(combinedArray); // [1, 2, 3, 4, 5, 6]
const numbers = [1, 2, 3, 4, 5]; numbers.reverse(); console.log(numbers); // [5, 4, 3, 2, 1]
const fruits = ['apple', 'banana', 'orange']; const joinedString = fruits.join(', '); console.log(joinedString); // 'apple, banana, orange'
What is garbage collector? Why do we need it?
Garbage collector (сборщик мусора) - это механизм, встроенный во многие языки программирования, включая JavaScript, который автоматически управляет памятью и освобождает память, занятую объектами, которые больше не используются программой.
Когда вы создаете объекты или выделяете память во время выполнения программы, операционная система выделяет блоки памяти для хранения этих данных. Однако, когда объекты или данные больше не нужны, например, когда они выходят из области видимости или больше не используются, эти блоки памяти остаются занятыми. Если не освободить эти блоки памяти, со временем программа может использовать все больше и больше памяти, что может привести к исчерпанию доступной памяти и ухудшению производительности.
Сборщик мусора решает эту проблему, автоматически определяя, какие объекты в программе больше не используются, и освобождая память, занятую этими объектами. Он следит за доступностью объектов, определяя, есть ли на них ссылки из других частей программы. Если объект больше не имеет ссылок, он считается недостижимым, и сборщик мусора может освободить память, занимаемую этим объектом.
Сборка мусора происходит автоматически и фоново, без явного вмешательства программиста. В JavaScript, например, встроенный механизм сборки мусора просматривает все переменные, объекты и структуры данных, отслеживает ссылки между ними и освобождает память, занятую объектами, на которые нет ссылок.
Использование сборщика мусора освобождает программиста от необходимости вручную управлять памятью, что может быть сложной и подверженной ошибкам задачей. Сборщик мусора помогает предотвратить утечки памяти и обеспечивает более безопасное и удобное программирование.
Keys, values, fromEntries, entries methods
const person = { name: 'John', age: 30, city: 'London' }; const keys = Object.keys(person); console.log(keys); // ['name', 'age', 'city']
const person = { name: 'John', age: 30, city: 'London' }; const values = Object.values(person); console.log(values); // ['John', 30, 'Lon
don']
Object.entries()
: МетодObject.entries()
возвращает массив, содержащий массивы пар ключ-значение для каждого свойства объекта.
const person = { name: 'John', age: 30, city: 'London' }; const entries = Object.entries(person); console.log(entries); // [['name', 'John'], ['age', 30], ['city', 'London']]
Object.fromEntries()
: МетодObject.fromEntries()
преобразует массив, содержащий массивы пар ключ-значение, обратно в объект.
const entries = [['name', 'John'], ['age', 30], ['city', 'London']]; const person = Object.fromEntries(entries); console.log(person); // { name: 'John', age: 30, city: 'London' }
Методы Object.keys()
, Object.values()
, и Object.entries()
позволяют легко итерироваться по свойствам объекта и получать их ключи и значения. Метод Object.fromEntries()
выполняет обратное преобразование, позволяя создавать объекты из массивов пар ключ-значение.
Flat, flatMap, includes, Array.from() methods
flat
: Методflat
используется для "сплющивания" (преобразования из многоуровневой структуры в одноуровневую) массива, удаляя все вложенные подмассивы и создавая новый массив.
const nestedArray = [1, [2, 3], [4, [5, 6]]]; const flattenedArray = nestedArray.flat(); console.log(flattenedArray); // [1, 2, 3, 4, 5, 6]
flatMap
: МетодflatMap
комбинирует методыmap
иflat
, применяя заданную функцию ко всем элементам массива и затем "сплющивая" результаты в новый массив.
const numbers = [1, 2, 3, 4, 5]; const doubledAndFlattened = numbers.flatMap(num => [num * 2]); console.log(doubledAndFlattened); // [2, 4, 6, 8, 10]
includes
: Методincludes
проверяет, содержит ли массив определенный элемент, и возвращает соответствующее логическое значение.
const numbers = [1, 2, 3, 4, 5]; console.log(numbers.includes(3)); // true console.log(numbers.includes(6)); // false
Array.from()
: МетодArray.from()
создает новый массив из итерируемого объекта, такого как строка или массив-подобный объект.
const str = 'Hello'; const charArray = Array.from(str); console.log(charArray); // ['H', 'e', 'l', 'l', 'o']
Методы flat
и flatMap
предоставляют удобные средства для работы с массивами, особенно когда у вас есть вложенные структуры или необходимость в преобразовании данных. Метод includes
помогает проверить наличие элемента в массиве без необходимости использования циклов. Метод Array.from()
позволяет создавать массивы из других итерируемых объектов или преобразовывать строки в массивы символов.
Object cloning
В JavaScript существует несколько способов клонирования объектов. Рассмотрим различные методы клонирования объектов:
- Поверхностное клонирование (Shallow cloning):
- Метод
Object.assign()
: МетодObject.assign()
используется для копирования значений всех перечисляемых свойств из одного или более исходных объектов в целевой объект.javascriptCopy codeconst obj = { a: 1, b: 2 }; const clone = Object.assign({}, obj); console.log(clone); // { a: 1, b: 2 }
- Spread-оператор: Spread-оператор (
...
) также может использоваться для поверхностного клонирования объекта.javascriptCopy codeconst obj = { a: 1, b: 2 }; const clone = { ...obj }; console.log(clone); // { a: 1, b: 2 }
- Глубокое клонирование (Deep cloning):
- JSON методы: Вы можете преобразовать объект в строку JSON с помощью
JSON.stringify()
, а затем преобразовать обратно в объект с помощьюJSON.parse()
. Этот подход обеспечивает глубокое клонирование объекта, но имеет ограничения, такие как невозможность клонирования функций и значений, которые не могут быть сериализованы в JSON.javascriptCopy codeconst obj = { a: 1, b: { c: 2 } }; const clone = JSON.parse(JSON.stringify(obj)); console.log(clone); // { a: 1, b: { c: 2 } }
- Библиотеки клонирования: Существуют различные сторонние библиотеки, такие как Lodash и Ramda, которые предоставляют функции для глубокого клонирования объектов. Например,
_.cloneDeep(obj)
из Lodash клонирует объект рекурсивно.javascriptCopy codeconst _ = require('lodash'); const obj = { a: 1, b: { c: 2 } }; const clone = _.cloneDeep(obj); console.log(clone); // { a: 1, b: { c: 2 } }
При клонировании объектов важно понимать разницу между поверхностным и глубоким клонированием. Поверхностное клонирование создает новый объект, но его вложенные объекты остаются ссылками на оригинальные объекты. Глубокое клонирование, с другой стороны, создает полную копию объекта и всех его вложенных объектов.
Array cloning
- Метод
slice()
: Методslice()
создает новый массив, содержащий копию части исходного массива или весь массив, если не указаны аргументы.
const arr = [1, 2, 3, 4, 5]; const clone = arr.slice(); console.log(clone); // [1, 2, 3, 4, 5]
- Spread-оператор: Spread-оператор (
...
) может использоваться для клонирования массива. Он распыляет элементы массива в новый массив.
const arr = [1, 2, 3, 4, 5]; const clone = [...arr]; console.log(clone); // [1, 2, 3, 4, 5]
- Метод
Array.from()
: МетодArray.from()
создает новый массив из итерируемого объекта, включая массив-подобные объекты и строки.
const arr = [1, 2, 3, 4, 5]; const clone = Array.from(arr); console.log(clone); // [1, 2, 3, 4, 5]
- JSON методы: Вы можете преобразовать массив в строку JSON с помощью
JSON.stringify()
, а затем преобразовать обратно в массив с помощьюJSON.parse()
. Однако этот метод используется для поверхностного клонирования и не подходит для глубокого клонирования массивов с вложенными объектами.
const arr = [1, 2, 3, 4, 5]; const clone = JSON.parse(JSON.stringify(arr)); console.log(clone); // [1, 2, 3, 4, 5]
- Библиотеки клонирования: Как и в случае с объектами, сторонние библиотеки, такие как Lodash и Ramda, предоставляют функции для глубокого клонирования массивов. Например,
_.cloneDeep(arr)
из Lodash клонирует массив рекурсивно.
const _ = require('lodash'); const arr = [1, 2, 3, 4, 5]; const clone = _.cloneDeep(arr); console.log(clone); // [1, 2, 3, 4, 5]
При клонировании массивов важно учитывать, что поверхностное клонирование создает новый массив, но его элементы, которые являются объектами или другими массивами, остаются ссылками на оригинальные элементы. Глубокое клонирование создает полную копию массива и всех его вложенных элементов.
Context
What is context
В контексте JavaScript термин "context" (контекст) обычно относится к значению this
внутри функции и связанной с ней области видимости. this
ссылается на объект, в контексте которого вызывается функция.
Значение this
может быть определено несколькими способами, в зависимости от того, как функция вызывается:
- Глобальный контекст: В глобальном контексте, за пределами любых функций,
this
ссылается на глобальный объект, который являетсяwindow
в браузере илиglobal
в Node.js.
console.log(this === window); // true (в браузере) console.log(this === global); // true (в Node.js)
- Метод объекта: Когда функция вызывается как метод объекта,
this
ссылается на сам объект, к которому принадлежит метод.
const obj = { name: 'John', sayHello: function() { console.log(`Hello, ${this.name}!`); } }; obj.sayHello(); // "Hello, John!"
- Конструктор: При вызове функции в качестве конструктора с использованием оператора
new
,this
ссылается на новый экземпляр объекта, который создается конструктором.
function Person(name) { this.name = name; } const person = new Person('John'); console.log(person.name); // "John"
- Привязка с помощью методов
call()
иapply()
: Методыcall()
иapply()
позволяют явно установить значениеthis
при вызове функции, передавая объект в качестве первого аргумента.
function greet() { console.log(`Hello, ${this.name}!`); } const person = { name: 'John' }; greet.call(person); // "Hello, John!"
What is the global context
Глобальный контекст в JavaScript относится к контексту, в котором выполняется код за пределами любых функций. Это наивысший уровень контекста выполнения в JavaScript.
Когда JavaScript-код запускается, глобальный контекст создается автоматически. Глобальный контекст обычно связан с глобальным объектом, который является window
в браузере или global
в среде Node.js. В глобальном контексте доступны глобальные переменные и функции, которые могут быть использованы в любом месте программы.
В глобальном контексте ключевое слово this
ссылается на глобальный объект. Например, в браузере объект window
является глобальным объектом, поэтому this
в глобальном контексте будет ссылаться на window
.
Пример использования глобального контекста:
// Глобальный контекст console.log(this === window); // true (в браузере) const name = 'John'; // Глобальная переменная function greet() { console.log(`Hello, ${this.name}!`); // this === window } greet(); // "Hello, John!"
В глобальном контексте можно объявлять глобальные переменные, определять функции и выполнять другие глобальные действия. Однако, хорошей практикой является минимизация использования глобальных переменных, чтобы избежать возможных конфликтов и неожиданных побочных эффектов.
How is context defined at runtime
В JavaScript есть несколько способов явно связать контекст функции. Рассмотрим два основных метода: bind()
и call()
/apply()
.
- Метод
bind()
: Методbind()
создает новую функцию, привязывая указанный объект к значениюthis
внутри функции. Возвращаемая функция может быть вызвана позднее с привязанным контекстом.
const obj = { name: 'John' }; function greet() { console.log(`Hello, ${this.name}!`); } const boundFunc = greet.bind(obj); boundFunc(); // "Hello, John!"
- Методы
call()
иapply()
: Методыcall()
иapply()
вызывают функцию с указанным контекстом. Они различаются способом передачи аргументов.call()
принимает аргументы в виде отдельных значений, аapply()
принимает аргументы в виде массива.
const obj = { name: 'John' }; function greet() { console.log(`Hello, ${this.name}!`); } greet.call(obj); // "Hello, John!" greet.apply(obj); // "Hello, John!"
Связывание контекста с помощью bind()
, call()
и apply()
является одноразовым действием, и после этого контекст функции нельзя изменить. То есть, если контекст уже был связан с помощью одного из этих методов, повторное связывание контекста не будет иметь никакого эффекта.
const obj1 = { name: 'John' }; const obj2 = { name: 'Jane' }; function greet() { console.log(`Hello, ${this.name}!`); } const boundFunc = greet.bind(obj1); boundFunc(); // "Hello, John!" const secondBoundFunc = boundFunc.bind(obj2); secondBoundFunc(); // "Hello, John!" (контекст не изменился)
Обратите внимание, что повторное связывание контекста просто создает новую функцию, привязанную к предыдущему контексту, но не изменяет контекст исходной функции.
When context is lost
- Глобальный контекст: Когда функция вызывается в глобальном контексте (за пределами любых функций), контекст будет ссылаться на глобальный объект (
window
в браузере илиglobal
в Node.js).
function greet() { console.log(`Hello, ${this.name}!`); } greet(); // "Hello, undefined!" (контекст потерян)
- Методы объекта: Контекст метода объекта (
this
) будет ссылаться на сам объект только при вызове метода напрямую от объекта. Если метод передается в другую переменную или вызывается без привязки контекста, контекст будет потерян.
const obj = { name: 'John', greet: function() { console.log(`Hello, ${this.name}!`); } }; const func = obj.greet; func(); // "Hello, undefined!" (контекст потерян)
- Callback-функции: Когда функция передается в качестве аргумента в другую функцию в качестве обратного вызова (callback), контекст функции может быть изменен на контекст вызывающей функции или быть потерян.
const obj = { name: 'John' }; function greet() { console.log(`Hello, ${this.name}!`); } function callFunction(callback) { callback(); } callFunction(greet); // "Hello, undefined!" (контекст потерян)
- Стрелочные функции: Стрелочные функции не создают собственного контекста (
this
) и используют контекст, в котором были объявлены.
const obj = { name: 'John', greet: function() { const innerFunc = () => { console.log(`Hello, ${this.name}!`); }; innerFunc(); } }; obj.greet(); // "Hello, John!"
Regular Expressions, RegExp
What is RegExp
Регулярные выражения (Regular Expressions или Regex) - это мощный инструмент для работы с текстовыми данными в различных языках программирования, включая JavaScript. Регулярные выражения представляют собой последовательность символов, которая определяет шаблон поиска в тексте.
С помощью регулярных выражений можно выполнять следующие операции:
- Поиск и сопоставление: Регулярные выражения позволяют найти совпадения с определенным шаблоном в тексте. Например, можно найти все вхождения слова "apple" в тексте или проверить, соответствует ли строка определенному формату.
- Замена: Регулярные выражения позволяют заменить совпадения с определенным шаблоном на другую строку или выполнить определенные преобразования текста.
- Разделение: Регулярные выражения могут использоваться для разделения строки на подстроки на основе заданного разделителя или шаблона.
- Проверка формата: Регулярные выражения могут использоваться для проверки формата строки, таких как проверка правильности электронной почты, номера телефона или пароля.
В JavaScript регулярные выражения создаются с помощью литерала /pattern/
или с помощью конструктора RegExp()
. Например, /apple/
- это регулярное выражение для поиска слова "apple".
Пример использования регулярного выражения в JavaScript:
const str = 'I have an apple and a banana'; const pattern = /apple/; const result = pattern.test(str); // Проверяет, содержится ли "apple" в строке console.log(result); // true const replaced = str.replace(/apple/, 'orange'); // Заменяет "apple" на "orange" console.log(replaced); // "I have an orange and a banana" const parts = str.split(/ an /); // Разделяет строку по шаблону " an " console.log(parts); // ['I have', 'apple and a banana']
Two methods of RegExp object
Объект RegExp
в JavaScript представляет регулярное выражение. У этого объекта есть два основных метода: test()
и exec()
, которые используются для поиска совпадений с регулярным выражением в строке.
- Метод
test()
: Методtest()
используется для проверки, совпадает ли строка с регулярным выражением. Он возвращаетtrue
, если найдено совпадение, иfalse
, если совпадений нет.
const pattern = /apple/; const str = 'I have an apple and a banana'; const result = pattern.test(str); console.log(result); // true
- Метод
exec()
: Методexec()
используется для поиска совпадений с регулярным выражением в строке. Он возвращает массив информации о первом совпадении илиnull
, если совпадений не найдено. Если регулярное выражение имеет флагg
(глобальный поиск),exec()
можно вызывать повторно для поиска последующих совпадений.
const pattern = /apple/g; const str = 'I have an apple and a banana, and another apple'; let match; while ((match = pattern.exec(str)) !== null) { console.log(match[0]); // "apple" (первое совпадение), затем "apple" (второе совпадение) }
Метод exec()
также возвращает объект RegExp
, содержащий информацию о совпадении, включая само совпадение (match[0]
), найденные подгруппы (match[1]
, match[2]
и т.д.), позицию совпадения в строке и другую информацию.
Two ways to create RegExp
В JavaScript существуют два способа создания объекта RegExp
для представления регулярных выражений: с использованием литерала и с использованием конструктора RegExp()
.
- Литерал регулярного выражения: Литерал регулярного выражения - это форма записи регулярного выражения, заключенного в два слеша (
/
). Он состоит из шаблона (последовательности символов, определяющих шаблон поиска) и необязательных флагов, которые определяют дополнительные параметры поиска.
Пример с литералом регулярного выражения:
const pattern = /apple/;
- Конструктор
RegExp()
: КонструкторRegExp()
используется для создания объектаRegExp
с помощью строки, содержащей шаблон регулярного выражения. Можно также передать второй аргумент, содержащий флаги.
Пример с конструктором RegExp()
:
const pattern = new RegExp('apple');
Использование литерала или конструктора RegExp()
в основном зависит от того, нужно ли создавать регулярное выражение на основе статического шаблона (литерал) или динамического шаблона, который может быть определен в виде строки (конструктор RegExp()
).
Флаги, которые можно использовать с регулярными выражениями, включают:
g
(глобальный поиск): поиск всех совпадений, а не только первого.i
(игнорирование регистра): игнорирование регистра символов при поиске.m
(многострочный режим): поиск совпадений в нескольких строках.
Пример использования флагов с литералом и конструктором RegExp()
:
const pattern1 = /apple/gi; // Использование флагов с литералом const pattern2 = new RegExp('apple', 'gi'); // Использование флагов с конструктором RegExp()
Basic RegExp pattern (character classes)
В регулярных выражениях (RegExp) в JavaScript, Basic RegExp pattern, также известный как Character Classes (классы символов), используется для указания набора символов, которые могут совпадать с определенными позициями в тексте. Классы символов позволяют задавать ожидаемые значения и шаблоны поиска для символов или групп символов.
Вот некоторые распространенные символьные классы, которые можно использовать в регулярных выражениях:
[abc]
: Совпадение с любым одним символом из указанного набора. Например,[abc]
будет соответствовать символам "a", "b" или "c".[a-z]
: Совпадение с любым одним символом в диапазоне от "a" до "z". Например,[a-z]
будет соответствовать любой строчной букве от "a" до "z".[A-Z]
: Совпадение с любым одним символом в диапазоне от "A" до "Z". Например,[A-Z]
будет соответствовать любой заглавной букве от "A" до "Z".[0-9]
: Совпадение с любой одной цифрой. Например,[0-9]
будет соответствовать любой цифре от 0 до 9.[^abc]
: Совпадение с любым одним символом, который НЕ находится в указанном наборе. Например,[^abc]
будет соответствовать любому символу, кроме "a", "b" или "c".[\d]
: Совпадение с любой одной цифрой (аналогично[0-9]
).[\D]
: Совпадение с любым одним НЕцифровым символом (аналогично[^0-9]
).[\w]
: Совпадение с любым одним буквенно-цифровым символом или символом подчеркивания. Эквивалентно[a-zA-Z0-9_]
.[\W]
: Совпадение с любым одним НЕбуквенно-цифровым символом или НЕсимволом подчеркивания. Эквивалентно[^a-zA-Z0-9_]
.
Пример использования символьных классов в регулярном выражении:
const pattern = /[a-zA-Z0-9]/; const str = 'Hello123!'; const result = pattern.test(str); console.log(result); // true
В этом примере регулярное выражение [a-zA-Z0-9]
ищет любой символ, который является буквой верхнего или нижнего регистра или цифрой. В данном случае, оно вернет true
, так как строка содержит символы, удовлетворяющие этому шаблону.
Квантификаторы
В регулярных выражениях (RegExp) в JavaScript, квантификаторы используются для определения количества повторений символов или групп символов. Они позволяют указывать, сколько раз предыдущий элемент должен быть найден, чтобы считаться совпадением.
Вот некоторые распространенные квантификаторы, которые можно использовать в регулярных выражениях:
*
(звездочка): Предыдущий элемент может быть найден 0 или более раз. Например,a*b
будет соответствовать "ab", "aab", "aaab" и так далее.+
(плюс): Предыдущий элемент должен быть найден 1 или более раз. Например,a+b
будет соответствовать "ab", "aab", "aaab" и так далее, но не "b" (поскольку "a" должно быть хотя бы одно).?
(вопросительный знак): Предыдущий элемент может быть найден 0 или 1 раз. Например,colou?r
будет соответствовать "color" или "colour".{n}
: Предыдущий элемент должен быть найден ровно n раз. Например,a{3}
будет соответствовать только "aaa".{n,}
: Предыдущий элемент должен быть найден по меньшей мере n раз. Например,a{2,}
будет соответствовать "aa", "aaa", "aaaa" и так далее.{n,m}
: Предыдущий элемент должен быть найден от n до m раз. Например,a{2,4}
будет соответствовать "aa", "aaa" и "aaaa"..
(точка): Соответствует любому одиночному символу, кроме символа новой строки. Например,a.b
будет соответствовать "aab", "axb", "a1b" и так далее.
Примеры использования квантификаторов в регулярных выражениях:
const pattern1 = /a*b/; const str1 = 'aab'; const result1 = pattern1.test(str1); console.log(result1); // true const pattern2 = /a+b/; const str2 = 'ab'; const result2 = pattern2.test(str2); console.log(result2); // false const pattern3 = /colou?r/; const str3 = 'color'; const result3 = pattern3.test(str3); console.log(result3); // true const pattern4 = /a{3}/; const str4 = 'aaa'; const result4 = pattern4.test(str4); console.log(result4); // true
Methods of Object, Array, Function.prototype
Object.keys
Метод Object.keys()
в JavaScript используется для получения массива, содержащего все перечисляемые свойства объекта. Он возвращает массив строк, представляющих имена свойств объекта в порядке, в котором они были определены.
Object.keys(obj)
где obj
- объект, чьи свойства нужно перечислить.
Пример использования Object.keys()
:
const person = { name: 'John', age: 30, city: 'New York' }; const keys = Object.keys(person); console.log(keys); // ['name', 'age', 'city']
В приведенном примере, Object.keys(person)
возвращает массив ['name', 'age', 'city']
, потому что объект person
имеет три свойства: name
, age
и city
.
После получения массива ключей, вы можете использовать его для итерации или выполнения других операций над свойствами объекта. Например, вы можете перебрать массив ключей с помощью цикла for...of
или метода forEach()
для выполнения определенных действий для каждого свойства объекта.
const person = { name: 'John', age: 30, city: 'New York' }; const keys = Object.keys(person); for (const key of keys) { console.log(key + ':', person[key]); } // Output: // name: John // age: 30 // city: New York
Метод Object.keys()
в основном используется для работы с перечисляемыми свойствами объекта и предоставляет удобный способ получить доступ к ключам объекта в виде массива. Обратите внимание, что метод Object.keys()
не включает в себя не перечисляемые свойства и свойства, находящиеся в прототипе объекта.
Object.values
Метод Object.values()
в JavaScript используется для получения массива значений всех перечисляемых свойств объекта. Он возвращает массив значений, представляющих значения свойств объекта в том же порядке, в котором они были определены.
Object.values(obj)
где obj
- объект, чьи значения свойств нужно получить.
Пример использования Object.values()
:
const person = { name: 'John', age: 30, city: 'New York' }; const values = Object.values(person); console.log(values); // ['John', 30, 'New York']
В приведенном примере, Object.values(person)
возвращает массив ['John', 30, 'New York']
, так как объект person
имеет три свойства, и их значения соответственно: 'John'
, 30
и 'New York'
.
Массив значений может быть использован для итерации или выполнения операций над значениями свойств объекта. Например, вы можете использовать цикл for...of
или метод forEach()
для перебора массива значений и выполнения определенных действий для каждого значения.
const person = { name: 'John', age: 30, city: 'New York' }; const values = Object.values(person); values.forEach(value => { console.log(value); }); // Output: // John // 30 // New York
Метод Object.values()
удобен для получения доступа к значениям свойств объекта в виде массива. Обратите внимание, что метод Object.values()
возвращает только значения перечисляемых свойств и не включает не перечисляемые свойства и свойства, находящиеся в прототипе объекта.
for/in
Цикл for...in
в JavaScript используется для перебора перечисляемых свойств объекта. Он позволяет выполнить итерацию по всем свойствам объекта и выполнить определенные операции для каждого свойства.
for (variable in object) { // statements }
где variable
- переменная, которая будет содержать имя текущего свойства на каждой итерации, а object
- объект, для которого будет выполняться перебор.
Пример использования for...in
для перебора свойств объекта:
const person = { name: 'John', age: 30, city: 'New York' }; for (const key in person) { console.log(key + ':', person[key]); } // Output: // name: John // age: 30 // city: New York
В приведенном примере, цикл for...in
перебирает все свойства объекта person
. На каждой итерации, переменная key
содержит имя текущего свойства, а person[key]
предоставляет доступ к значению этого свойства.
Важно отметить, что цикл for...in
также обойдет свойства, находящиеся в прототипе объекта. Чтобы исключить свойства из прототипа, рекомендуется использовать проверку hasOwnProperty()
:
for (const key in person) { if (person.hasOwnProperty(key)) { console.log(key + ':', person[key]); } }
Также стоит учесть, что порядок перебора свойств может быть неопределенным, и не гарантируется, что свойства будут перебираться в том же порядке, в котором они были определены.
Цикл for...in
полезен при итерации по свойствам объекта и выполнении операций для каждого свойства. Однако, если вам нужно только получить ключи или значения объекта, рекомендуется использовать методы Object.keys()
, Object.values()
или Object.entries()
для получения массивов ключей, значений или пар ключ-значение объекта соответственно.
Map vs forEach
map()
и forEach()
- это два метода массива в JavaScript, которые позволяют выполнить итерацию по элементам массива и выполнить определенное действие для каждого элемента. Оба метода похожи, но есть некоторые ключевые различия:
forEach()
: МетодforEach()
выполняет указанную функцию обратного вызова один раз для каждого элемента массива. Он не возвращает новый массив и не изменяет исходный массив.
Пример использования forEach()
:
const array = [1, 2, 3]; array.forEach(element => { console.log(element); }); // Output: // 1 // 2 // 3
map()
: Методmap()
также выполняет указанную функцию обратного вызова для каждого элемента массива, но возвращает новый массив, содержащий результаты вызова функции обратного вызова для каждого элемента. Он позволяет преобразовать каждый элемент массива и получить новый массив с преобразованными значениями.
const array = [1, 2, 3]; const newArray = array.map(element => { return element * 2; }); console.log(newArray); // [2, 4, 6]
В приведенном примере, map()
умножает каждый элемент массива на 2 и возвращает новый массив [2, 4, 6]
.
Основные различия между map()
и forEach()
:
map()
возвращает новый массив с результатами вызова функции обратного вызова для каждого элемента, тогда какforEach()
не возвращает новый массив.map()
позволяет преобразовать элементы массива и получить новый массив с преобразованными значениями, тогда какforEach()
просто выполняет действие для каждого элемента массива без изменения исходного массива.map()
возвращает новый массив той же длины, что и исходный массив, в то время какforEach()
не изменяет длину массива.
Find, filter, some, includes
find()
, filter()
, some()
и includes()
- это методы массивов в JavaScript, которые позволяют выполнять операции поиска и проверки наличия элементов в массиве. Вот их описание и различия:
find()
: Методfind()
возвращает первый элемент массива, для которого функция обратного вызова возвращаетtrue
. Если ни один элемент не удовлетворяет условию, возвращаетсяundefined
.
const array = [1, 2, 3, 4, 5]; const foundElement = array.find(element => element > 3); console.log(foundElement); // 4
В приведенном примере, find()
ищет первый элемент в массиве, который больше 3, и возвращает 4.
filter()
: Методfilter()
создает новый массив, содержащий все элементы, для которых функция обратного вызова возвращаетtrue
.
Пример использования filter()
:
const array = [1, 2, 3, 4, 5]; const filteredArray = array.filter(element => element % 2 === 0); console.log(filteredArray); // [2, 4]
В приведенном примере, filter()
создает новый массив, содержащий только четные элементы из исходного массива.
some()
: Методsome()
проверяет, удовлетворяет ли хотя бы один элемент массива условию, заданному функцией обратного вызова. Он возвращаетtrue
, если условие выполняется хотя бы для одного элемента, иfalse
, если ни один элемент не удовлетворяет условию.
const array = [1, 2, 3, 4, 5]; const hasEvenNumber = array.some(element => element % 2 === 0); console.log(hasEvenNumber); // true
В приведенном примере, some()
проверяет, содержит ли массив хотя бы одно четное число, и возвращает true
.
includes()
: Методincludes()
проверяет, содержит ли массив определенное значение. Он возвращаетtrue
, если значение найдено в массиве, иfalse
, если значение отсутствует.
Пример использования includes()
:
const array = [1, 2, 3, 4, 5]; const includesThree = array.includes(3); console.log(includesThree); // true
В приведенном примере, includes()
проверяет, содержит ли массив значение 3, и возвращает true
.
find()
возвращает первый найденный элемент, который удовлетворяет условию, и прекращает поиск.filter()
создает новый массив со всеми элементами, которые удовлетворяют условию.some()
возвращаетtrue
, если хотя бы один элемент удовлетворяет условию, в то время какincludes()
возвращаетtrue
, если значение найдено в массиве.filter()
возвращает новый массив, даже если только один элемент удовлетворяет условию, в то время какsome()
иincludes()
возвращают логическое значение.includes()
выполняет простую проверку наличия значения, тогда какfind()
,filter()
иsome()
позволяют выполнять более сложные операции с элементами массива, используя функции обратного вызова.
Reduse
Метод reduce()
является одним из методов массива в JavaScript и используется для преобразования массива в одно единственное значение. Он выполняет итерацию по элементам массива и применяет указанную функцию обратного вызова (редуктор) к каждому элементу, сохраняя промежуточный результат.
array.reduce(callback, initialValue)
array
- исходный массив, над которым будет выполнена операция.callback
- функция обратного вызова (редуктор), принимающая четыре аргумента:accumulator
(аккумулятор) - значение, накапливающее результат предыдущих вызовов функции обратного вызова.currentValue
(текущий элемент) - текущий обрабатываемый элемент массива.currentIndex
(индекс текущего элемента) - индекс текущего элемента массива.array
- исходный массив, над которым выполняется операция.initialValue
(необязательный) - начальное значение аккумулятора. Если не указано, первый элемент массива будет использован в качестве начального значения, а итерация начнется со второго элемента.
Пример использования reduce()
:
const array = [1, 2, 3, 4, 5]; const sum = array.reduce((accumulator, currentValue) => { return accumulator + currentValue; }, 0); console.log(sum); // 15
В приведенном примере, reduce()
суммирует все элементы массива, начиная с начального значения 0. В результате получается сумма всех элементов массива, которая равна 15.
Метод reduce()
может выполнять более сложные операции и трансформации с использованием функции обратного вызова. Например, можно использовать reduce()
для нахождения максимального или минимального значения в массиве, объединения элементов массива в строку или создания нового объекта на основе элементов массива.
Важно помнить, что функция обратного вызова должна быть чистой функцией без побочных эффектов, чтобы обеспечить предсказуемое поведение.
Clone object
Вот несколько распространенных способов клонирования объектов в JavaScript:
const obj = { name: 'John', age: 30 }; const clone = { ...obj };
const obj = { name: 'John', age: 30 }; const clone = Object.assign({}, obj);
const obj = { name: 'John', age: 30 }; const clone = JSON.parse(JSON.stringify(obj));
const obj = { name: 'John', age: 30 }; const clone = _.cloneDeep(obj); // lodash // или const clone = R.clone(obj); // ramda
Важно отметить, что эти методы клонирования объектов создают поверхностные копии объектов. Если объект содержит вложенные объекты или ссылки на другие объекты, эти вложенные объекты будут передаваться по ссылке, а не копироваться полностью. В таких случаях может потребоваться глубокое клонирование, чтобы создать полную копию объекта со всеми его вложенными структурами.
Sort
Метод sort()
в JavaScript используется для сортировки элементов массива в соответствии с заданным порядком сортировки. Он изменяет исходный массив, переупорядочивая его элементы.
array.sort(compareFunction)
array
- исходный массив, который требуется отсортировать.compareFunction
(необязательный) - функция сравнения, определяющая порядок сортировки. Если не указана, элементы сортируются в соответствии с их строковым значением.
Функция сравнения (compareFunction
) может принимать два аргумента, обозначающих пару элементов массива, которые необходимо сравнить. Она должна возвращать отрицательное число, если первый элемент должен быть расположен перед вторым, положительное число, если первый элемент должен быть расположен после второго, или 0, если порядок элементов не требуется менять.
Пример сортировки числового массива:
const numbers = [5, 2, 8, 1, 4]; numbers.sort((a, b) => a - b); console.log(numbers); // [1, 2, 4, 5, 8]
В приведенном примере, массив numbers
сортируется в порядке возрастания, используя функцию сравнения (a, b) => a - b
. Это означает, что элементы будут сравниваться по числовому значению.
Пример сортировки строкового массива:
const fruits = ['banana', 'apple', 'orange', 'grape']; fruits.sort(); console.log(fruits); // ['apple', 'banana', 'grape', 'orange']
В этом примере, массив fruits
сортируется в лексикографическом (алфавитном) порядке по умолчанию, так как не указана функция сравнения.
Метод sort()
осуществляет сортировку внутри самого массива, изменяя исходный порядок элементов. Если вам нужно сохранить исходный массив и получить новый отсортированный массив, рекомендуется создать копию исходного массива перед вызовом sort()
.
Object freeze, seal
Object.freeze()
: МетодObject.freeze()
используется для замораживания объекта, делая его неизменным. Замороженный объект становится неизменяемым, и нельзя добавлять, удалять или изменять его свойства или методы. Попытка сделать изменения в замороженном объекте будет проигнорирована или вызовет ошибку в строгом режиме.
Пример использования Object.freeze()
:
const obj = { name: 'John', age: 30 }; Object.freeze(obj); // Попытка изменить свойство объекта obj.name = 'Jane'; // Изменение будет проигнорировано // Попытка добавить новое свойство объекта obj.city = 'New York'; // Добавление будет проигнорировано console.log(obj); // { name: 'John', age: 30 }
В приведенном примере, Object.freeze(obj)
замораживает объект obj
, делая его свойства неизменными.
Object.seal()
: МетодObject.seal()
используется для запечатывания объекта. Запечатанный объект остается изменяемым, но нельзя добавлять или удалять его свойства. Можно изменять значения существующих свойств, но не их тип или дескрипторы свойств. Попытка добавить или удалить свойство объекта будет проигнорирована или вызовет ошибку в строгом режиме.
Пример использования Object.seal()
:
const obj = { name: 'John', age: 30 }; Object.seal(obj); // Попытка удалить свойство объекта delete obj.age; // Удаление будет проигнорировано // Попытка добавить новое свойство объекта obj.city = 'New York'; // Добавление будет проигнорировано // Изменение значения существующего свойства obj.name = 'Jane'; console.log(obj); // { name: 'Jane', age: 30 }
В приведенном примере, Object.seal(obj)
запечатывает объект obj
, предотвращая удаление и добавление свойств, но разрешая изменение значений существующих свойств.
Object defined writable enumerable, definenProperty, For in в прототип
- Свойства объекта:
- Writable (доступность для записи): Определяет, может ли быть изменено значение свойства. Если значение установлено в
false
, то свойство становится доступным только для чтения, и попытка изменить его будет игнорироваться. - Enumerable (перечислимость): Определяет, будет ли свойство перечислено при итерации по свойствам объекта. Если значение установлено в
false
, то свойство не будет появляться в циклеfor...in
,Object.keys()
и других методах перебора свойств. - Configurable (конфигурируемость): Определяет, может ли быть свойство удалено или изменено его определение. Если значение установлено в
false
, то попытка удалить свойство или изменить его атрибуты будет игнорироваться. - Метод
Object.defineProperty()
: МетодObject.defineProperty()
используется для добавления нового свойства в объект или изменения атрибутов существующего свойства. Он позволяет явно установить атрибутыwritable
,enumerable
иconfigurable
для свойства объекта.
Пример использования Object.defineProperty()
:
const obj = {}; Object.defineProperty(obj, 'name', { value: 'John', writable: false, enumerable: true, configurable: false }); console.log(obj.name); // John obj.name = 'Jane'; // Изменение будет проигнорировано console.log(obj.name); // John for (const key in obj) { console.log(key); // name }
В этом примере, Object.defineProperty()
добавляет свойство name
в объект obj
с атрибутами writable: false
, enumerable: true
и configurable: false
. Следовательно, свойство name
доступно только для чтения, будет перечислено при итерации по свойствам объекта и его определение нельзя изменить или удалить.
- Цикл
for...in
и прототип: Циклfor...in
используется для перебора всех перечисляемых свойств объекта, включая свойства, унаследованные от его прототипа. При использованииfor...in
также следует учитывать свойства, определенные в цепочке прототипов.
Пример использования for...in
и свойств прототипа:
const obj = { name: 'John' }; Object.defineProperty(obj, 'age', { value: 30, enumerable: true }); Object.setPrototypeOf(obj, { city: 'New York' }); for (const key in obj) { console.log(key); // name, age, city }
В этом примере, свойство name
принадлежит самому объекту obj
, свойство age
добавлено с помощью Object.defineProperty()
, а свойство city
определено в прототипе объекта. В результате, при использовании for...in
, все эти свойства будут перечислены.
Promise, async/await
Promises vs callbacks
Промисы и колбэки (callback) - это два важных концепта в асинхронном программировании. Они используются для управления выполнением кода, который может занять некоторое время, например, операции сети или работы с файлами. В то время как колбэки являются традиционным способом работы с асинхронными операциями, промисы представляют более современный и элегантный подход.
Колбэки представляют собой функции, которые передаются в качестве аргументов другим функциям. Они вызываются после завершения асинхронной операции, позволяя программе продолжить свое выполнение. В JavaScript колбэки часто используются для обработки асинхронных задач.
Промисы представляют собой более структурированный и читаемый способ работы с асинхронными операциями. Они представляют собой обещание о будущем завершении (или ошибке) асинхронной задачи и позволяют связывать несколько асинхронных операций вместе.
Основное преимущество промисов перед колбэками заключается в том, что они позволяют писать асинхронный код более последовательно и читаемо с использованием методов then
и catch
для связывания операций и обработки ошибок. Промисы также предоставляют дополнительные возможности, например, возможность обрабатывать несколько асинхронных операций параллельно с помощью методов Promise.all
или Promise.race
.
Следует отметить, что начиная с ES2017, в JavaScript был добавлен синтаксис async/await
, который основан на промисах и предоставляет еще более выразительный способ написания асинхронного кода.
Examples of async functions
Вот пример простой асинхронной функции на JavaScript, которая задерживает выполнение на несколько секунд, а затем возвращает результат:
function delayAndReturnResult(value, delay) { return new Promise(function(resolve) { setTimeout(function() { resolve(value); }, delay); }); } // Пример использования асинхронной функции async function exampleAsyncFunction() { console.log("Начало выполнения"); const result = await delayAndReturnResult("Привет, мир!", 2000); console.log("Результат:", result); console.log("Завершение выполнения"); } exampleAsyncFunction();
В этом примере у нас есть асинхронная функция delayAndReturnResult
, которая принимает значение и задержку в миллисекундах. Она возвращает промис, который будет разрешен с переданным значением после указанной задержки.
Затем мы определяем основную асинхронную функцию exampleAsyncFunction
, которая использует ключевое слово await
, чтобы дождаться разрешения промиса внутри delayAndReturnResult
. В данном случае, функция будет ждать 2000 миллисекунд (или 2 секунды), прежде чем продолжить выполнение.
В результате выполнения кода мы увидим следующий вывод:
makefileCopy codeНачало выполнения (пауза в 2 секунды) Результат: Привет, мир! Завершение выполнения
Обратите внимание, что ключевое слово await
может использоваться только внутри асинхронных функций. Оно позволяет остановить выполнение функции до тех пор, пока промис не будет разрешен или отклонен. Это делает асинхронный код более линейным и понятным в отличие от использования колбэков или цепочек промисов.
States of promises
Промисы (Promises) имеют три основных состояния, которые отражают их текущее состояние выполнения. Эти состояния определяются спецификацией Promises/A+ и являются частью стандартного поведения промисов. Вот они:
- Ожидание (Pending): Изначальное состояние промиса называется "ожидание". В этом состоянии промис выполняется и не завершен ни успешно, ни с ошибкой. Промис может перейти в другое состояние, будь то выполнение (fulfilled) или отклонение (rejected).
- Выполнение (Fulfilled): Когда промис завершается успешно, переходит в состояние "выполнение". В этом состоянии промис считается успешно разрешенным, и он содержит значение, которое было передано функции
resolve()
при разрешении промиса. Значение это становится результатом промиса. - Отклонение (Rejected): Если при выполнении промиса происходит ошибка или отклонение, он переходит в состояние "отклонение". В этом состоянии промис считается отклоненным, и он содержит информацию об ошибке, которая была передана функции
reject()
при отклонении промиса.
Промис может перейти только из состояния "ожидание" в одно из двух конечных состояний: "выполнение" или "отклонение". После перехода в одно из этих состояний, промис больше не может изменить свое состояние.
Эти состояния промисов позволяют обработать результат асинхронной операции. При использовании промисов вы можете привязывать обработчики (then()
, catch()
, finally()
) к промису, которые будут вызываться при разрешении или отклонении промиса. Это позволяет легко обрабатывать успешные результаты или ошибки после выполнения асинхронной операции.
Finally
Метод finally()
- это метод, доступный для промисов в JavaScript, который позволяет указать код, который должен быть выполнен вне зависимости от того, был ли промис разрешен или отклонен. Этот метод добавляет обработчик "finally" к промису.
Синтаксис использования метода finally()
выглядит следующим образом:
promise.finally(onFinally);
promise
- промис, к которому вы хотите добавить обработчик "finally".onFinally
- функция обратного вызова, которая будет вызвана при завершении промиса, независимо от его разрешения или отклонения.
Вот пример использования метода finally()
:
function fetchData() { return new Promise(function(resolve, reject) { // Имитация асинхронной операции setTimeout(function() { const success = Math.random() < 0.5; if (success) { resolve("Данные успешно получены"); } else { reject("Ошибка при получении данных"); } }, 1000); }); } fetchData() .then(function(result) { console.log("Результат:", result); }) .catch(function(error) { console.error("Ошибка:", error); }) .finally(function() { console.log("Завершение операции"); });
В этом примере у нас есть функция fetchData()
, которая возвращает промис. Этот промис имитирует асинхронную операцию, которая может завершиться успешно или с ошибкой.
Мы используем метод then()
для обработки успешного разрешения промиса и метод catch()
для обработки ошибок. Затем мы добавляем метод finally()
, который указывает, что нужно выполнить код внутри функции обратного вызова независимо от того, был ли промис разрешен или отклонен.
В результате выполнения кода мы получим следующий вывод:
Результат: Данные успешно получены Завершение операции
Обратите внимание, что метод finally()
не возвращает новый промис. Он выполняется независимо от состояния промиса и не изменяет его разрешения или отклонения. Метод finally()
полезен для выполнения кода, который должен быть выполнен в любом случае, например, для очистки ресурсов или выполнения завершающих действий после асинхронной операции.
Exceptions/errors handling
Обработка исключений (exceptions) или ошибок является важной частью разработки программного обеспечения. В JavaScript и асинхронном программировании в особенности, есть несколько способов обработки ошибок. Рассмотрим некоторые из них:
- Использование блока try-catch: В JavaScript можно использовать блок
try-catch
для обработки синхронных ошибок. Код, который может вызвать исключение, помещается в блокtry
, а обработчик ошибок находится в блокеcatch
. Если в блокеtry
происходит исключение, выполнение кода переходит в блокcatch
. - Использование метода
catch()
у промисов: В асинхронном программировании с промисами можно использовать методcatch()
для обработки ошибок. Методcatch()
привязывается к промису и вызывается только в случае отклонения промиса. - Использование метода
finally()
у промисов: Как было упомянуто ранее, методfinally()
применяется для выполнения кода, который должен быть выполнен независимо от успешного разрешения или отклонения промиса. Он может использоваться для выполнения завершающих действий или очистки ресурсов. Обратите внимание, что в промисах методfinally()
не возвращает новый промис. Он выполняется независимо от разрешения или отклонения промиса.
Важно правильно обрабатывать исключения или ошибки в своем коде, чтобы обеспечить более надежную и предсказуемую работу программы. При обработке ошибок можно принимать различные решения, такие как вывод сообщений об ошибке, ретраи операции, запись логов и другие действия в зависимости от потребностей вашего приложения.
How to use then to catch errors
Метод then()
в промисах используется для обработки успешного разрешения промиса. Однако он также может быть использован для отлова ошибок в асинхронном коде. Для этого можно в then() передать первым аргументом null, а вторым функцию, для отлова ошибок.
Вот пример использования метода then()
для отлова ошибок в асинхронном коде:
function fetchData() { return new Promise(function(resolve, reject) { // Имитация асинхронной операции setTimeout(function() { const success = Math.random() < 0.5; if (success) { resolve("Данные успешно получены"); } else { reject("Ошибка при получении данных"); } }, 1000); }); } fetchData() .then(function(result) { console.log("Результат:", result); throw new Error("Некритическая ошибка"); // Генерация ошибки }) .then(null, function(error) { console.error("Произошла ошибка:", error); }) .then(function() { console.log("Продолжение выполнения"); });
В этом примере мы имитируем асинхронную операцию с помощью функции fetchData()
, которая возвращает промис. В первом обработчике then()
мы выводим успешный результат и генерируем некритическую ошибку с помощью оператора throw
.
Затем мы добавляем второй обработчик then()
, который принимает два аргумента. Первый аргумент - функция для обработки успешного разрешения промиса (в этом случае она равна null
, так как мы не обрабатываем успешное разрешение здесь). Второй аргумент - функция для обработки ошибок, которая будет вызвана при возникновении ошибки в предыдущем обработчике then()
.
Если в предыдущем обработчике then()
была сгенерирована ошибка, управление перейдет к второму обработчику then()
для обработки ошибок, и мы выведем сообщение об ошибке. Затем мы добавляем третий обработчик then()
, чтобы продолжить выполнение кода.
Обратите внимание, что если в обработчике then()
возвращается отклоненный промис или возникает исключение, управление перейдет непосредственно к ближайшему обработчику catch()
или следующему обработчику then()
с функцией обработки ошибок.
Таким образом, метод then()
может использоваться не только для обработки успешных результатов промиса, но и для отлова и обработки ошибок в асинхронном коде.
Проваливание промисов (Promise Chaining)
Проваливание промисов (Promise Chaining) — это практика цепочки методов then()
и catch()
для последовательного выполнения нескольких асинхронных операций, используя результат предыдущей операции в следующей.
При проваливании промисов каждый метод then()
возвращает новый промис, позволяя нам непрерывно связывать асинхронные операции в цепочку. Это делает код более лаконичным, понятным и позволяет легко обрабатывать ошибки.
Например, предположим, у нас есть три асинхронные функции fetchData1()
, fetchData2()
и fetchData3()
, которые возвращают промисы. Мы хотим последовательно вызвать эти функции и использовать результат каждой функции в следующей операции.
fetchData1() .then(function(result1) { console.log("Результат 1:", result1); return fetchData2(result1); // Используем результат в следующей операции }) .then(function(result2) { console.log("Результат 2:", result2); return fetchData3(result2); // Используем результат в следующей операции }) .then(function(result3) { console.log("Результат 3:", result3); }) .catch(function(error) { console.error("Произошла ошибка:", error); });
В этом примере мы вызываем fetchData1()
, а затем в обработчике then()
используем результат этого промиса для вызова fetchData2(result1)
. После успешного разрешения второго промиса, мы вызываем fetchData3(result2)
и так далее.
Каждый обработчик then()
возвращает новый промис, который может быть последовательно связан с последующими обработчиками. В результате мы имеем последовательное выполнение нескольких асинхронных операций в порядке их зависимости.
Если в любом обработчике then()
происходит отклонение промиса или возникает исключение, выполнение цепочки операций прекращается и управление переходит к ближайшему обработчику catch()
, где мы можем обработать ошибку.
Проваливание промисов позволяет писать чистый и последовательный код для выполнения цепочки асинхронных операций. Это особенно полезно, когда у нас есть множество операций, которые должны выполняться последовательно и зависят от предыдущих результатов.
Events
DOM events
DOM-события (Document Object Model events) позволяют отслеживать и реагировать на различные действия пользователя или изменения, происходящие веб-странице. События могут быть вызваны пользователем (например, кликом мыши или нажатием клавиши) или возникать в результате изменений веб-страницы (например, загрузка документа или изменение размера окна).
Вот некоторые основные понятия и примеры работы с DOM-событиями:
- Слушатели событий (Event Listeners): Чтобы отслеживать события, вы можете добавить слушатели событий к элементам DOM. Слушатели - это функции, которые вызываются при возникновении события. Вы можете добавить слушатель событий с помощью метода
addEventListener()
. - Объект события (Event Object): При вызове обработчика события создается объект события, который содержит информацию о событии и его контексте. Объект события передается в функцию обработчика события как аргумент. Вы можете получить доступ к свойствам и методам объекта события для получения информации о событии и его элементе-цели.
- Перехват и всплытие событий (Event Capturing and Bubbling): В DOM события могут распространяться по разным направлениям: погружение (capturing) и всплытие (bubbling). По умолчанию, события всплывают от вложенных элементов к родительским элементам (от внутри к снаружи). Вы можете использовать метод
addEventListener()
с опцией{ capture: true }
, чтобы установить режим погружения. - Отмена действия по умолчанию (Prevent Default): В некоторых случаях вы можете захотеть отменить действие по умолчанию, связанное с событием. Например, при клике на ссылку вы можете предотвратить переход по ссылке. Для этого используется метод
event.preventDefault()
.
DOM-события предоставляют мощный механизм для отслеживания и реагирования на взаимодействия пользователя и изменения на веб-странице. Вы можете использовать различные события и методы для создания интерактивных и отзывчивых пользовательских интерфейсов.
Handling (add/remove event handlers)
Обработка событий (event handling) включает добавление и удаление обработчиков событий для элементов DOM. Обработчики событий - это функции, которые вызываются при возникновении определенного события. Вы можете использовать методы addEventListener()
и removeEventListener()
для добавления и удаления обработчиков событий.
- Добавление обработчика событий (add event handler): Для добавления обработчика события к элементу DOM вы можете использовать метод
addEventListener()
. Этот метод принимает два аргумента: тип события и функцию обработчика. - Удаление обработчика событий (remove event handler): Если вам необходимо удалить ранее добавленный обработчик события, вы можете использовать метод
removeEventListener()
. Для удаления обработчика события необходимо передать тот же тип события и функцию обработчика, которую вы хотите удалить.
Обработчики событий можно также добавлять и удалять с помощью анонимных функций, как показано ниже:
element.addEventListener('click', function(event) { // Обработка события });
Использование анонимной функции удобно, когда обработчик события не нужно удалять позже.
При добавлении и удалении обработчиков событий убедитесь, что тип события и функция обработчика точно соответствуют. В противном случае, если вы пытаетесь удалить обработчик, который не был ранее добавлен, или добавить несуществующую функцию в качестве обработчика, ничего не произойдет или возникнут ошибки.
Basic types of DOM events
DOM (Document Object Model) предоставляет набор базовых типов событий, которые можно использовать для отслеживания различных действий пользователя или изменений веб-страницы. Вот некоторые из базовых типов событий DOM:
- События мыши (Mouse events):
click
: Событие возникает при клике на элемент левой кнопкой мыши.dblclick
: Событие возникает при двойном клике на элемент левой кнопкой мыши.mousedown
: Событие возникает при нажатии кнопки мыши на элементе.mouseup
: Событие возникает при отпускании кнопки мыши на элементе.mousemove
: Событие возникает при движении мыши над элементом.mouseover
: Событие возникает, когда указатель мыши перемещается на элемент.mouseout
: Событие возникает, когда указатель мыши покидает элемент.- События клавиатуры (Keyboard events):
keydown
: Событие возникает при нажатии клавиши на клавиатуре.keyup
: Событие возникает при отпускании клавиши на клавиатуре.keypress
: Событие возникает при нажатии клавиши, которая может создать символ (не всегда генерируется для всех клавиш).- События формы (Form events):
submit
: Событие возникает при отправке формы.input
: Событие возникает при изменении значения элемента формы (input, textarea, select).- События фокуса (Focus events):
focus
: Событие возникает, когда элемент получает фокус ввода.blur
: Событие возникает, когда элемент теряет фокус ввода.- События загрузки (Load events):
load
: Событие возникает, когда элемент или веб-страница полностью загружены.DOMContentLoaded
: Событие возникает, когда DOM-дерево документа полностью построено и доступно для манипуляции.- События изменения размера (Resize events):
- События прокрутки (Scroll events):
Это только некоторые из базовых типов событий DOM. Существуют и другие типы событий, а также пользовательские события, которые могут быть созданы и использованы в вашем коде.
Ways to prevent DOM event
Для предотвращения событий DOM и отмены их стандартного поведения, вы можете использовать методы event.preventDefault()
и event.stopPropagation()
внутри обработчика события. Вот как они работают:
event.preventDefault()
: МетодpreventDefault()
используется для отмены стандартного поведения события, которое обычно происходит при взаимодействии с элементами DOM. Например, при клике на ссылку или отправке формы, браузер обычно выполняет соответствующие действия (переход по ссылке, отправку формы), но с использованиемpreventDefault()
вы можете предотвратить это поведение.event.stopPropagation()
: МетодstopPropagation()
используется для предотвращения распространения события по иерархии элементов DOM. По умолчанию, события всплывают от вложенных элементов к родительским элементам. Если вы хотите остановить всплытие события на определенном элементе, вы можете вызватьstopPropagation()
внутри его обработчика события.
Метод preventDefault()
предотвращает стандартное поведение события, а метод stopPropagation()
предотвращает только всплытие события. Если событие имеет как стандартное поведение, так и всплытие, вы можете использовать оба метода внутри обработчика события.
preventDefault()
и stopPropagation()
работают только в пределах текущего обработчика события. Если у вас есть несколько обработчиков для одного и того же события, вызов этих методов в одном обработчике не влияет на другие обработчики.
Events propagation (bubbling/capturing)
Bubbling (всплытие) и capturing (погружение) - это два способа распространения событий в DOM (Document Object Model). Они определяют порядок, в котором события передаются от вложенных элементов к родительским элементам (всплытие) или от родительских элементов к вложенным элементам (погружение). Эти механизмы нужны для обработки событий на разных уровнях элементов DOM и предоставляют гибкость при создании интерактивных пользовательских интерфейсов.
- Capturing (погружение): В фазе перехвата событие передается сверху вниз от родительских элементов к вложенным элементам. При использовании перехвата обработчик события наиболее внешнего родительского элемента будет вызван первым, а затем обработчики вложенных элементов будут вызваны в порядке вложенности.
- Bubbling (всплытие): В фазе всплытия событие передается снизу вверх от вложенных элементов к родительским элементам. При использовании всплытия обработчик события наиболее вложенного элемента будет вызван первым, а затем обработчики родительских элементов будут вызваны в порядке восхождения по иерархии.
Обработка событий в режиме перехвата или всплытия позволяет вам контролировать порядок обработки событий в зависимости от вложенности элементов и определить, какие элементы должны обрабатывать события первыми. Это полезно, когда у вас есть иерархия элементов DOM, и вам нужно реагировать на события на разных уровнях элементов.
По умолчанию события в DOM распространяются в режиме всплытия (bubbling), но вы можете явно указать режим перехвата, устанавливая третий аргумент метода addEventListener()
в { capture: true }
.
Events delegation
Event delegation (делегирование событий) - это подход в обработке событий, при котором обработчик события добавляется к родительскому элементу, но реагирует на события, происходящие внутри его дочерних элементов. Вместо того чтобы добавлять обработчики событий к каждому дочернему элементу, вы добавляете один обработчик на родительский элемент и используете информацию о событии, чтобы определить, какой дочерний элемент вызвал событие.
Преимущества использования делегирования событий:
- Экномия ресурсов: При использовании делегирования событий вы добавляете только один обработчик на родительский элемент, вместо добавления обработчиков ко всем дочерним элементам. Это позволяет сэкономить ресурсы, особенно при работе с большим количеством элементов.
- Динамическое управление: Делегирование событий позволяет обрабатывать события для дочерних элементов, которые могут быть добавлены или удалены динамически во время выполнения. Обработчик события на родительском элементе будет автоматически применяться к новым дочерним элементам без необходимости явного добавления обработчиков.
- Упрощенный код: Делегирование событий может упростить ваш код, особенно когда у вас есть много элементов с общими обработчиками событий. Вместо того, чтобы добавлять и удалять обработчики для каждого элемента, вы добавляете один обработчик на родительский элемент и обрабатываете события на основе информации о событии.
При использовании делегирования событий важно правильно выбрать родительский элемент, который будет слушать события, чтобы добиться желаемого поведения. Также обратите внимание, что делегирование событий может быть осуществлено для любого типа события, такого как click
, mouseover
, keyup
и др.
Делегирование событий - это мощный подход, который помогает упростить код и повысить производительность, особенно при работе с динамическими иерархиями элементов DOM.
Стадии погружение-target-всплытие
Погружение (capturing), цель (target) и всплытие (bubbling) - это три фазы, которые событие проходит при распространении в DOM (Document Object Model) от вложенных элементов к родительским или в обратном направлении. Разберем каждую фазу подробнее:
- Погружение (capturing): В фазе погружения событие передается от верхнего родительского элемента к наиболее вложенному элементу. В этой фазе событие проходит через каждый элемент на пути к целевому элементу. Однако большинство обработчиков событий обычно не используют эту фазу, и она редко используется в практике.
- Цель (target): Фаза цели наступает, когда событие достигает целевого элемента, на котором произошло событие. В этой фазе событие находится в самом центре и обрабатывается обработчиком события, назначенным этому элементу.
- Всплытие (bubbling): В фазе всплытия событие передается от целевого элемента к его родительским элементам по иерархии. В этой фазе событие проходит через каждый родительский элемент на пути к корневому элементу (обычно это
window
илиdocument
). Обработчики событий, назначенные родительским элементам, также могут обрабатывать событие.
Важно отметить, что по умолчанию события в DOM распространяются в фазе всплытия (bubbling). Это означает, что сначала вызываются обработчики событий на самом вложенном элементе, затем на его родителе и так далее, пока не достигнут корневой элемент. Этот механизм всплытия позволяет событиям быть обработанными на разных уровнях иерархии элементов.
В результате, при клике на childElement
, обработчик для фазы погружения будет вызван первым, затем обработчик для фазы цели и, наконец, обработчик для фазы всплытия на родительском элементе.
Использование разных фаз событий позволяет вам контролировать порядок обработки событий и логику выполнения кода на разных уровнях элементов DOM в зависимости от ваших потребностей.
Semantic, Critical Rendering path, block/inline elements
Basic scheme for HTML document
Базовая схема HTML-документа включает в себя несколько основных элементов:
1. <!DOCTYPE html> - это объявление типа документа, которое сообщает браузеру, что документ является HTML-документом.
2. <html> - это элемент, который обозначает начало и конец HTML-документа.
3. <head> - это элемент, который содержит информацию о документе, такую как заголовок страницы, мета-теги, стили и скрипты.
4. <title> - это элемент, который определяет заголовок страницы, который отображается в верхней части окна браузера.
5. <body> - это элемент, который содержит основное содержимое страницы, такое как текст, изображения, ссылки и другие элементы.
Пример базовой структуры HTML-документа:
<!DOCTYPE html>
<html>
<head>
<title>Название страницы</title>
</head>
<body>
Основное содержимое страницы
</body>
</html>
HTML symbols usage
HTML-символы - это специальные символы, которые используются в HTML-коде для отображения различных символов, которые не могут быть набраны на клавиатуре или которые могут быть интерпретированы как часть HTML-кода.
Например, символ "&" (амперсанд) используется для обозначения начала HTML-символа, а символ ";" (точка с запятой) используется для обозначения конца HTML-символа.
Некоторые примеры HTML-символов:
1. < - символ "меньше" (<)
2. > - символ "больше" (>)
3. & - символ "амперсанд" (&)
4. " - символ двойной кавычки (")
5. ' - символ одинарной кавычки (')
6. - символ неразрывного пробела
Использование HTML-символов позволяет отображать специальные символы в HTML-коде без их интерпретации как части кода. Например, если вы хотите отобразить символ "<" на странице, вы можете использовать HTML-символ "<", который будет отображаться как "<".
Text formatting, paragraphs
HTML позволяет форматировать текст и параграфы на странице с помощью тегов.
Для форматирования текста можно использовать следующие теги:
1. <b> - жирный текст
2. <i> - курсивный текст
3. <u> - подчеркнутый текст
4. <strike> - зачеркнутый текст
5. <sup> - верхний индекс
6. <sub> - нижний индекс
Пример использования тега для жирного текста:
<b>Этот текст будет жирным</b>
Для форматирования параграфов можно использовать тег <p>, который создает новый абзац на странице.
Пример использования тега для параграфа:
<p>Это первый абзац.</p>
<p>Это второй абзац.</p>
Также можно использовать другие теги для форматирования текста, например, <h1>-<h6> для заголовков различных уровней или <pre> для отображения текста в формате, сохраняющем все пробелы и переносы строк.
Важно помнить, что использование излишнего форматирования может усложнить чтение и восприятие текста на странице, поэтому необходимо использовать его с умом и в соответствии с целями дизайна и стиля страницы.
HTML links. Link target
В HTML ссылки создаются с помощью тега <a>, который может содержать атрибуты href и target.
Атрибут href задает адрес, на который будет переходить пользователь при клике на ссылку. Например:
<a href="https://www.google.com">Перейти на Google</a>
Атрибут target задает, как будет открываться страница по указанной ссылке. Значение атрибута может быть одним из следующих:
- _self (по умолчанию) - страница откроется в текущем окне или вкладке браузера
- _blank - страница откроется в новом окне или вкладке браузера
- _parent - страница откроется в родительском фрейме, если он есть
- _top - страница откроется в верхнем фрейме, если он есть
<a href="https://www.google.com" target="_blank">Перейти на Google в новом окне</a>
Важно помнить, что использование атрибута target со значением _blank может вызвать негативную реакцию у пользователей, так как это может быть воспринято как попытка открыть рекламу или вирусы. Поэтому необходимо использовать этот атрибут с умом и только в тех случаях, когда это действительно необходимо для пользовательского опыта.
HTML tables
Таблицы HTML позволяют веб-разработчикам располагать данные в строках и столбцах.
Таблица в HTML состоит из ячеек таблицы, расположенных в строках и столбцах.
<table> <tr> <th>Company</th> <th>Contact</th> <th>Country</th> </tr> <tr> <td>Alfreds Futterkiste</td> <td>Maria Anders</td> <td>Germany</td> </tr> <tr> <td>Centro comercial Moctezuma</td> <td>Francisco Chang</td> <td>Mexico</td> </tr> </table>
Каждая ячейка таблицы определяется тегом <td> и </td>. td означает табличные данные. Все, что находится между <td> и </td>, является содержимым ячейки таблицы.
Ячейка таблицы может содержать всевозможные элементы HTML: текст, изображения, списки, ссылки, другие таблицы и т.д.
Каждая строка таблицы начинается с тега <tr> и заканчивается тегом </tr>. tr означает строку таблицы. В таблице может быть сколько угодно строк; просто важно убедиться, что количество ячеек в каждой строке одинаково.
Для того, чтобы ячейки были ячейками заголовка таблицы, нужно использовать тег <th> вместо тега <td>. th означает заголовок таблицы. По умолчанию текст в элементах <th> выделен жирным шрифтом и центрирован, но можно изменить это с помощью CSS.
Adding of scripts
Существует несколько способов добавления JavaScript-кода в HTML-страницу. Рассмотрим их особенности, плюсы и минусы.
- Внешний файл JavaScript:
- Подключение происходит с помощью тега
<script src="script.js"></script>
, гдеscript.js
- путь к внешнему файлу с JavaScript-кодом. - Плюсы:
- Позволяет разделять HTML-код и JavaScript-код, делая код более структурированным и читабельным.
- Кэшируется браузером, что может ускорить загрузку страницы при повторном посещении.
- Можно использовать один и тот же внешний файл JavaScript на нескольких страницах.
- Минусы:
- Встроенный JavaScript:
- JavaScript-код встраивается непосредственно в HTML-страницу с помощью тега
<script>
. - Плюсы:
- Нет необходимости загружать дополнительный файл.
- Удобно использовать для небольших фрагментов кода или одноразовых скриптов.
- Минусы:
- Обработка событий:
В целом, выбор метода зависит от конкретной ситуации и требований проекта. Рекомендуется использовать внешние файлы JavaScript для общего кода, встроенные скрипты для небольших фрагментов кода и обработку событий для простых скриптов, связанных с событиями.
Basic html elements (block, input, nav, select, p, li,button, forms, etc.
-
<div>
является блочным элементом и используется для группировки других элементов или создания блоков контента на веб-странице. <input>
: Этот элемент используется для создания интерактивных полей ввода на веб-странице. Например, с помощью<input>
можно создать текстовое поле (<input type="text">
), поле для пароля (<input type="password">
), флажок (<input type="checkbox">
), радиокнопку (<input type="radio">
) и другие виды полей ввода.<nav>
: Этот элемент представляет собой навигационное меню или блок навигации на веб-странице. Обычно он содержит ссылки или другие элементы, позволяющие пользователям перемещаться по различным разделам или страницам сайта.<select>
: Данный элемент представляет собой выпадающий список. Он позволяет пользователям выбрать один или несколько вариантов из предложенного списка. Для определения доступных вариантов используются элементы<option>
, которые обычно вложены внутрь тега<select>
.<p>
: Этот элемент используется для создания абзацев или отдельных текстовых блоков на веб-странице. Он обычно содержит текст, который будет отображаться как отдельный абзац.<li>
: Этот элемент представляет собой элемент списка. Он обычно используется внутри элементов<ul>
(маркированный список) или<ol>
(нумерованный список) для определения отдельных пунктов списка.<button>
: Данный элемент создает кнопку на веб-странице. Он может быть использован для вызова различных действий или отправки данных на сервер при нажатии на кнопку. Можно также определить тип кнопки с помощью атрибутаtype
, например:<button type="submit">
для отправки формы.- Формы (
<form>
): Элемент<form>
используется для создания форм на веб-странице. Формы позволяют пользователям вводить данные и отправлять их на сервер для обработки. Они содержат различные элементы, такие как<input>
,<select>
,<textarea>
и другие, которые позволяют пользователю вводить информацию.
Difference between block and inline elements
В HTML элементы можно классифицировать на блочные (block-level) и строчные (inline) элементы. Разница между ними заключается в их отображении и взаимодействии с другими элементами.
- Занимают всю доступную ширину горизонтально и располагаются на новой строке.
- Их ширина по умолчанию равна 100% от родительского элемента, если не задано иное.
- Можно устанавливать высоту, ширину, отступы, границы и задавать другие стили.
- Примеры блочных элементов:
<div>
,<p>
,<h1>
-<h6>
,<ul>
,<li>
,<nav>
,<form>
,<header>
,<footer>
и другие.
- Занимают только столько пространства, сколько необходимо для отображения содержимого.
- Не начинаются с новой строки, а идут в потоке текста рядом с предыдущим или следующим элементом.
- Нельзя задавать высоту и ширину, отступы по вертикали (только слева и справа), границы (только нижнюю и верхнюю) и другие свойства, которые влияют на вертикальное позиционирование.
- Примеры строчных элементов:
<span>
,<a>
,<strong>
,<em>
,<img>
,<input>
,<button>
,<label>
и другие.
Кроме того, есть также элементы, которые могут быть и блочными, и строчными, их называют инлайн-блоками (inline-block). Они занимают только столько горизонтального пространства, сколько необходимо для отображения содержимого, но при этом можно задавать высоту, ширину и другие стили, как для блочных элементов.
Понимание разницы между блочными и строчными элементами важно при создании и разметке веб-страниц, так как они влияют на компоновку элементов и их внешний вид.
Media (basic understanding what is it ond for what)
Атрибут media указывает, для какого носителя/устройства оптимизирован связанный документ.
Атрибут media используется для указания того, что целевой URL-адрес предназначен для специальных устройств (например, iPhone), речевых или печатных носителей.
Атрибут media может принимать несколько значений.
Атрибут media можно использовать для <a>, <area>, <link>, <source>, <style>.
Critical path of rendering
Критический путь рендеринга (Critical Rendering Path) - это последовательность шагов и процессов, которые браузер выполняет для отображения веб-страницы на экране пользователя. Оптимизация критического пути рендеринга является важным фактором для достижения быстрой и отзывчивой загрузки веб-страницы. Рассмотрим основные этапы критического пути рендеринга:
- Загрузка HTML: Браузер начинает загрузку HTML-кода веб-страницы. Этот шаг включает получение и анализ основной структуры документа, определение основных ресурсов (CSS, JavaScript) и начало формирования DOM (Document Object Model).
- Загрузка CSS: Браузер обрабатывает и загружает CSS-файлы, которые определяют стили и внешний вид элементов страницы. Загружаемые CSS-файлы могут быть внешними или встроенными в HTML. Во время загрузки CSS, браузер выполняет парсинг CSS-правил, создает CSSOM (CSS Object Model) и применяет стили к элементам DOM.
- Формирование DOM и CSSOM: Браузер строит DOM, представляющий структуру HTML-документа, и CSSOM, представляющий структуру стилей CSS. Оба этих объекта важны для определения, какие элементы должны быть отображены и как они должны быть стилизованы.
- Формирование Render Tree: Браузер объединяет DOM и CSSOM в Render Tree (дерево отображения), которое представляет фактическую структуру элементов, которые будут отображены на экране. Render Tree включает только те элементы, которые нужно отобразить и на которые применены стили.
- Вычисление макета (Layout): На этом этапе браузер определяет расположение и размеры каждого элемента в Render Tree. Это называется вычислением макета или рефлоу (reflow). Вычисление макета может быть ресурсоемким процессом, поэтому оптимизация этого шага является важным для достижения быстрой загрузки страницы.
- Отрисовка (Painting): На последнем этапе браузер производит отрисовку элементов Render Tree на экране. Это называется отрисовкой или растеризацией (painting). Браузер преобразует каждый элемент в Render Tree в пиксели на экране.
Оптимизация критического пути рендеринга включает такие подходы, как:
- Минимизация размера и количество CSS и JavaScript файлов.
- Использование сжатия и кэширования файлов.
- Асинхронная загрузка ресурсов.
- Использование критического CSS для быстрого отображения верхней части страницы.
- Размещение скриптов перед закрывающим тегом
</body>
для предотвращения блокировки рендеринга страницы. - Уменьшение количества и сложности селекторов CSS для более быстрой обработки стилей.
- Оптимизация изображений и их форматов.
Selector types, Selector weight, styles that are inherited
Frameworks layout technique
Frameworks layout technique - это подход к созданию веб-дизайна, использующий готовые CSS-фреймворки, которые предоставляют широкий набор готовых компонентов и сеток для быстрого создания современных и адаптивных макетов веб-страниц.
CSS-фреймворки содержат наборы классов и стилей, которые могут использоваться для создания различных элементов дизайна, таких как кнопки, формы, таблицы, сетки и т.д. Фреймворки предоставляют также множество различных опций для адаптивного дизайна, таких как классы для медиа-запросов, которые позволяют изменять стили элементов в зависимости от размера экрана устройства.
Одним из основных преимуществ использования фреймворков является экономия времени, которая достигается благодаря использованию готовых компонентов и стилей. Это позволяет быстро создавать и поддерживать веб-сайты, особенно когда нужно создавать прототипы или простые страницы.
Однако, следует учитывать, что использование фреймворков может привести к созданию сайтов с похожими дизайнами и ограниченной индивидуальностью. Кроме того, многие фреймворки имеют вес, который может замедлить загрузку страницы. Поэтому важно выбирать фреймворки с учетом потребностей проекта и оптимизировать их для максимальной производительности.
Selectors and their weight
Существует несколько способов выбора элементов, которые необходимо стилизовать в CSS.
- Селекторы типов: выберите нужный элемент, используя его тип элемента. Например, чтобы выбрать все ваши
<p>
теги, используйте ихp
в таблице стилей CSS. - Псевдоэлементы: как следует из названия, псевдоэлементы сами по себе не являются элементами, но позволяют выбрать часть HTML относительно другого селектора. Например, выберите первую букву каждого абзаца с помощью
p::first-letter
. - Селекторы классов: Элементы могут иметь несколько классов, установленных для них, чтобы быть выбранными в вашей таблице стилей CSS. Например,
<h1 class='header'>
можно выбрать с помощью.header
. К нескольким элементам может быть применен один и тот же класс. - Селекторы атрибутов: выберите элементы, к которым применен определенный тип атрибута. Например, выберите входы, которые принимают числа только с
input[type='number']
. - Псевдоклассы: выберите элементы на основе состояния CSS, в котором они находятся. Например, стилизуйте состояние наведения кнопки с помощью
button:hover
. - Селекторы идентификаторов: выберите конкретный элемент с его уникальным идентификатором. Может быть только один элемент с каждым идентификатором, тогда как классы могут применяться к нескольким элементам. Например,
<h1 id='mainHeader'>
с помощью#mainHeader
. - Встроенные стили: встроенные стили применяются к элементам непосредственно с
style
атрибутом, поэтому фактически селекторы CSS не используются. Например, можно сделать цвет шрифта заголовка синим непосредственно с помощью<h1 style='color: blue;'>
Каждый тип селектора, перечисленный выше, имеет свой вес. Все они могут быть разделены на четыре основные группы:
- наименьший вес: селекторы типов и псевдоэлементов
- низкий вес: селекторы классов, атрибутов и псевдоклассов
- средний вес: селекторы идентификаторов
- высокий вес: инлайн-стилизация
Если к одному и тому же элементу применяются стили с разным весом, то будет применен стиль с большим весом. Если применяются стили с одинаковым весом, то будут применены стили, которые идут последними (ближе к концу таблицы стилей). К любому стилизуемому элементу будет применен последний блок стилизации с наибольшим весом. При этом инлайн-стилизация будет иметь преимущество перед стилизацией селектора CSS.
Positioning
Позиционирование в CSS позволяет управлять размещением элементов на веб-странице. CSS предоставляет несколько методов позиционирования, которые можно применять к элементам. Вот некоторые из них:
position: static;
: Это значение по умолчанию для всех элементов. Элементы с позиционированием static располагаются в соответствии с нормальным потоком документа и их позиция не изменяется.position: relative;
: Элементы с позиционированием relative сдвигаются относительно своего нормального местоположения. Вы можете использовать свойстваtop
,right
,bottom
иleft
, чтобы указать, насколько сдвинуть элемент относительно его исходной позиции.position: absolute;
: Элементы с позиционированием absolute позиционируются относительно ближайшего предка с позиционированием, отличным от static, или относительно самого окна просмотра, если такого предка нет. Элемент с позиционированием absolute не оказывает влияния на позиционирование других элементов на странице.position: fixed;
: Элементы с позиционированием fixed позиционируются относительно окна просмотра и остаются на фиксированной позиции при прокрутке страницы. Они не будет прокручиваться вместе со страницей.position: sticky;
: Элементы с позиционированием sticky позиционируются в соответствии с нормальным потоком документа до тех пор, пока не достигнут определенного порога (например, определенной позиции прокрутки). Затем они становятся фиксированными и остаются на своем месте до тех пор, пока не будет прокручена вверх или достигнут другой порог.
Кроме указанных значений свойства position
, существуют и другие свойства, которые можно использовать для точного позиционирования элементов, такие как top
, right
, bottom
, left
, z-index
и другие. Они позволяют управлять точным положением элементов на странице.
Позиционирование в CSS является мощным инструментом для создания сложных макетов и адаптивного дизайна. Однако, при использовании позиционирования важно быть внимательным и следить за тем, чтобы не возникали перекрытия элементов или проблемы с доступностью. Рекомендуется также использовать позиционирование в сочетании с другими методами макета, такими как Flexbox или Grid, для достижения более гибкого и адаптивного дизайна.
Margings vs paddings
В CSS margin - это пространство вокруг границы элемента, а padding - пространство между границей элемента и его содержимым. Другими словами, свойство margin управляет пространством вне элемента, а свойство padding - пространством внутри элемента.
Fonts adding
Есть несколько способов добавления шрифтов CSS в проект:
1. Использование веб-шрифтов (web fonts), таких как Google Fonts или Adobe Fonts. Это позволяет подключить шрифт через URL-адрес веб-шрифта, который можно добавить в файл CSS.
2. Использование локальных шрифтов. Это означает, что вы загружаете шрифт на свой сервер и затем подключаете его к файлу CSS с помощью правильного пути к файлу шрифта.
3. Использование встроенных шрифтов. Это означает, что вы используете шрифт, который уже установлен на компьютере пользователя, и указываете его в свойствах CSS.
4. Использование @font-face правила CSS. Это позволяет загрузить шрифт с вашего сервера и использовать его в свойствах CSS.
5. Использование сторонних библиотек и плагинов, которые позволяют добавлять шрифты CSS в ваш проект. Например, Font Awesome позволяет использовать иконки вместе со специальным шрифтом.
Пример добавления шрифта с помощью @font-face:
@font-face { font-family: "MyFont"; src: url("path/to/font.ttf"); }
В этом примере мы создали новый шрифтовой семейство "MyFont" и указали путь к файлу шрифта TTF. Теперь этот шрифт можно использовать в свойствах CSS, например:
body { font-family: "MyFont", sans-serif; }
Element visibility. Ways to hide element
Видимость элементов в CSS определяется свойством visibility, которое может принимать значения visible (видимый) и hidden (скрытый). Однако, при использовании свойства hidden, элемент также становится невидимым, но его место сохраняется на странице, в отличие от свойства visibility: hidden, которое делает элемент скрытым, но место, занимаемое им, остается свободным.
Существует несколько способов скрыть элементы в CSS:
1. Свойство display: none полностью скрывает элемент из потока документа. Это означает, что элемент не будет занимать место на странице.
.element { display: none; }
2. Свойство visibility: hidden делает элемент невидимым, но он все еще занимает место на странице.
.element { visibility: hidden; }
3. Свойство opacity: 0 делает элемент полупрозрачным, но он все еще занимает место на странице.
.element { opacity: 0; }
4. Использование классов и псевдоклассов для изменения видимости элементов:
.hidden { display: none; }
.visible { display: block; }
5. Использование JavaScript для изменения стилей элементов:
document.getElementById('myElement').style.display = 'none';
Inline/block/block-inline elements, difference
В HTML элементы могут быть разных типов, например, inline, block и inline-block.
Элементы типа block занимают всю доступную ширину на странице и начинаются с новой строки. Это означает, что другие элементы, следующие после блочного элемента, будут располагаться под ним. Примерами блочных элементов являются <div>, <h1>-<h6>, <ul>, <ol>, <p>, <form> и другие.
Элементы типа inline занимают только столько места, сколько необходимо для отображения содержимого внутри элемента. Это означает, что другие элементы могут располагаться рядом с инлайн-элементом. Примерами инлайн-элементов являются <span>, <a>, <img>, <strong>, <em>, <input> и другие.
Элементы типа inline-block работают как инлайн-элементы, но имеют возможность установки ширины и высоты, как у блочных элементов. Они занимают только столько места, сколько необходимо для отображения содержимого внутри элемента и позволяют установить размеры элемента. Примерами таких элементов являются <button>, <textarea>, <select> и другие.
Важно понимать различия между типами элементов, чтобы выбрать наиболее подходящий элемент для каждой ситуации. Например, блочные элементы могут быть полезны для разделения контента на разные секции, в то время как инлайн-элементы могут быть полезны для отображения текста внутри предложения, а элементы inline-block могут быть полезны для создания кнопок и других элементов формы.
Z-index
Z-index в CSS - это свойство, которое определяет порядок наложения элементов на странице при использовании позиционирования. Значение z-index задает "глубину" элемента по отношению к другим элементам на странице. Это означает, что элемент с более высоким значением z-index будет отображаться поверх элементов с более низким значением z-index.
Значения z-index могут быть положительными числами, отрицательными числами или ключевыми словами. Положительные числа устанавливают более высокий уровень z-index, отрицательные числа - более низкий, а ключевые слова (например, auto, initial, inherit) определяют стандартные значения.
Например, чтобы установить элемент с классом "header" поверх остального контента, можно использовать следующее свойство:
.header { position: relative; z-index: 999; }
Здесь мы установили значение z-index равным 999, чтобы убедиться, что элемент "header" будет отображаться поверх остального контента. Также мы установили position: relative, чтобы свойство z-index работало корректно.
Значение z-index может быть полезно при создании слоев на странице и контролировании порядка наложения элементов. Однако, следует использовать значение z-index осторожно, чтобы не создавать нежелательных перекрытий и перекрытий элементов.
Types of position
В CSS существуют 5 типов позиционирования:
1. Статическое позиционирование (static) - это значение по умолчанию для всех элементов. Элементы с этим типом позиционирования располагаются в потоке документа, как обычно.
2. Относительное позиционирование (relative) - это тип позиционирования, при котором элемент также располагается в потоке документа, но может быть смещен относительно своего нормального положения с помощью свойств top, bottom, left и right.
3. Абсолютное позиционирование (absolute) - это тип позиционирования, при котором элемент вынимается из потока документа и размещается на странице в соответствии с его родительским элементом, который имеет позиционирование, отличное от static. Элемент может быть смещен относительно своего родительского элемента с помощью свойств top, bottom, left и right.
4. Фиксированное позиционирование (fixed) - это тип позиционирования, при котором элемент также вынимается из потока документа и размещается на странице в соответствии с окном браузера. Элемент может быть смещен относительно окна браузера с помощью свойств top, bottom, left и right.
5. Стикующее позиционирование (sticky) - это новый тип позиционирования, добавленный в CSS3. Элемент с этим типом позиционирования сначала располагается в потоке документа, но затем "прилипает" к верхней или нижней границе родительского элемента, когда пользователь прокручивает страницу.
Применение различных типов позиционирования в CSS позволяет создавать разнообразные макеты и манипулировать расположением элементов на странице.
Types of display
В CSS существуют несколько типов отображения (display):
1. block - элемент занимает всю доступную ширину на странице и начинается с новой строки. Примерами блочных элементов являются div, p, h1-h6, ul, ol, form и другие.
2. inline - элемент занимает только столько места, сколько необходимо для отображения содержимого внутри элемента. Это означает, что другие элементы могут располагаться рядом с инлайн-элементом. Примерами инлайн-элементов являются span, a, img, strong, em, input и другие.
3. inline-block - работает как инлайн-элементы, но имеет возможность установки ширины и высоты, как у блочных элементов. Они занимают только столько места, сколько необходимо для отображения содержимого внутри элемента и позволяют установить размеры элемента. Примерами таких элементов являются button, textarea, select и другие.
4. flex - это новое свойство в CSS3, которое позволяет создавать гибкие контейнеры для управления расположением элементов внутри них. Flexbox используется для создания сложных макетов и управления расположением элементов в трехмерном пространстве.
5. grid - это еще один новый тип отображения, добавленный в CSS3, который используется для создания сложных сеток и макетов. CSS Grid позволяет создавать многоколоночные макеты и управлять расположением элементов внутри сетки.
6. none - элемент не будет отображаться на странице.
7. table - элемент будет отображаться как таблица.
8. table-row - элемент будет отображаться как строка таблицы.
9. table-cell - элемент будет отображаться как ячейка таблицы.
10. list-item - элемент будет отображаться как элемент списка.
Выбор подходящего типа отображения для элементов на странице является важным шагом в создании хорошо структурированной и удобной для использования страницы.
Flexbox layout technique
Flexbox - это техника верстки, которая позволяет управлять расположением элементов на странице внутри контейнера. Она основана на концепции гибких контейнеров (flex-контейнеров) и гибких элементов (flex-элементов).
Flexbox предоставляет нам возможность управлять:
1. Направлением оси гибкого контейнера (row или column).
2. Расположением элементов вдоль оси (start, end, center, space-between, space-around).
3. Расположением элементов вдоль поперечной оси (stretch, baseline).
4. Размерами элементов (ширина и высота).
5. Порядком следования элементов в контейнере.
Для использования Flexbox необходимо определить контейнер с помощью свойства display: flex; или display: inline-flex; для инлайновых элементов. Затем мы можем использовать различные свойства для управления расположением элементов в контейнере.
Например, свойство justify-content позволяет управлять расположением элементов вдоль оси контейнера. Свойство align-items позволяет управлять расположением элементов вдоль поперечной оси.
Flexbox также позволяет нам управлять порядком следования элементов в контейнере с помощью свойства order. Это очень удобно, когда мы хотим изменить порядок элементов на мобильных устройствах или при использовании адаптивной верстки.
Одним из главных преимуществ Flexbox является его гибкость и простота использования. Он позволяет нам быстро и легко создавать сложные макеты, которые раньше были трудны для реализации с помощью CSS.
Responsive design (concept understanding)
Отзывчивый дизайн (responsive design) в CSS - это подход к верстке сайтов, который позволяет создавать сайты, которые автоматически адаптируются к различным устройствам и экранам.
Основная концепция отзывчивого дизайна заключается в том, что элементы на странице должны быть гибкими и универсальными, чтобы они могли изменять свой размер и расположение в зависимости от размера экрана устройства, на котором сайт просматривается.
Для реализации отзывчивого дизайна используются медиа-запросы (media queries), которые позволяют изменять стили элементов в зависимости от ширины экрана. Также используются относительные единицы измерения, такие как проценты и em, вместо фиксированных значений, чтобы элементы могли гибко изменять свой размер.
Отзывчивый дизайн в CSS позволяет создавать сайты, которые легко читаются и используются на различных устройствах, включая мобильные телефоны, планшеты и настольные компьютеры. Это повышает удобство использования сайта и улучшает опыт пользователей.
CSS Box Model
CSS Box Model - это концепция, которая описывает, как элементы на веб-странице отображаются и взаимодействуют друг с другом. Каждый элемент на странице представляет собой прямоугольную область, которая состоит из нескольких частей: контент (content), границы (border), поля (padding) и внешнего отступа (margin).
Контент - это область, в которой отображается содержимое элемента, такое как текст, изображения или видео.
Границы - это линия, которая окружает контент и отделяет его от других элементов на странице. Граница может иметь различную толщину, стиль и цвет.
Поля - это область между контентом и границей, которая может использоваться для создания отступов и увеличения пространства между контентом и границей.
Внешний отступ - это область за пределами границы элемента, которая используется для создания отступов между элементами на странице.
CSS Box Model позволяет управлять размерами и расположением элементов на странице, используя свойства CSS, такие как width, height, margin и padding. При правильном использовании CSS Box Model можно создавать красивые и функциональные веб-страницы с хорошей читаемостью и удобством использования для пользователей.
!important (> + ~)
!important
, >
, +
и ~
- это селекторы и комбинаторы в CSS, которые используются для определения и изменения стилей элементов на веб-странице. Рассмотрим каждый из них подробнее:
!important
:!important
- это ключевое слово, которое можно добавить к стилевому правилу в CSS для указания, что это правило должно иметь наивысший приоритет и переопределять любые другие стили для данного элемента. Правила, помеченные!important
, имеют преимущество над другими стилями, включая встроенные стили, стили, заданные атрибутомstyle
и стили, определенные через классы и селекторы.>
(Child combinator):>
(дочерний комбинатор) используется для выбора элементов, которые являются непосредственными дочерними элементами определенного родительского элемента.+
(Adjacent sibling combinator):+
(комбинатор соседних элементов) используется для выбора элементов, которые следуют непосредственно за другим элементом и имеют общего родителя.~
(General sibling combinator):~
(общий комбинатор соседних элементов) также используется для выбора элементов, но в отличие от комбинатора+
, он выбирает все элементы, которые следуют за другим элементом и имеют общего родителя.
Важно использовать эти селекторы и комбинаторы с осторожностью и ограничивать их использование только в необходимых случаях. Использование !important
может сделать стили менее предсказуемыми и сложнее в поддержке. Комбинаторы >
, +
и ~
могут быть полезными для определенных сценариев стилизации, но чрезмерное использование их может привести к неэффективным и сложным в понимании правилам стилей.
Why hooks?
What are hooks in React?
Хуки (Hooks) в React — это новое добавление в React 16.8, которое позволяет использовать состояние (state) и другие возможности React в функциональных компонентах. Они предоставляют более простой и элегантный способ написания компонентов, обеспечивая возможность использования состояния, эффектов (side effects) и других функциональностей React без необходимости создания классовых компонентов. Хуки позволяют разработчикам использовать и комбинировать предоставляемые React функции, такие как useState, useEffect, useContext и др., чтобы создавать более чистый и масштабируемый код.
The ability to explain the benefits of hooks in yourown words
Использование хуков в React предоставляет несколько преимуществ:
- Более простой и понятный код: Хуки позволяют писать компоненты в функциональном стиле, что делает код более читабельным и легким для понимания. Они устраняют необходимость в использовании классовых компонентов, которые могут быть громоздкими и сложными.
- Повторное использование логики: Хуки позволяют разделить и повторно использовать логику компонента. Вы можете создавать пользовательские хуки, которые инкапсулируют определенную функциональность и затем использовать их в разных компонентах. Это способствует улучшению модульности и переиспользованию кода.
- Управление состоянием: Хук useState позволяет добавлять и изменять состояние компонента без необходимости создания классовых компонентов и использования this.setState(). Это упрощает управление состоянием компонента и делает его более предсказуемым.
- Управление эффектами: Хук useEffect предоставляет возможность выполнять побочные эффекты (side effects), такие как загрузка данных, подписка на события или очистка ресурсов, в функциональных компонентах. Он заменяет методы жизненного цикла, такие как componentDidMount, componentDidUpdate и componentWillUnmount, и позволяет легко контролировать выполнение эффектов.
- Легкая миграция: Использование хуков не требует полной переписывания существующего кода. Они предназначены для постепенной миграции, поэтому вы можете постепенно заменять классовые компоненты на функциональные компоненты с использованием хуков.
Good understanding of hooks
Для работы с хуками в React можно использовать встроенные хуки, такие как useState, useEffect, useContext и другие. Их можно вызывать прямо внутри функциональных компонентов, чтобы получить доступ к состоянию, эффектам или контексту. Это позволяет вам добавлять функциональность к компонентам без необходимости создания классов. Помните, что названия хуков должны начинаться с префикса "use".
Why you can't use hooks inside conditions, loopsand nested functions?
- Условия: При использовании хуков внутри условных операторов (if, switch) может произойти изменение порядка вызова хуков между разными рендерами компонента. Если условие меняется между рендерами, то хуки могут быть вызваны в разном порядке, что приведет к несоответствию между сохраненным состоянием и отображаемыми данными.
- Циклы: Использование хуков внутри циклов (например, for или while) может привести к неожиданным результатам, так как цикл может выполняться множество раз, и вызовы хуков могут быть сделаны некорректно или в неправильном порядке. Каждый вызов хука должен происходить в одинаковом порядке для сохранения состояния и правильной работы компонента.
- Вложенные функции: Если хуки вызываются внутри вложенных функций, то это может привести к нарушению правил вызова хуков на верхнем уровне функционального компонента. Хуки должны быть вызваны непосредственно внутри основной функции-компонента, чтобы обеспечить последовательность вызовов и корректное сохранение состояния.
How to implement branching in hooks?
В хуках React можно реализовать ветвление, используя условные операторы внутри основной функции-компонента. Вот пример:
import React, { useState } from 'react'; const MyComponent = () => { const [isConditionMet, setConditionMet] = useState(false); const handleClick = () => { setConditionMet(!isConditionMet); }; return ( <div> <button onClick={handleClick}>Toggle Condition</button> {isConditionMet ? <p>Condition is met</p> : <p>Condition is not met</p>} </div> ); }; export default MyComponent;
В приведенном примере используется хук useState
, чтобы создать состояние isConditionMet
и функцию setConditionMet
для его изменения. При клике на кнопку Toggle Condition
, значение состояния меняется, и основной компонент рендерит различный контент в зависимости от значения isConditionMet
.
Используя условные операторы, такие как тернарный оператор (condition ? valueIfTrue : valueIfFalse
) или блок if
, можно определить различные варианты отображения или логику ветвления в зависимости от условий.
Важно помнить, что ветвление в хуках React следует осуществлять внутри функции-компонента, а не внутри условий, циклов или вложенных функций, чтобы сохранить правильный порядок вызовов хуков и предотвратить проблемы с состоянием и обновлением компонента.
Optimize hooks
Оптимизация хуков в React важна для улучшения производительности компонентов. Как их можно оптимизировать:
- Использовать хук React.memo для оптимизации рендеринга компонентов. Нужно обернуть компонент с помощью React.memo, чтобы предотвратить его повторный рендеринг при неизменных пропсах.
- Разделять хуки по логическим блокам. Если есть несколько состояний и эффектов в компоненте, нужно разделить их на отдельные хуки. Это улучшит читаемость кода и облегчит мемоизацию.
- Использовать мемоизацию с помощью хука useMemo или библиотеки, такой как reselect, чтобы предотвратить повторные вычисления сложных или затратных операций.
- Избегать создания хуков внутри условных операторов или циклов. Хуки нужно создавать на верхнем уровне функции-компонента, чтобы их вызовы оставались стабильными между рендерами.
- Использовать хук useCallback для мемоизации колбэк-функций, передаваемых в дочерние компоненты, чтобы предотвратить их перерисовку при изменении других состояний.
- Избегать бесконечных циклов обновлений. Убедитесь, что зависимости в хуках useEffect или useMemo правильно указаны, чтобы избежать ненужных повторных вызовов.
- Профилировать и измерять производительность компонента с помощью инструментов разработчика, таких как React DevTools или библиотеки для профилирования, чтобы идентифицировать проблемные места и улучшить их производительность.
Важно помнить, что оптимизация хуков нужно проводить в контексте конкретных потребностей и особенностей того или иного приложения. Необходимо оценивать производительность и проводить профилирование для определения оптимальных решений для какого-либо конкретного случая.
Writing custom hooks
Кастомные хуки в React - это функции, которые позволяют повторно использовать логику состояния и эффектов между различными компонентами. Они позволяют абстрагировать общую функциональность в отдельные хуки для более удобного и модульного кода.
Для написания кастомного хука понадобится создать обычную функцию в JavaScript, которая будет возвращать необходимые значения и функции для использования в компонентах. Название кастомного хука должно начинаться с префикса "use", чтобы соответствовать правилам хуков React.
Пример простого кастомного хука:
import { useState, useEffect } from 'react'; function useCounter(initialValue) { const [count, setCount] = useState(initialValue); useEffect(() => { document.title = `Count: ${count}`; }, [count]); function increment() { setCount(prevCount => prevCount + 1); } return { count, increment }; } export default useCounter;
В этом примере мы создаем кастомный хук useCounter
, который возвращает состояние count
и функцию increment
для увеличения значения count
. Каждый раз, когда изменяется count
, мы также обновляем заголовок документа с помощью хука useEffect
.
После написания кастомного хука вы можете использовать его в любом функциональном компоненте:
javascriptCopy codeimport useCounter from './useCounter'; function MyComponent() { const { count, increment } = useCounter(0); return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); }
Таким образом, кастомные хуки позволяют создавать переиспользуемую логику и разделять ее между различными компонентами в приложении.
useImperativeHandle, useTransition, useDefferedValue, useMemo, useCallback
useImperativeHandle
- это кастомный хук в React, который позволяет компоненту настраивать значение, которое передается родительскому компоненту при использованииref
. Он позволяет определить, какие значения или функции должны быть доступны черезref
при использовании компонента.useTransition
- это хук в React, который позволяет создавать плавные переходы при добавлении, обновлении или удалении элементов из списка. Он используется вместе сSuspense
иReact.lazy
для создания анимированных переходов между различными состояниями компонента.useDeferredValue
- это хук в React, который позволяет отложить обновление значения состояния на указанное количество рендеров. Это может быть полезно для оптимизации производительности и предотвращения перерисовок компонента при быстром изменении состояния.useMemo
- это хук в React, который позволяет мемоизировать вычисленное значение и повторно использовать его только при изменении зависимостей. Это помогает избежать лишних вычислений при каждом рендеринге компонента.useCallback
- это хук в React, который позволяет мемоизировать колбэк-функцию и повторно использовать ее только при изменении зависимостей. Это особенно полезно, когда колбэк передается дочерним компонентам в качестве пропса, чтобы избежать ненужных повторных рендерингов.
useId
useId - это хук React для генерации уникальных идентификаторов, которые можно передавать в атрибуты доступности.
Вызовите useId на верхнем уровне компонента, чтобы сгенерировать уникальный идентификатор:
import { useId } from 'react'; function PasswordField() { const passwordHintId = useId(); // ...
Зачем возвращать функцию в useEffect?
- Отмена подписок и отписка от событий: Если внутри
useEffect
была установлена подписка на событие или ресурс, возвращение функции позволяет отменить подписку или отписаться от события перед удалением компонента.
useEffect(() => { const subscription = subscribeToEvent(eventType, callback); return () => { subscription.unsubscribe(); // отмена подписки при удалении компонента }; }, [eventType, callback]);
- Очистка ресурсов: Если
useEffect
используется для создания или управления ресурсами, такими как таймеры или выделенная память, возвращение функции позволяет выполнить очистку этих ресурсов перед удалением компонента.
useEffect(() => { const timer = setInterval(() => { // логика обновления компонента }, 1000); return () => { clearInterval(timer); // очистка таймера при удалении компонента }; }, []);
- Отмена или очистка эффектов: Если внутри
useEffect
были применены эффекты, которые должны быть отменены или очищены при удалении компонента, возвращение функции позволяет выполнить эти действия.
useEffect(() => { document.addEventListener('keydown', handleKeyPress); return () => { document.removeEventListener('keydown', handleKeyPress); // удаление обработчика события при удалении компонента }; }, []);
Возвращение функции из useEffect
предоставляет механизм для выполнения таких "очисточных" или "отписывающих" операций, которые необходимы при удалении компонента или при изменении зависимостей эффекта. Это позволяет управлять ресурсами и подписками, предотвращать утечку памяти и поддерживать компоненты в состоянии готовности и очищенности.
Context
Understanding the concept of React.Context
React Context - это механизм в React, который позволяет передавать данные через дерево компонентов без явной передачи через каждый промежуточный компонент. Он позволяет создавать "контекст" данных, которые становятся доступными для всех компонентов внутри определенной области видимости.
Основные концепции и API React Context включают:
1) Создание контекста: Для создания контекста используется функция React.createContext()
. Она возвращает объект контекста, который состоит из Provider
и Consumer
.
const MyContext = React.createContext();
2) Поставщик (Provider): Компонент Provider
используется для определения данных, которые должны быть доступны внутри контекста. Он оборачивает компоненты, которым нужны эти данные.
<MyContext.Provider value={/* данные */}> {/* дочерние компоненты */} </MyContext.Provider>
Компоненты, обернутые в Provider
, могут получить доступ к данным из контекста с помощью Consumer
или хуков.
3) Потребитель (Consumer): Компонент Consumer
используется для чтения данных из контекста. Он должен быть вложен внутри Provider
и может быть использован в виде компонента или с помощью функциональных компонентов и хуков.
<MyContext.Consumer> {value => /* использование данных */} </MyContext.Consumer>
В React 16.8 и выше рекомендуется использовать хуки, такие как useContext
, для доступа к данным контекста.
4) Хук useContext: Хук useContext
позволяет функциональным компонентам получать доступ к данным контекста без использования Consumer
.
const value = useContext(MyContext);
Хук useContext
возвращает текущее значение контекста, предоставленное ближайшим Provider
выше компонента.
Контекст в React позволяет обеспечить глобальную доступность данных в приложении без явной передачи через множество компонентов. Это особенно полезно, когда данные должны быть доступны внутри глубоко вложенных компонентов или когда компоненты не имеют прямого родительского-потомка отношения. Однако следует быть осторожным при использовании контекста, чтобы не злоупотреблять им, так как он может усложнить отслеживание потоков данных и создать неявную связь между компонентами.
Ability to apply contexts
1) Создайте контекст: Используйте функцию React.createContext()
для создания контекста.
const MyContext = React.createContext();
2) Определите провайдер (Provider): Оберните компоненты, которым нужны данные из контекста, в компонент Provider
. Передайте данные в value
атрибут провайдера.
<MyContext.Provider value={/* данные */}> {/* дочерние компоненты */} </MyContext.Provider>
3) Получите доступ к данным контекста:
<MyContext.Consumer> {value => /* использование данных */} </MyContext.Consumer>
const value = useContext(MyContext);
Помните, что хук useContext
должен вызываться внутри функционального компонента.
4) Обновление данных контекста: Данные в контексте могут быть обновлены путем изменения значения, переданного в Provider
. Рекомендуется использовать состояние или Redux для управления обновлением данных контекста.
Пример использования контекста:
// Создание контекста const MyContext = React.createContext(); // Компонент-провайдер function MyProvider({ children }) { const data = "Some data"; // Данные контекста return ( <MyContext.Provider value={data}> {children} </MyContext.Provider> ); } // Компонент-потребитель function MyConsumer() { const value = useContext(MyContext); // Получение данных из контекста return <div>{value}</div>; } // Использование контекста function App() { return ( <MyProvider> <MyConsumer /> </MyProvider> ); }
В приведенном примере компонент MyConsumer
получает данные из контекста MyContext
, предоставленные MyProvider
. Компонент App
является точкой входа и оборачивает MyProvider
и MyConsumer
.
Обратите внимание, что провайдер и потребитель должны находиться в одной и той же ветке компонентов для того, чтобы данные контекста были доступными.
Для чего нужен Concumer, contextType в классах
Context.Consumer
:Context.Consumer
- это компонент-потребитель контекста, который позволяет получить значение из контекста в функциональных компонентах. Он принимает функцию в качестве дочернего элемента и передает значение контекста этой функции в качестве аргумента.Пример использованияContext.Consumer
:
const MyContext = React.createContext(); const MyComponent = () => ( <MyContext.Consumer> {(value) => <div>Value from context: {value}</div>} </MyContext.Consumer> );
contextType
в классах:contextType
- это свойство, которое можно добавить к классу компонента в React, чтобы указать, какой контекст должен быть доступен в этом компоненте. Оно позволяет получить значение контекста с помощьюthis.context
внутри методов класса.Пример использованияcontextType
:
const MyContext = React.createContext(); class MyClassComponent extends React.Component { static contextType = MyContext; render() { const value = this.context; return <div>Value from context: {value}</div>; } }
В этом примере классовый компонент MyClassComponent
определяет contextType
равным MyContext
. Затем значение контекста становится доступным в методах класса через this.context
.
Bubbling event in react
Understanding how component bubbling works
Всплытие компонентов (англ. component bubbling) - это механизм, встроенный в React, который позволяет событиям, возникающим вложенным компонентам, "всплывать" вверх по иерархии компонентов до тех пор, пока они не будут обработаны родительским компонентом.
Когда происходит событие внутри компонента, например, клик на кнопку, React сначала вызывает соответствующий обработчик события на самом компоненте. Затем событие передается вверх по иерархии компонентов от ребенка к родителю, и так далее, пока оно не будет полностью обработано или не достигнет корневого компонента приложения.
Для того чтобы событие всплыло, React использует синтетические события.
Для обработки всплывающих событий в React, можно назначить обработчики событий на родительские компоненты. Когда событие всплывает, родительский компонент может перехватить его и выполнить определенные действия. Это позволяет создавать компоненты с иерархией и передавать данные и обработчики событий от родительских компонентов к дочерним.
Working with bubbling in components
- Передача данных от дочерних компонентов к родительским: Можно использовать всплытие событий для передачи данных или состояния от дочернего компонента к родительскому. Например, когда происходит изменение в дочернем компоненте (например, изменение значения в поле ввода), событие может всплыть к родительскому компоненту, который может обновить своё состояние или выполнить другие действия на основе этих данных.
- Обработка событий на уровне родительского компонента: Можно использовать всплытие событий, чтобы обрабатывать определенные события на уровне родительского компонента. Например, если есть список элементов, каждый из которых имеет кнопку "Удалить", можно добавить обработчик события на родительский компонент списка, который будет реагировать на событие "клик на кнопку удаления" для выполнения соответствующих действий.
- Делегирование обработчиков событий: Можно использовать всплытие событий для делегирования обработчиков событий на высоком уровне компонентов. Например, может быть контейнерный компонент, содержащий несколько подкомпонентов, каждый из которых имеет собственные обработчики событий. Вместо явного назначения обработчиков каждому подкомпоненту, можно использовать всплытие событий и назначить обработчик на контейнерный компонент, который будет реагировать на события от любого из подкомпонентов.
- Разделение ответственности компонентов: Всплытие событий позволяет разделить ответственность между компонентами и сделать их более независимыми. Каждый компонент может быть ответственным только за обработку событий, происходящих внутри него, и передачу релевантных данных или событий выше по иерархии компонентов для дальнейшей обработки.
Understanding How Portal Bubbling Works (Basic understanding)
Фактически компоненты, размещенные в портале, все еще находятся в той же иерархии компонентов React, но они рендерятся в отдельном DOM-узле, который может быть размещен вне иерархии компонентов React.
Когда используются порталы в React, можно указать целевой DOM-узел, в котором компонент будет рендериться, вместо того, чтобы рендериться в DOM-узле, соответствующем родительскому компоненту. Это позволяет размещать компоненты вне текущей иерархии компонентов React.
Однако, с точки зрения иерархии компонентов React, компоненты в портале все равно являются дочерними элементами родительского компонента. Это означает, что они будут иметь доступ к контексту и пропсам родительского компонента и будут обновляться, когда изменяются состояния родительского компонента или его пропсы.
Таким образом, порталы позволяют контролировать место рендеринга компонентов в DOM, но не влияют на их иерархию внутри React.
Bubbling through portals (good understanding)
- Передача событий: Как упоминалось ранее, события не будут автоматически всплывать из порталов в родительские компоненты. Если нужно передать событие из портала в родительский компонент, придется явно передать данные через пропсы или использовать глобальное состояние (Redux, Context API).
- Пересечение стилей и событий: Поскольку порталы позволяют рендерить компоненты вне иерархии компонентов React, возможны конфликты стилей и обработчиков событий. Например, если портал размещается внутри компонента с определенными стилями, стили в портале могут пересекаться или переопределяться. То же самое относится и к обработчикам событий - если есть обработчик события в родительском компоненте, он может конфликтовать с обработчиком события в портале.
- Ограничения браузера: В некоторых случаях, особенно при работе с Shadow DOM или веб-компонентами, браузер может накладывать ограничения на использование порталов или вызывать неожиданное поведение. Важно проверить совместимость среды, в которой используются порталы.
- Семантическое понимание: Понимание концепции порталов может потребовать некоторого семантического понимания. Необходимо ясно представлять, что компоненты в портале все равно являются дочерними элементами родительского компонента в иерархии React, хотя они могут быть отображены в другом месте в DOM.
Возможна ли обработка событий в реакте на погружении?
В React события обычно обрабатываются на стадии всплытия (bubbling), а не на стадии погружения (capturing).
Стадия всплытия означает, что событие сначала срабатывает на самом вложенном элементе и затем "всплывает" вверх по иерархии DOM-дерева, срабатывая на родительских элементах.
Однако, в React также существует возможность обработки событий на стадии погружения, используя специальный флаг useCapture
в методе addEventListener
. Однако, стандартный подход в React не рекомендует использовать погружение для обработки событий.
Пример использования погружения в React:
jsxCopy codeimport React, { useRef } from 'react'; const MyComponent = () => { const myRef = useRef(); const handleEvent = (event) => { // Обработка события }; useEffect(() => { myRef.current.addEventListener('click', handleEvent, true); // true указывает на использование погружения return () => { myRef.current.removeEventListener('click', handleEvent, true); }; }, []); return <div ref={myRef}>My Component</div>; };
В этом примере компонент MyComponent
использует метод addEventListener
с флагом true
для указания погружения. Обработчик события будет вызван на стадии погружения при клике на элемент. Обратите внимание, что внутри хука useEffect
мы добавляем и удаляем обработчик события.
Однако, использование погружения для обработки событий в React не является типичным подходом, так как это может привести к сложностям в управлении событиями и понимании порядка обработки. В большинстве случаев использование стадии всплытия является более прямым и предпочтительным подходом.
Reselect&Recompose
Understanding what Reselect and Recompose are used for
Reselect и Recompose - это две разные библиотеки для работы с React, они позволяют разработчикам React улучшить производительность приложения, оптимизировать ререндеринг компонентов и сделать код более модульным и переиспользуемым. Reselect помогает избежать ненужных вычислений и перерисовок, а Recompose предоставляет удобные функции для композиции и обработки логики компонентов..
Reselect является библиотекой, которая предоставляет селекторы (selectors) для создания мемоизированных селекторов данных в приложении.
Селекторы - это функции, которые получают состояние хранилища данных (например, Redux) и вычисляют производные данные из него. Они могут быть использованы для извлечения и преобразования данных перед их передачей в компоненты.
Одна из основных задач Reselect состоит в том, чтобы предотвратить ненужные перерисовки компонентов, когда входные данные не изменились. Она делает это путем кэширования результатов селекторов и сравнения предыдущих и текущих результатов перед перерисовкой компонента.
Recompose - это набор функций высшего порядка (Higher-Order Components, HOC), которые позволяют компонентам React легко преобразовывать и композировать логику. Он предоставляет набор утилит, таких как `pure`, `withProps`, `withState`, `lifecycle` и других, которые позволяют управлять состоянием компонента, преобразовывать пропсы или жизненный цикл компонента. Recompose также обладает интеграцией с Reselect, позволяя использовать селекторы в композиции компонентов и обновлять их только при необходимости.
Ability to describe in your own words
Reselect предоставляет способ создания мемоизированных селекторов, которые позволяют получать данные из глобального состояния Redux-хранилища и преобразовывать их для использования в компонентах. Когда компонент запрашивает данные через селектор, Reselect проверяет, были ли входные данные селектора изменены с момента последнего запроса. Если данные не изменились, Reselect возвращает закэшированный результат, в противном случае он повторно вычисляет результат. Это позволяет избежать повторных вычислений и улучшает производительность приложения.
Recompose - это набор функций высшего порядка (Higher Order Components, HOC) для композиции и улучшения компонентов в React. Он предоставляет удобные методы для работы с жизненным циклом компонентов, обработки пропсов, оборачивания компонентов в другие компоненты и многое другое. Recompose позволяет создавать более модульный и читабельный код, разделять логику компонентов на более мелкие и переиспользовать их в разных частях приложения.
createSelector в RTK
createSelector
- это функция из библиотеки Redux Toolkit (RTK), которая позволяет создавать мемоизированные селекторы для выборки данных из состояния Redux. Она предоставляет удобный способ оптимизировать перерисовку компонентов, использующих данные из Redux, путем кэширования результатов предыдущих вызовов селекторов.
Селекторы в Redux позволяют получать определенные значения из хранилища состояния Redux. Они принимают состояние в качестве аргумента и возвращают вычисленные данные. Однако, без мемоизации селекторы могут вызываться повторно даже при отсутствии изменений в состоянии, что может привести к ненужным перерисовкам компонентов.
createSelector
решает эту проблему, создавая мемоизированный селектор, который кэширует результаты предыдущих вызовов и возвращает сохраненное значение, если входные данные не изменились.
Пример использования createSelector
в RTK:
import { createSelector } from '@reduxjs/toolkit'; // Селектор для получения списка пользователей const getUsers = (state) => state.users; // Создание мемоизированного селектора с помощью createSelector const selectActiveUsers = createSelector( getUsers, (users) => users.filter((user) => user.isActive) );
В этом примере сначала определяется базовый селектор getUsers
, который извлекает список пользователей из состояния Redux. Затем используется createSelector
, который принимает базовый селектор и функцию преобразования данных. В данном случае, функция фильтрует активных пользователей из списка.
Когда компонент использует selectActiveUsers
, селектор будет автоматически мемоизировать результаты предыдущих вызовов. Если состояние Redux не изменилось, то селектор вернет сохраненное значение, избегая ненужных перерисовок компонента.
Мемоизированные селекторы, созданные с помощью createSelector
, являются эффективным инструментом для оптимизации перерисовки компонентов в Redux приложениях. Они позволяют избежать повторных вычислений и обновлений компонентов только при изменении необходимых данных в состоянии.
Jest, Enzyme and other tools
What are jest and enzyme for
Jest - это фреймворк для тестирования JavaScript-кода. Jest предоставляет различные инструменты и функции, которые помогают разработчикам создавать и запускать тесты для своего кода.
- Юнит-тестирование компонентов: Jest обеспечивает простой и удобный способ тестирования отдельных компонентов React. Можно создавать тесты для каждого компонента и проверять, что они работают правильно. Jest предоставляет функциональность для создания фальшивых (mock) объектов и функций, а также утверждений (assertions), чтобы упростить их тестирование.
- Интеграционное тестирование: Jest также поддерживает интеграционное тестирование React-приложений. Можно создавать тесты, которые проверяют взаимодействие различных компонентов и их работу вместе. Это позволяет обнаруживать потенциальные проблемы или конфликты между компонентами.
- Снимки (Snapshots) компонентов: Jest позволяет создавать снимки компонентов, которые представляют собой предварительно сохраненные "скриншоты" вывода компонента. Можно сохранить снимки при первом выполнении теста, а затем автоматически сравнивать их с последующими запусками тестов. Это помогает обнаруживать нежелательные изменения в компонентах и предотвращать неожиданные побочные эффекты.
- Покрытие кода тестами: Jest также предоставляет отчеты о покрытии кода тестами. Он позволяет видеть, какая часть кода покрыта тестами и какие участки требуют дополнительного тестирования. Это полезно для поддержания высокого уровня качества кода и обеспечения полного покрытия.
Enzyme - это библиотека, разработанная Airbnb, которая используется для тестирования React-компонентов.
- Широкий выбор методов для манипуляции с компонентами: Enzyme предлагает различные методы для манипуляции с компонентами React, такие как монтирование (mount), нерендеринг (shallow), и рендеринг (render). Можно использовать эти методы для создания экземпляров компонентов, получения доступа к их состоянию и свойствам, и выполнения различных операций с ними во время тестирования.
- Удобный доступ к элементам компонента: Enzyme предоставляет удобные методы для поиска и манипуляции с элементами компонента, такими как
find
,at
,first
,last
и другие. Это позволяет легко получать доступ к определенным элементам компонента и проверять их состояние или свойства во время тестирования. - Проверка состояния и свойств компонента: Enzyme предоставляет функции утверждений (assertions), которые помогают проверять состояние и свойства компонентов. Можно использовать эти функции для проверки значения состояния, свойств или стилей компонента и убедиться, что они соответствуют ожидаемым результатам.
- Симуляция событий и взаимодействие: Enzyme позволяет симулировать события и взаимодействие с компонентами. Можно использовать методы, такие как
simulate
иprops
, чтобы запускать события, такие как клики, ввод текста и другие, и проверять, как компонент реагирует на эти события.
Basic understanding of writing tests
1. Создание тестового файла: Создайте новый файл с расширением `.test.js` или `.spec.js` рядом с компонентом, который вы хотите протестировать. Например, если есть компонент `Button.js`, нужно создать файл `Button.test.js` для его тестирования.
2. Описание и написание тестов: Нужно определить блок тестов с помощью функции `describe`, где можно объединить связанные тесты. Затем, внутри блока тестов, нужно использовать функцию `it` или `test` для написания отдельных тестовых случаев. Внутри каждого тестового случая можно выполнять операции, вызывать методы компонента, симулировать события и проверять ожидаемые результаты с помощью функций утверждений Jest.
describe('Button component', () => { it('renders without crashing', () => { const wrapper = shallow(<Button />); expect(wrapper.exists()).toBe(true); }); it('displays correct label', () => { const label = 'Click me'; const wrapper = shallow(<Button label={label} />); expect(wrapper.text()).toBe(label); }); // Другие тесты... });
3. Запуск тестов: Нужно запустить тесты с помощью команды `jest` в командной строке или в скрипте в проекте. Jest автоматически найдет и выполнит все тестовые файлы с расширением `.test.js` или `.spec.js`.
4. Анализ результатов: После выполнения тестов Jest выдаст отчет о результатах, указывая, пройдены ли тесты или нет, и информацию о покрытии кода тестами.
Good understanding of how to test various entities: components, utilities, HOCs, modals
- Тестирование компонентов:
- Импортируйте необходимые зависимости, включая компонент, который вы
хотите протестировать.
- Напишите тестовый случай с помощью функцииtest
илиit
и опишите
ожидаемое поведение компонента.
- Используйте функции утверждений (assertions) Jest для проверки
ожидаемых результатов. Например, вы можете использоватьexpect
для
сравнения фактического значения с ожидаемым, таким какexpect(actualValue).toBe(expectedValue)
.
- Запустите тесты с помощью Jest с помощью командыjest
в командной
строке или в скрипте в вашем проекте. - Тестирование HOC (Higher-Order Components):
- Импортируйте необходимые зависимости, включая ваш HOC и внутренний
компонент.
- Напишите тестовый случай с помощью функцииtest
илиit
и опишите
ожидаемое поведение HOC.
- Оберните внутренний компонент в HOC и проверьте, что HOC правильно
передает свойства (props) во внутренний компонент.
- Используйте функции утверждений Jest для проверки ожидаемых
результатов. - Тестирование утилит и модулей:
- Импортируйте необходимые зависимости, включая вашу утилиту или
модуль.
- Напишите тестовый случай с помощью функцииtest
илиit
и опишите
ожидаемое поведение вашей утилиты или модуля.
- Вызовите функцию или метод вашей утилиты или модуля и используйте
функции утверждений Jest для проверки ожидаемых результатов. - Дополнительные функциональности Jest:
Jest предлагает множество функций и возможностей для упрощения тестирования, таких как мокирование (mocking) и фальшивые (mock) функции, таймеры, асинхронные операции и другие. Вы можете использовать их для создания более сложных и полных тестов.
Basic application of test patterns
В контексте Jest, тестовые шаблоны (test templates) представляют собой предустановленные конфигурации для написания определенных типов тестов. Они предоставляют шаблоны кода, которые можно использовать как отправную точку при написании тестов, чтобы упростить и ускорить процесс.
Jest предоставляет несколько встроенных тестовых шаблонов, которые можно использовать в своих проектах:
describe
иtest
:
Это базовый шаблон для написания тестов.describe
используется для группировки связанных тестов внутри блока, аtest
(илиit
) используется для написания отдельных тестовых случаев. Пример использования:
describe('Math functions', () => { test('adds two numbers correctly', () => { expect(sum(2, 3)).toBe(5); }); test('multiplies two numbers correctly', () => { expect(multiply(2, 3)).toBe(6); }); });
beforeEach
иafterEach
:
Этот шаблон позволяет выполнять определенные действия перед каждым тестовым случаем (beforeEach
) и после каждого тестового случая (afterEach
). Пример использования:
describe('User management', () => { let user; beforeEach(() => { user = createUser(); }); afterEach(() => { deleteUser(user); }); test('user is created successfully', () => { expect(user.name).toBeDefined(); }); test('user is deleted successfully', () => { expect(user.deleted).toBeTruthy(); }); });
beforeAll
иafterAll
:
Этот шаблон позволяет выполнять определенные действия один раз перед всеми тестами (beforeAll
) и после всех тестов (afterAll
). Пример использования:
describe('Database connection', () => { let db; beforeAll(() => { db = connectToDatabase(); }); afterAll(() => { closeDatabaseConnection(db); }); test('inserts data into the database', () => { expect(insertData(db, { name: 'John' })).toBeTruthy(); }); test('fetches data from the database', () => { const data = fetchData(db); expect(data.length).toBeGreaterThan(0); }); });
Working with TTD approach is possible
TDD (Test-Driven Development) — это методология разработки программного обеспечения, которая заключается в написании тестов перед написанием самого кода. TDD подразумевает следование следующим шагам:
- Написание теста: Сначала разработчик пишет тест, который описывает ожидаемое поведение программы. Этот тест изначально должен не проходить, поскольку соответствующая функциональность еще не реализована.
- Запуск теста: Запуск теста для проверки неудачного прохождения.
- Написание кода: Затем разработчик пишет код, который реализует необходимую функциональность.
- Запуск тестов: После написания кода, разработчик запускает все тесты, включая только что написанный. Цель состоит в том, чтобы проверить, проходят ли все тесты, включая новый тест.
- Рефакторинг: При прохождении всех тестов, если код не удовлетворяет определенным стандартам, производится рефакторинг, чтобы улучшить его качество и поддерживаемость.
- Повторение: Процесс повторяется с написанием следующего теста и повторным написанием кода.
Таким образом, TDD заключается в циклическом повторении шагов "написание теста - написание кода - запуск тестов - рефакторинг". Главная идея TDD заключается в том, чтобы написать тесты, которые описывают ожидаемое поведение системы перед написанием фактического кода.
Почему TDD возможен? Основные преимущества TDD включают:
- Более надежный код: Наличие автоматических тестов гарантирует, что изменения в коде не приводят к нежелательным побочным эффектам и не ломают существующую функциональность.
- Быстрая обратная связь: Написание тестов перед кодом позволяет получить быструю обратную связь о том, что именно нужно реализовать. Тесты становятся спецификациями требований.
- Улучшение архитектуры: TDD способствует разделению ответственности и улучшает модульность кода, поскольку разработчики обязаны писать код, который легко тестируется.
- Легкое рефакторинг: Наличие набора автоматических тестов обеспечивает уверенность в том, что рефакторинг кода не нарушает его функциональность.
В целом, TDD позволяет разработчикам создавать более надежное, гибкое и хорошо тестируемое программное обеспечение.
React-router
Knows how to work with react-router
React Router - это библиотека маршрутизации для приложений на React, которая позволяет управлять навигацией веб-приложения и отображать соответствующие компоненты для каждого маршрута.
Основная идея React Router заключается в том, что каждому маршруту соответствует определенный компонент, который должен быть отображен при активации этого маршрута. Библиотека предоставляет набор компонентов и хуков, которые позволяют определить маршруты и обрабатывать навигацию пользователя.
Ключевые понятия и компоненты, которые предоставляет React Router:
- BrowserRouter: Компонент, который оборачивает ваше приложение и устанавливает контекст маршрутизации. Он использует HTML5 History API для поддержки маршрутизации на клиентской стороне.
- Route: Компонент, который определяет соответствие между маршрутом и отображаемым компонентом. Вы определяете путь маршрута и указываете, какой компонент должен быть отображен при активации этого маршрута.
- Switch: Компонент, который используется для обертывания компонентов Route. Он отображает только первый совпавший маршрут, что позволяет избежать отображения нескольких компонентов одновременно.
- Link: Компонент, который предоставляет ссылку для навигации между маршрутами. Он автоматически обрабатывает клики пользователя и обновляет URL, а также изменяет отображаемый компонент соответственно.
Can explain in his own words how it works
Для работы с библиотекой React Router вам потребуется выполнить несколько шагов:
- Установите React Router с помощью команды
npm install react-router-dom
. - Убедитесь, что у вас установлена версия React, так как React Router зависит от него.
- Импортируйте необходимые компоненты из
react-router-dom
, такие какBrowserRouter
,Route
,Switch
иLink
.
- Оберните ваше приложение в компонент
BrowserRouter
, чтобы установить контекст маршрутизации. - Определите маршруты с помощью компонента
Route
, указывая путь маршрута и компонент, который должен быть отображен при его активации. - Используйте компонент
Switch
, чтобы обернуть все компонентыRoute
. Это гарантирует отображение только первого совпадающего маршрута.
Шаг 4: Навигация между маршрутами.
- Используйте компонент
Link
для создания ссылок, которые будут переходить на другие маршруты. - Установите атрибут
to
у компонентаLink
, указывая путь маршрута, на который нужно перейти при клике на ссылку.
Пример использования React Router:
import React from 'react'; import { BrowserRouter, Route, Switch, Link } from 'react-router-dom'; import Home from './components/Home'; import About from './components/About'; import Contact from './components/Contact'; const App = () => { return ( <BrowserRouter> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/about">About</Link> </li> <li> <Link to="/contact">Contact</Link> </li> </ul> </nav> <Switch> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/contact" component={Contact} /> </Switch> </BrowserRouter> ); }; export default App;
В этом примере мы определяем маршруты для главной страницы, страницы "About" и страницы "Contact". Мы используем компонент Link
для создания ссылок в навигационном меню, которые переключаются между маршрутами. Компоненты Route
определяют соответствие между путями маршрутов и отображаемыми компонентами.
React Router предоставляет более мощные возможности для управления маршрутизацией, такие как передача параметров в пути, использование вложенных маршрутов и динамическое определение маршрутов. Для более сложных сценариев вы можете обратиться к документации React Router, где представлены подробные примеры и инструкции.
Hooks routing
При маршрутизации в React наиболее часто используются следующие хуки из библиотеки React Router:
useParams
: Этот хук используется для получения параметров из URL внутри компонента. Он позволяет извлекать динамические сегменты из пути маршрута и использовать их в компоненте.
Когда вы определяете динамический путь маршрута с помощью параметров в React Router, вы можете использовать useParams
для получения значения этих параметров внутри соответствующего компонента. Например, если у вас есть маршрут /users/:id
, где :id
является динамическим параметром, вы можете использовать useParams
для получения значения id
из URL.
Вот пример использования useParams
:
import { useParams } from 'react-router-dom'; const User = () => { const { id } = useParams(); return <div>User ID: {id}</div>; };
В этом примере мы импортируем useParams
из библиотеки React Router. Затем мы вызываем хук useParams
внутри компонента User
и деструктурируем значение id
из результата хука. Значение id
будет содержать значение параметра id
из URL.
Таким образом, если текущий URL соответствует маршруту /users/123
, то в компоненте User
будет отображаться текст "User ID: 123".
useParams
позволяет извлекать несколько параметров из URL, если ваш маршрут имеет несколько динамических сегментов. Вы можете добавить несколько переменных в деструктурирующем присваивании, чтобы получить значения каждого параметра.
useLocation
: Хук useLocation
позволяет получить текущий объект location
, который содержит информацию о текущем URL и его параметрах. Этот хук полезен, когда вам нужно получить доступ к текущему пути или параметрам URL.
Когда вы используете useLocation
, он возвращает объект location
, содержащий следующие свойства:
pathname
: Строка, представляющая текущий путь маршрута.search
: Строка, содержащая параметры строки запроса URL, начинающиеся с знака вопроса (?).hash
: Строка, содержащая хэш (anchor) URL, начинающийся с символа решетки (#).state
: Любые данные состояния, которые могут быть переданы черезlocation
(например, при использовании методаhistory.push
с объектом состояния).
Вот пример использования useLocation
:
import { useLocation } from 'react-router-dom'; const MyComponent = () => { const location = useLocation(); return ( <div> <p>Pathname: {location.pathname}</p> <p>Search: {location.search}</p> <p>Hash: {location.hash}</p> </div> ); };
В этом примере мы импортируем useLocation
из библиотеки React Router. Затем мы вызываем хук useLocation
внутри компонента MyComponent
и сохраняем результат в переменной location
. Мы используем свойства pathname
, search
и hash
объекта location
для отображения соответствующих значений в JSX.
Когда текущий URL соответствует маршруту /about?lang=en#section
, компонент MyComponent
будет отображать следующий результат:
Pathname: /about Search: ?lang=en Hash: #section
useLocation
обновляется автоматически при изменении URL. Если вы переходите на другой маршрут, объект location
будет содержать актуальные данные для нового местоположения. Это позволяет вам реагировать на изменения URL и выполнять соответствующие действия в вашем компоненте.
useHistory
: Хук useHistory
предоставляет объект history
, который позволяет программно управлять историей браузера. Он дает возможность выполнять навигацию и переходить на другие страницы.
Когда вы используете useHistory
, он возвращает объект history
, который содержит следующие методы:
push(path, [state])
: Добавляет новую запись в историю браузера и перенаправляет на указанный путь. Вы можете также передать необязательный объект состоянияstate
, который будет доступен в целевом компоненте через объектlocation.state
.replace(path, [state])
: Заменяет текущую запись в истории браузера новой записью и перенаправляет на указанный путь. Это аналогично методуpush
, но без добавления новой записи в историю.go(n)
: Переходит на предыдущую или следующую запись в истории, исходя из переданного значенияn
. Значениеn
может быть положительным (следующая запись) или отрицательным (предыдущая запись).goBack()
: Эквивалентно вызовуgo(-1)
, переходит на предыдущую запись в истории браузера.goForward()
: Эквивалентно вызовуgo(1)
, переходит на следующую запись в истории браузера.block(prompt)
: Позволяет заблокировать навигацию пользователя на другую страницу с помощью определенного сообщенияprompt
. Вы можете использовать это, чтобы предупредить пользователя о возможной потере несохраненных данных при попытке покинуть текущую страницу.
Вот пример использования useHistory
:
jsx Copy codeimport { useHistory } from 'react-router-dom'; const MyComponent = () => { const history = useHistory(); const handleButtonClick = () => { history.push('/about'); }; return ( <div> <button onClick={handleButtonClick}>Go to About Page</button> </div> ); };
В этом примере мы импортируем useHistory
из библиотеки React Router. Затем мы вызываем хук useHistory
внутри компонента MyComponent
и сохраняем результат в переменной history
. Мы используем метод push
объекта history
, чтобы выполнить программный переход на путь /about
при клике на кнопку.
useRouteMatch
: Хук useRouteMatch
позволяет получить информацию о совпавшем маршруте внутри компонента. Он полезен, когда вам нужно получить доступ к деталям о совпавшем маршруте, таким как путь, параметры, URL и другие свойства.
Когда вы используете useRouteMatch
, он возвращает объект, содержащий следующие свойства:
path
: Строка, представляющая путь маршрута, который совпал с текущим URL. Это может быть статический путь или путь с параметрами.url
: Строка, содержащая URL, соответствующий текущему пути маршрута. В случае использования динамических параметров, URL будет содержать значения параметров.isExact
: Булевое значение, указывающее, совпал ли текущий URL полностью с путем маршрута. ЕслиisExact
равноtrue
, это означает, что URL полностью соответствует пути маршрута, без дополнительных сегментов.params
: Объект, содержащий параметры из URL. Ключами в объекте будут имена параметров, а значениями - соответствующие значения параметров из URL.
Вот пример использования useRouteMatch
:
import { useRouteMatch } from 'react-router-dom'; const MyComponent = () => { const match = useRouteMatch('/users/:id'); if (match) { const { path, url, isExact, params } = match; return ( <div> <p>Path: {path}</p> <p>URL: {url}</p> <p>Is Exact: {isExact ? 'true' : 'false'}</p> <p>Params: {JSON.stringify(params)}</p> </div> ); } return <div>No match</div>; };
В этом примере мы импортируем useRouteMatch
из библиотеки React Router. Затем мы вызываем хук useRouteMatch
и передаем ему путь маршрута '/users/:id'
. Если текущий URL соответствует этому пути, мы получаем объект match
с информацией о совпавшем маршруте. Мы деструктурируем свойства из объекта match
и отображаем их значения в JSX.
Если текущий URL не совпадает с путем маршрута, будет отображено "No match".
React-router API
Good knowledge of what Route, Switch, Link and Redirect are for
- Route: Компонент
Route
используется для определения отдельного маршрута в вашем приложении. Он связывает определенный путь URL с соответствующим компонентом, который должен быть отображен, когда URL соответствует данному пути. КомпонентRoute
позволяет передавать дополнительные параметры, такие как параметры пути и запроса, через свойства. - Switch: Компонент
Switch
используется для обертывания несколькихRoute
-компонентов. Он отображает только первый совпавший маршрут внутри себя. Это полезно, когда у вас есть несколько маршрутов, и вы хотите отобразить только один из них, соответствующий текущему URL.Switch
помогает избежать отображения нескольких маршрутов одновременно. - Link: Компонент
Link
используется для создания ссылок в вашем приложении. Он заменяет стандартные HTML-теги<a>
.Link
предоставляет кликабельные ссылки, которые позволяют пользователям переходить по разным маршрутам в вашем приложении, не перезагружая страницу полностью. Он также обеспечивает активное состояние для текущего маршрута, добавляя классы или стили, чтобы выделить активный маршрут. - Redirect: Компонент
Redirect
используется для перенаправления пользователя с одного маршрута на другой. Он позволяет программно управлять навигацией и перенаправлением пользователей на другие страницы или маршруты в вашем приложении.Redirect
может быть использован, например, после успешной аутентификации пользователя или для обработки некорректных URL.
Что добавили в 6 версии react-router
В React Router 6 были внесены следующие изменения:
- Переключение на декларативный подход: В React Router 6 реализован полностью декларативный подход, отказавшись от предыдущих рендер-пропов и переключателя
<Switch>
. Теперь маршруты объявляются с помощью компонента<Route>
, а не рендерятся в зависимости от пути URL. Это упрощает конфигурацию и позволяет лучше контролировать визуализацию маршрутов. - Новый компонент
<Routes>
: Вместо<Switch>
теперь используется новый компонент<Routes>
, который позволяет определить дерево маршрутов в одном месте.<Routes>
позволяет определить все маршруты и их вложенность внутри него, что делает конфигурацию маршрутизации более читаемой и понятной. - Динамические маршруты: В React Router 6 добавлена поддержка динамических маршрутов, которые позволяют передавать параметры или переменные в пути URL. Динамические маршруты определяются с использованием синтаксиса
:paramName
в пути. Таким образом, компоненты могут получать значения параметров прямо из URL. - Отказ от
exact
: В React Router 6 отказались от использования пропаexact
для точного сопоставления пути маршрута. Вместо этого используется новый синтаксис*
, который указывает на точное сопоставление пути. Такое поведение стало более интуитивным и предсказуемым. - Улучшенная поддержка TypeScript: React Router 6 предоставляет улучшенную поддержку типизации с использованием TypeScript. Введены новые типы и улучшена интеграция с типами React.
3 redux main principles
Describe how it works
Redux - это библиотека управления состоянием (state management) в приложениях на основе React. Она помогает организовать и управлять состоянием приложения в централизованном хранилище (store), что упрощает разработку и поддержку сложных приложений.
Вот основные концепции и шаги, чтобы понять, как работает Redux:
- Хранилище (Store): Redux использует единое хранилище для хранения состояния всего приложения. Хранилище представляет собой объект, содержащий все данные состояния приложения.
- Действия (Actions): Действия представляют собой объекты, которые описывают, что произошло в приложении. Они являются единственным источником информации о том, что происходит с состоянием. Действия могут быть запущены из компонентов или других мест в приложении.
- Редюсеры (Reducers): Редюсеры - это функции, которые обрабатывают действия и определяют, как изменяется состояние приложения. Они принимают предыдущее состояние и действие, и возвращают новое состояние. Каждый редюсер отвечает за обновление определенной части состояния.
- Хранилище подписывается на редюсеры: Хранилище и редюсеры связываются вместе. Редюсеры передаются в хранилище, чтобы определить, как изменить состояние приложения в ответ на действия.
- Изменение состояния с помощью диспетчеризации действий: Для изменения состояния приложения необходимо отправить действие в хранилище. Это делается с помощью функции
dispatch
, которая отправляет действие в редюсеры. - Подписка на изменения состояния: Компоненты могут подписаться на изменения состояния, чтобы получать актуальную информацию. При каждом изменении состояния, Redux автоматически уведомляет подписанные компоненты, позволяя им обновиться.
- Инструменты разработчика: Redux предоставляет мощные инструменты разработчика, такие как Redux DevTools, которые помогают отслеживать и отлаживать изменения состояния, записывать действия и многое другое.
Почему нельзя мутировать store напрямую
В Redux нельзя мутировать состояние (store) напрямую, чтобы гарантировать предсказуемость и управляемость изменений в состоянии. Основная причина заключается в том, что Redux стремится к принципу неизменности данных (immutability).
Вот несколько причин, почему нежелательно мутировать состояние Redux напрямую:
- Предсказуемость: Изменение состояния Redux напрямую может привести к непредсказуемому поведению, так как компоненты могут не обновиться, если они не обнаружат изменений в состоянии. Redux использует механизм сравнения ссылок и значений для определения изменений, поэтому мутирование состояния может нарушить этот механизм и привести к неправильному обновлению компонентов.
- Отслеживаемость: Если изменить состояние Redux напрямую, будет сложно отследить, когда и какие изменения произошли. Redux имеет строгий контроль над изменениями состояния через действия (actions) и редюсеры (reducers), что делает процесс отслеживания и отладки более простым и понятным.
- Иммутабельность: Работа с неизменяемыми данными (immutable data) облегчает отслеживание изменений и реализацию временной путаницы (time-travel debugging) в Redux. Иммутабельность помогает обеспечить целостность состояния и избежать неожиданных побочных эффектов.
Вместо мутации состояния Redux рекомендуется использовать паттерн "единое направление данных" (unidirectional data flow). Это означает, что изменения состояния происходят путем создания и отправки действий (actions), которые затем обрабатываются редюсерами (reducers) для обновления состояния. Это позволяет Redux отслеживать изменения и обновлять компоненты в соответствии с этими изменениями.
HOC connect
connect
- это функция высшего порядка (Higher-Order Function), предоставляемая Redux, которая используется для связывания компонентов React с хранилищем Redux. Она создает новый компонент, который оборачивает исходный компонент и предоставляет ему доступ к состоянию и действиям из Redux.
- Подключение к хранилищу:
connect
принимает две функции в качестве аргументов -mapStateToProps
иmapDispatchToProps
.mapStateToProps
определяет, какое состояние из хранилища будет доступно в компоненте.mapDispatchToProps
определяет, какие действия из хранилища будут доступны в компоненте. - Создание нового компонента:
connect
создает новый компонент, который оборачивает исходный компонент. Этот новый компонент имеет доступ к состоянию и действиям Redux. - Подписка на изменения состояния: Новый компонент, созданный
connect
, автоматически подписывается на изменения состояния в хранилище. Это означает, что компонент будет обновляться при изменении соответствующих данных в хранилище. - Передача состояния и действий в компонент: С помощью функций
mapStateToProps
иmapDispatchToProps
,connect
передает соответствующие состояние и действия из хранилища в свойства (props) нового компонента. Это позволяет компоненту получать и использовать данные из Redux.
import { connect } from 'react-redux'; import { incrementCounter } from './actions'; // Определение компонента const Counter = ({ counter, incrementCounter }) => { return ( <div> <p>Counter: {counter}</p> <button onClick={incrementCounter}>Increment</button> </div> ); }; // Определение функций mapStateToProps и mapDispatchToProps const mapStateToProps = state => ({ counter: state.counter }); const mapDispatchToProps = { incrementCounter }; // Подключение компонента к хранилищу export default connect(mapStateToProps, mapDispatchToProps)(Counter);
В этом примере, connect
оборачивает компонент Counter
и связывает его с Redux. mapStateToProps
определяет, что состояние counter
из хранилища будет доступно в компоненте через свойство counter
. mapDispatchToProps
указывает, что действие incrementCounter
из хранилища будет доступно в компоненте как функция incrementCounter
.
4 аргумента HOC
High Order Component (HOC) - это функция, которая принимает компонент и возвращает новый компонент с дополнительным функциональным или декоративным поведением. HOC позволяют повторно использовать логику и функциональность между компонентами и предоставляют механизм композиции компонентов в React.
HOC может принимать до четырех аргументов, включая WrappedComponent и остальные аргументы:
- WrappedComponent: Это компонент, который будет обернут и расширен HOC. Он может быть передан как первый аргумент HOC.
- AdditionalArguments: Дополнительные аргументы могут быть переданы HOC, чтобы предоставить ему дополнительные данные или конфигурацию. Эти аргументы могут быть любого типа и передаются после WrappedComponent.
- Options: В некоторых случаях HOC может принимать настройки (options) в виде объекта, который определяет определенное поведение HOC. Настройки обычно передаются после AdditionalArguments и перед WrappedComponent.
- DisplayName: Аргумент DisplayName используется для задания имени обернутого компонента. Он может быть полезен для отладки и вывода имени компонента в инструментах разработчика. Обычно передается после WrappedComponent и Options.
Вот пример использования HOC с четырьмя аргументами:
jsxCopy codeconst withCustomBehavior = (WrappedComponent, additionalArg1, additionalArg2, options) => { const EnhancedComponent = (props) => { // Логика и функциональность HOC // ... return <WrappedComponent {...props} />; }; EnhancedComponent.displayName = `withCustomBehavior(${WrappedComponent.displayName || WrappedComponent.name})`; return EnhancedComponent; };
В этом примере withCustomBehavior
- это HOC, который принимает WrappedComponent и три дополнительных аргумента. Он создает новый компонент EnhancedComponent, который расширяет функциональность WrappedComponent. HOC может применять дополнительные аргументы или опции для настройки своего поведения.
Аргумент DisplayName используется для установки имени обернутого компонента. Если обернутый компонент имеет свое собственное имя, то оно будет отображаться в имени EnhancedComponent, иначе будет использовано общее имя HOC.
Несмотря на то, что HOC могут принимать четыре аргумента, часто в большинстве случаев используется только первый аргумент - WrappedComponent. Остальные аргументы и настройки зависят от конкретной реализации HOC и требований приложения.
useState/useReducer/useContext
Explain in your own words how useReducer work
Хук useReducer
является базовым хуком в React, который позволяет управлять состоянием в функциональных компонентах с помощью логики, основанной на "редюсере" (reducer) — функции, которая принимает предыдущее состояние и action, и возвращает новое состояние.
Вот как работает хук useReducer
:
- Импорт: В начале надо импортировать хук
useReducer
из библиотеки React. - Использование хука
useReducer
: Вызовите хукuseReducer
, передавая ему редюсер и начальное состояние. Он вернет массив с двумя элементами: текущее состояние и функцию диспетчера (dispatch), которую вы можете использовать для отправки действий редюсеру. - Использование состояния и диспетчера: Можно использовать текущее состояние, хранящееся в переменной
state
, внутри компонента. Для обновления состояния нужно вызывать функциюdispatch
, передавая ей объект action, содержащий свойствоtype
, указывающее тип действия, и другие необязательные свойства, необходимые для обновления состояния. - Пример использования:
import React, { useReducer } from 'react'; const initialState = 0; const reducer = (state, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } }; function Counter() { const [count, dispatch] = useReducer(reducer, initialState); const increment = () => { dispatch({ type: 'INCREMENT' }); }; const decrement = () => { dispatch({ type: 'DECREMENT' }); }; return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); }
В этом примере хук useReducer
используется для управления состоянием count
с использованием редюсера reducer
. При клике на кнопку "Increment" или "Decrement" вызывается соответствующая функция, которая отправляет соответствующее действие редюсеру через dispatch
.
useContext
Хук useContext
является одним из базовых хуков в React и позволяет получать доступ к значению контекста, предоставляемого родительским компонентом, внутри дочернего компонента без явной передачи пропсов через иерархию компонентов.
Вот как работает хук useContext
:
- Импорт: нужно импортировать хук
useContext
и контекст, к которому нужно получить доступ, из библиотеки React. - Использование хука
useContext
: Нужно вызвать хукuseContext
, передавая ему контекст, к которому нужно получить доступ. Он вернет текущее значение контекста. - Использование значения контекста: Теперь можно использовать значение контекста, хранящееся в переменной, внутри компонента.
- Пример использования:
import React, { useContext } from 'react'; import MyContext from './MyContext'; function ChildComponent() const contextValue = useContext(MyContext); return <p>Context Value: {contextValue}</p>; } function ParentComponent() { const contextValue = 'Hello, World!'; return ( <MyContext.Provider value={contextValue}> <ChildComponent /> </MyContext.Provider> ); }
В этом примере хук useContext
используется для получения доступа к значению контекста MyContext
внутри дочернего компонента ChildComponent
. Родительский компонент ParentComponent
определяет значение контекста и оборачивает ChildComponent
в провайдер контекста (MyContext.Provider
), передавая значение через value
.
Батчинг, flushSync
В React существует концепция "батчинга" (batching), которая позволяет группировать несколько обновлений состояния или изменений DOM в одну операцию. Один из механизмов батчинга в React - это функция flushSync
.
Функция flushSync
предоставляет возможность выполнить синхронные операции обновления состояния и изменения DOM без задержек. Она используется для явного запуска синхронных обновлений внутри компонентов, даже если React находится в режиме асинхронного рендеринга.
Вот пример использования flushSync
:
import { flushSync } from 'react-dom'; const MyComponent = () => { const handleClick = () => { flushSync(() => { // Синхронные операции обновления состояния или изменения DOM }); }; return <button onClick={handleClick}>Click Me</button>; };
В этом примере flushSync
вызывается внутри обработчика клика кнопки. Внутри функции flushSync
можно выполнять синхронные операции обновления состояния или изменения DOM, которые будут выполнены немедленно без ожидания следующего цикла рендеринга.
Обратите внимание, что использование flushSync
должно быть ограничено критическим случаям, когда требуется синхронное обновление. В большинстве ситуаций React автоматически обрабатывает батчинг и оптимизирует обновления, поэтому обычно нет необходимости явно вызывать flushSync
.
Однако, если вам требуется точно управлять синхронностью обновлений в компонентах, flushSync
может быть полезной функцией для выполнения синхронных операций в React.