Был в отпуске 2 недели. Погулял по Питеру несколько дней и после съездил к родственникам, которые живут за 1500 км от Минска.
Главное правило любого отдыха - не брать с собой ноутбук, с чем я успешно справился.
Сейчас потиху нужно возвращаться в рабочие задачи. Сегодня смог заставить себя сесть за работу только к 13:00 🙃
Главное правило любого отдыха - не брать с собой ноутбук, с чем я успешно справился.
Сейчас потиху нужно возвращаться в рабочие задачи. Сегодня смог заставить себя сесть за работу только к 13:00 🙃
🔥11
Совсем забыл рассказать, вышло новое видео на YouTube! 🔥
еще 3 недели назад))
В видео разобрал решение 3-х алгоритмических задач, которые часто попадаются на собеседованиях в бигтех. Задачи идут от простых к более сложным.
Поэтому кто еще не смотрел, переходите по ссылке! https://youtu.be/dYeOfz_ttUA?si=KFuP5zkr_NRu5Gu-
еще 3 недели назад))
В видео разобрал решение 3-х алгоритмических задач, которые часто попадаются на собеседованиях в бигтех. Задачи идут от простых к более сложным.
Поэтому кто еще не смотрел, переходите по ссылке! https://youtu.be/dYeOfz_ttUA?si=KFuP5zkr_NRu5Gu-
👍9
Расскажи, как работают браузеры?
Этот вопрос может попасться на собеседовании Front-End разработчику. Ставьте 🔥, если вам задавали такой вопрос на интервью.
Чаще всего интервьюер хочет от вас услышать про все этапы отображения контента в браузере. Начиная с того момента, как браузер получил первый пакет данных, и заканчивая отрисовкой контента на экране. Что ж, давайте подобно разберем все эти этапы.
1️⃣ Парсинг. Когда Браузер получил первый пакет данных, то он сразу начинает парсинг. Парсинг - Преобразование данных в DOM и CSSOM.
1.1. Построение DOM-дерева. На данном этапе происходит обработка разметки HTML.
Парсер (обработчик) кроме обычных HTML-тегов может наткнуться на неблокирующие ресурсы (например, изображения). В этот момент браузер отправляет запрос на загрузку ресурсов, но сам продолжает обработку.
Важно отменить, что тег script без async или defer - это блокирующий ресурс. Когда парсер встречает блокирующий ресурс, то обработка HTML приостанавливается до завершения загрузки скрипта.
1.2. Построение CSSOM (объектная модель CSS). Браузер считывает каждый набор правил в CSS, создаёт дерево узлов с родителями, детьми и соседями, основываясь на CSS селекторах.
2️⃣ Рендеринг. Он делится на следующие этапы: стилизация, компоновка, отрисовка и композиция.
2.1. Стилизация. На данном этапе идет комбинация DOM и CSSOM в дерево рендеринга (DOM + CSSOM = Render Tree). Render tree дублирует структуру DOM, но сюда не попадают невидимые элементы. Например, <head /> или элементы с display: none не будут включены в Render Tree, так как они не должны быть отрисованы.
Итого, дерево рендеринга содержит информацию о том, какие узлы отображаются вместе с их вычисляемыми стилями. Render Tree используется как входные данные для процесса визуализации, в ходе которого на экране отобразятся элементы страницы в виде пикселей.
2.2. Компоновка (layout). На данном шаге вычисляется геометрия каждого узла, то есть ширина, высота и расположение внутри окна браузера.
Важное уточнение: когда компоновка происходит 2 и более раз, то этот процесс называется перекомпоновка (reflow).
2.3. Отрисовка (Paint). Во время фазы отрисовки браузер преобразует каждое поле, вычисленное на этапе компоновки, в фактические пиксели на экране. Грубо говоря, на данном этапе идет раскрашивание элементов.
Важное уточнение: когда отрисовка происходит 2 или более раз, то этот процесс называется перерисовка (repaint).
2.4. Композиция (необязательный этап). Композиция происходит, когда разделы документа отрисованы на разных слоях. Благодаря тому что элементы расположены на отдельных слоях, reflow и repaint для элементов одного слоя не затрагивают элементы на остальных слоях. Этим самым улучшается производительность отрисовки контента браузером.
Хороший пример композиции - анимация через свойства transform и opacity. Элементы с данными свойствами выносятся на отдельный слой и обрабатываются при помощи GPU (графический процессор). Если основной поток JS заблокируется (например, через бесконечный цикл), то анимация через transform и opacity будет работать и не зависнет на экране, так как она запускается в отдельном потоке. Но вот если написать анимацию через другие свойства (например, left, right, top, bottom), то анимация зависнет, если основной поток JS будет занят другой тяжелой задачей. Визуально это можно посмотреть в данной статье.
P.S. Более подробно углубиться в тему работы браузеров рекомендую через эту статью на MDN.
Этот вопрос может попасться на собеседовании Front-End разработчику. Ставьте 🔥, если вам задавали такой вопрос на интервью.
Чаще всего интервьюер хочет от вас услышать про все этапы отображения контента в браузере. Начиная с того момента, как браузер получил первый пакет данных, и заканчивая отрисовкой контента на экране. Что ж, давайте подобно разберем все эти этапы.
1️⃣ Парсинг. Когда Браузер получил первый пакет данных, то он сразу начинает парсинг. Парсинг - Преобразование данных в DOM и CSSOM.
1.1. Построение DOM-дерева. На данном этапе происходит обработка разметки HTML.
Парсер (обработчик) кроме обычных HTML-тегов может наткнуться на неблокирующие ресурсы (например, изображения). В этот момент браузер отправляет запрос на загрузку ресурсов, но сам продолжает обработку.
Важно отменить, что тег script без async или defer - это блокирующий ресурс. Когда парсер встречает блокирующий ресурс, то обработка HTML приостанавливается до завершения загрузки скрипта.
1.2. Построение CSSOM (объектная модель CSS). Браузер считывает каждый набор правил в CSS, создаёт дерево узлов с родителями, детьми и соседями, основываясь на CSS селекторах.
2️⃣ Рендеринг. Он делится на следующие этапы: стилизация, компоновка, отрисовка и композиция.
2.1. Стилизация. На данном этапе идет комбинация DOM и CSSOM в дерево рендеринга (DOM + CSSOM = Render Tree). Render tree дублирует структуру DOM, но сюда не попадают невидимые элементы. Например, <head /> или элементы с display: none не будут включены в Render Tree, так как они не должны быть отрисованы.
Итого, дерево рендеринга содержит информацию о том, какие узлы отображаются вместе с их вычисляемыми стилями. Render Tree используется как входные данные для процесса визуализации, в ходе которого на экране отобразятся элементы страницы в виде пикселей.
2.2. Компоновка (layout). На данном шаге вычисляется геометрия каждого узла, то есть ширина, высота и расположение внутри окна браузера.
Важное уточнение: когда компоновка происходит 2 и более раз, то этот процесс называется перекомпоновка (reflow).
2.3. Отрисовка (Paint). Во время фазы отрисовки браузер преобразует каждое поле, вычисленное на этапе компоновки, в фактические пиксели на экране. Грубо говоря, на данном этапе идет раскрашивание элементов.
Важное уточнение: когда отрисовка происходит 2 или более раз, то этот процесс называется перерисовка (repaint).
2.4. Композиция (необязательный этап). Композиция происходит, когда разделы документа отрисованы на разных слоях. Благодаря тому что элементы расположены на отдельных слоях, reflow и repaint для элементов одного слоя не затрагивают элементы на остальных слоях. Этим самым улучшается производительность отрисовки контента браузером.
Хороший пример композиции - анимация через свойства transform и opacity. Элементы с данными свойствами выносятся на отдельный слой и обрабатываются при помощи GPU (графический процессор). Если основной поток JS заблокируется (например, через бесконечный цикл), то анимация через transform и opacity будет работать и не зависнет на экране, так как она запускается в отдельном потоке. Но вот если написать анимацию через другие свойства (например, left, right, top, bottom), то анимация зависнет, если основной поток JS будет занят другой тяжелой задачей. Визуально это можно посмотреть в данной статье.
P.S. Более подробно углубиться в тему работы браузеров рекомендую через эту статью на MDN.
🔥24❤1
⚛️ Как работает React и его алгоритм Reconciliation?
Первым делом выделим основные сущности алгоритма Reconciliation:
1. Current Tree - дерево (Virutal DOM), которое отображает текущее состояние приложения.
2. Rendering Environment (RE) - отдельная среда, которая преобразует Virutal DOM в набор операций (задач).
3. Work-in-Progress Tree - дерево (Virutal DOM), которое отображает обновленное состояние приложения.
Давайте разбираться, как вообще эти сущности работают и взаимодействуют между собой. Рассмотрим все подробно по шагам.
1. При монтировании у нас создается Current Tree.
2. Current Tree передается в Rendering Environment. RE в свою очередь преобразует дерево в набор задач и расставляет их по приоритетам.
3. Все задачи, которые создал RE, начинают выполняться по приоритету. После выполнения задач создается браузерный DOM и пользователь видит контент веб-страницы на экране.
4. После того как пользователь провзаимодействовал с веб-страницей (нажал кнопку, открыл модальное окно и т.п.), создается Work-in-Progress Tree, которое содержит обновленный Virtual DOM.
5. Происходит операция сравнения двух деревьев Current Tree и Work-in-Progress Tree. Все различия межу деревьями передаются в RE. Различия - это измененные, добавленные или удаленные узлы из Virtual DOM.
6. RE на основе различий двух деревьев создает задачи, которые опять же расставляет по приоритетам. Таким образом, React не обновляет полностью все дерево, а только те узлы, которые изменились.
7. Все заканчивается тем, что Work-in-Progress Tree становится Current Tree.
И дальше React снова ждет, когда появятся новые обновления в интерфейсе, чтобы проделать заново шаги 4-7. А интерфейс в React обновляется, когда изменяется state либо props компоненты.
Это базовое описание работы алгоритма Reconciliation. Конечно, можно углубиться в детали реализации виртуального DOM, но чаще всего данного объяснения на собеседовании хватает. В следующих постах будем более подробно разбирать Reconciliation и сам React.
P.S. У меня было одно собеседование, где интервьюер задал мне этот самый вопрос "Как работает React?". Я ему рассказал все примерно так же, как описано в этом посте. Он сказал, что вопросов задавать больше не будет, так как видит, что я хорошо шарю за React. В итоге собеседование длилось минут 15 🙃
Первым делом выделим основные сущности алгоритма Reconciliation:
1. Current Tree - дерево (Virutal DOM), которое отображает текущее состояние приложения.
2. Rendering Environment (RE) - отдельная среда, которая преобразует Virutal DOM в набор операций (задач).
3. Work-in-Progress Tree - дерево (Virutal DOM), которое отображает обновленное состояние приложения.
Давайте разбираться, как вообще эти сущности работают и взаимодействуют между собой. Рассмотрим все подробно по шагам.
1. При монтировании у нас создается Current Tree.
2. Current Tree передается в Rendering Environment. RE в свою очередь преобразует дерево в набор задач и расставляет их по приоритетам.
3. Все задачи, которые создал RE, начинают выполняться по приоритету. После выполнения задач создается браузерный DOM и пользователь видит контент веб-страницы на экране.
4. После того как пользователь провзаимодействовал с веб-страницей (нажал кнопку, открыл модальное окно и т.п.), создается Work-in-Progress Tree, которое содержит обновленный Virtual DOM.
5. Происходит операция сравнения двух деревьев Current Tree и Work-in-Progress Tree. Все различия межу деревьями передаются в RE. Различия - это измененные, добавленные или удаленные узлы из Virtual DOM.
6. RE на основе различий двух деревьев создает задачи, которые опять же расставляет по приоритетам. Таким образом, React не обновляет полностью все дерево, а только те узлы, которые изменились.
7. Все заканчивается тем, что Work-in-Progress Tree становится Current Tree.
И дальше React снова ждет, когда появятся новые обновления в интерфейсе, чтобы проделать заново шаги 4-7. А интерфейс в React обновляется, когда изменяется state либо props компоненты.
Это базовое описание работы алгоритма Reconciliation. Конечно, можно углубиться в детали реализации виртуального DOM, но чаще всего данного объяснения на собеседовании хватает. В следующих постах будем более подробно разбирать Reconciliation и сам React.
P.S. У меня было одно собеседование, где интервьюер задал мне этот самый вопрос "Как работает React?". Я ему рассказал все примерно так же, как описано в этом посте. Он сказал, что вопросов задавать больше не будет, так как видит, что я хорошо шарю за React. В итоге собеседование длилось минут 15 🙃
🔥19👍2
⚛️ Допущения алгоритма Reconciliation в React
В продолжение к посту как работает React и его алгоритм Reconciliation хочется рассказать про допущения этого алгоритма. Допущения алгоритма Reconciliation - это те условия, которые нам как разработчикам необходимо выполнять, чтобы React рендерил компоненты оптимизировано и без пролагивания UI.
Для начала давайте поймем, какая сложность алгоритма сравнения деревьев (Virtual DOM) у React? Алгоритм React это делает за O(N), т.е. сложность линейная. В то время как браузерное API сравнивает DOM-деревья за O(N^3).
И чтобы React продолжал выполнять свою работу за O(N), необходимо знать следующее.
1️⃣ изменился тип узла ==> происходит создание нового дерева.
Допустим, у нас было такое дерево,
а после обновления стало таким.
Как мы видим, тег main изменился на section. При этом контент внутри этого элемента остался без изменений. Но React удалит и заново создаст все узлы, которые начинаются с main и идут глубже, так как у узла изменился type. Был type: 'main', а стал type: 'section'.
2️⃣ изменился key узла ==> происходит создание нового дерева.
key - полезный инструмент в React. Он позволяет оставлять нетронутыми конкретные узлы между рендерами. Вы точно использовали key при работе со списками.
Даже если из списка удалить какой-нибудь элемент, то остальные останутся нетронутыми. React сверит их ключи до и после изменения. И если ключи равны, то элемент не будет создан заново.
Более наглядный пример. В коде ниже у компоненты Content произойдет размонтирование и после монтирование. Так как при изменении checked изменился key.
Но при этом, если бы key остался одинаковым между рендерами, то никакого размонтирования не произошло. Просто случилось бы обновление компонента Content.
3️⃣ изменился атрибут узла ==> обновляется только измененный атрибут, создание нового дерева не происходит
Допустим у нас есть следующий JSX.
Как мы видим, у div меняется только атрибут data-testid в зависимости от состояния isVisible. В таком случае создание нового дерева и размонтирование всех дочерних узлов div НЕ произойдет. Потому что изменение атрибута не вызывает создание нового дерева.
P.S. Ставь 🔥, если было полезно! Больше про advanced-темы в React можно найти в закрепленном посте.
В продолжение к посту как работает React и его алгоритм Reconciliation хочется рассказать про допущения этого алгоритма. Допущения алгоритма Reconciliation - это те условия, которые нам как разработчикам необходимо выполнять, чтобы React рендерил компоненты оптимизировано и без пролагивания UI.
Для начала давайте поймем, какая сложность алгоритма сравнения деревьев (Virtual DOM) у React? Алгоритм React это делает за O(N), т.е. сложность линейная. В то время как браузерное API сравнивает DOM-деревья за O(N^3).
И чтобы React продолжал выполнять свою работу за O(N), необходимо знать следующее.
1️⃣ изменился тип узла ==> происходит создание нового дерева.
Допустим, у нас было такое дерево,
<main>
<h1>Users</h1>
<UsersList />
</main>
а после обновления стало таким.
<section>
<h1>Users</h1>
<UsersList />
</section>
Как мы видим, тег main изменился на section. При этом контент внутри этого элемента остался без изменений. Но React удалит и заново создаст все узлы, которые начинаются с main и идут глубже, так как у узла изменился type. Был type: 'main', а стал type: 'section'.
2️⃣ изменился key узла ==> происходит создание нового дерева.
key - полезный инструмент в React. Он позволяет оставлять нетронутыми конкретные узлы между рендерами. Вы точно использовали key при работе со списками.
<div>
{users.map((user) => (
<UserItem key={user.id} id={user.id} name={user.name} />
))}
</div>
Даже если из списка удалить какой-нибудь элемент, то остальные останутся нетронутыми. React сверит их ключи до и после изменения. И если ключи равны, то элемент не будет создан заново.
Более наглядный пример. В коде ниже у компоненты Content произойдет размонтирование и после монтирование. Так как при изменении checked изменился key.
<div className="App">
{checked ? <Content key="1" /> : <Content key="2" />}
</div>
Но при этом, если бы key остался одинаковым между рендерами, то никакого размонтирования не произошло. Просто случилось бы обновление компонента Content.
3️⃣ изменился атрибут узла ==> обновляется только измененный атрибут, создание нового дерева не происходит
Допустим у нас есть следующий JSX.
<div data-testid={isVisible ? 'content-visible' ? 'content-hidden'}>
<UsersList />
</div>
Как мы видим, у div меняется только атрибут data-testid в зависимости от состояния isVisible. В таком случае создание нового дерева и размонтирование всех дочерних узлов div НЕ произойдет. Потому что изменение атрибута не вызывает создание нового дерева.
P.S. Ставь 🔥, если было полезно! Больше про advanced-темы в React можно найти в закрепленном посте.
🔥17❤1
Недавно помог Front-End разработчику пройти алгоритмическую секцию в Яндекс на Middle позицию 🔥
В своем приватном Telegram-сообществе я выкладываю видео-разборы live coding задач с реальных собеседований.
Вот человек посмотрел разбор, понял как решается одна из задач. В итоге, он без проблем решил похожую задачу на алгоритмической секции. Круто, не правда ли? 💪
Вы тоже можете получить доступ к видео-разборам и вдобавок гайд "Как получить оффер в ЯНДЕКС на 300k+ RUB на позицию Front-End разработчика?". Все это найдете в приватном сообществе 👇
@easy_jobinterivew_frontend_bot
В своем приватном Telegram-сообществе я выкладываю видео-разборы live coding задач с реальных собеседований.
Вот человек посмотрел разбор, понял как решается одна из задач. В итоге, он без проблем решил похожую задачу на алгоритмической секции. Круто, не правда ли? 💪
Вы тоже можете получить доступ к видео-разборам и вдобавок гайд "Как получить оффер в ЯНДЕКС на 300k+ RUB на позицию Front-End разработчика?". Все это найдете в приватном сообществе 👇
@easy_jobinterivew_frontend_bot
👍8❤3🔥2🤡1
————————————
В канале "Один день айтишника" выложили мою историю увольнения за то, что я не был онлайн в Slack 🟢
Благодаря этому на мой Telegram-канал за 2-е суток подписались больше 100 человек. Спасибо вам! 🔥
Кто еще не читал историю про мое увольнение, то вот ссылка: https://t.me/one_IT_day/443
В канале "Один день айтишника" выложили мою историю увольнения за то, что я не был онлайн в Slack 🟢
Благодаря этому на мой Telegram-канал за 2-е суток подписались больше 100 человек. Спасибо вам! 🔥
Кто еще не читал историю про мое увольнение, то вот ссылка: https://t.me/one_IT_day/443
🔥12❤1👍1
👍9❤3🔥2🆒2
Хочу вам рассказать про мой недавний опыт общения с рекрутером на звонке-знакомстве. Иногда рекрутеры могут задавать технические вопросы, чтобы проверить, что вы действительно программист и вас можно допустить к собеседованию.
- Максим, расскажите, как предотвратить лишние ререндеры у компоненты в React.
- Можно использовать PureComponent или shouldComponentUpdate у классовых компонент, у функциональных - memo.
- Подождите-подождите, Component что?
- Пью-эр ком-по-нент.
- Ага, поняла, записала. Давайте дальше. Максим, расскажите, что такое Promise в JavaScript и как он работает?
- Promise это специальное API в JS, которое используется для обработки асинхронного кода. Промисы применяются...
- Подождите, Максим, я не успеваю записывать, давайте еще раз.
- (диктую) Promise это. Специальное API в JS. Которое используется...
- Ага-ага, записываю. Максим, у меня еще 9 вопросов. Продолжаем?
- 🤯
Я не представляю, как компания хочет найти нормального разработчика с таким споcобом найма. Мы по факту в этом звонке играли в "cломанный телефон". Я боюсь представить, что рекрутер записала с моих слов. Она ведь не разбирается ни в React, ни в JavaScript.
Очень странно задавать на звонке-знакомстве вопросы, на которые нужно дать развернутый ответ. Я понимаю, если бы рекрутер задавала вопросы, на которые можно ответить 1-3 словами. В таком случае и я не потрачу много времени, и рекрутер не будет напрягаться и записывать все мои слова под диктовку.
Пример хорошего вопроса: "Как остановить всплытие события в браузере?". Ответ довольно простой: event.stopPropagation.
А вы что думаете насчет технических вопросов от рекрутера? Были у вас похожие случаи? 🙂
- Максим, расскажите, как предотвратить лишние ререндеры у компоненты в React.
- Можно использовать PureComponent или shouldComponentUpdate у классовых компонент, у функциональных - memo.
- Подождите-подождите, Component что?
- Пью-эр ком-по-нент.
- Ага, поняла, записала. Давайте дальше. Максим, расскажите, что такое Promise в JavaScript и как он работает?
- Promise это специальное API в JS, которое используется для обработки асинхронного кода. Промисы применяются...
- Подождите, Максим, я не успеваю записывать, давайте еще раз.
- (диктую) Promise это. Специальное API в JS. Которое используется...
- Ага-ага, записываю. Максим, у меня еще 9 вопросов. Продолжаем?
- 🤯
Я не представляю, как компания хочет найти нормального разработчика с таким споcобом найма. Мы по факту в этом звонке играли в "cломанный телефон". Я боюсь представить, что рекрутер записала с моих слов. Она ведь не разбирается ни в React, ни в JavaScript.
Очень странно задавать на звонке-знакомстве вопросы, на которые нужно дать развернутый ответ. Я понимаю, если бы рекрутер задавала вопросы, на которые можно ответить 1-3 словами. В таком случае и я не потрачу много времени, и рекрутер не будет напрягаться и записывать все мои слова под диктовку.
Пример хорошего вопроса: "Как остановить всплытие события в браузере?". Ответ довольно простой: event.stopPropagation.
А вы что думаете насчет технических вопросов от рекрутера? Были у вас похожие случаи? 🙂
👍16❤3😁1🤔1💯1
Расскажи, что такое Reflow и Repaint?
Вопрос на собеседовании довольно частый. И обычно на него хотят услышать краткий ответ. Давайте отвечать!
Repaint (перерисовка). Происходит при изменении стилей, которые никак не влияют на положение элемента на странице и его размеры. Допустим, мы поменяли цвет какого-нибудь блока. Перерисовка произойдет при обновлении значений у свойств background-color, border-color, visibility и других.
Reflow (перекомпоновка). Происходит при изменении положения элемента на странице и (или) изменении его размеров. Например, размеры элемента мы можем поменять через width, height, padding, border и др. Позицию элемента на странице изменяют свойства left, right, top, bottom, margin, display и тд.
Кроме этого, Reflow могут вызывать и манипуляции с DOM (добавление, удаление, изменение узлов), и ресайз окна браузера, и получение позиции скрола, и изменение содержимого HTML-элемента (например, добавить больше текста в div).
И еще один важный момент: обычно после каждого Reflow происходит Repaint, так как браузеру необходимо снова перерисовать измененный контент.
Как избежать лишних Reflow и Repaint?
Один из способов - использовать для анимаций CSS-свойства transform и opacity, чтобы делегировать обработку стилей на GPU (графический процессор) и вынести анимацию на отдельный слой.
Подробнее про это читайте в моей статье "Как работают браузеры?"
Вопрос на собеседовании довольно частый. И обычно на него хотят услышать краткий ответ. Давайте отвечать!
Repaint (перерисовка). Происходит при изменении стилей, которые никак не влияют на положение элемента на странице и его размеры. Допустим, мы поменяли цвет какого-нибудь блока. Перерисовка произойдет при обновлении значений у свойств background-color, border-color, visibility и других.
Reflow (перекомпоновка). Происходит при изменении положения элемента на странице и (или) изменении его размеров. Например, размеры элемента мы можем поменять через width, height, padding, border и др. Позицию элемента на странице изменяют свойства left, right, top, bottom, margin, display и тд.
Кроме этого, Reflow могут вызывать и манипуляции с DOM (добавление, удаление, изменение узлов), и ресайз окна браузера, и получение позиции скрола, и изменение содержимого HTML-элемента (например, добавить больше текста в div).
И еще один важный момент: обычно после каждого Reflow происходит Repaint, так как браузеру необходимо снова перерисовать измененный контент.
Как избежать лишних Reflow и Repaint?
Один из способов - использовать для анимаций CSS-свойства transform и opacity, чтобы делегировать обработку стилей на GPU (графический процессор) и вынести анимацию на отдельный слой.
Подробнее про это читайте в моей статье "Как работают браузеры?"
🔥13❤4
На моем YouTube недавно вышло 3 видео с разбором решения задач в Бигтехи (Яндекс, Т-Банк, Авито и другие).
Кто еще не смотрел, ловите ссылки ниже 😉
1️⃣ Реализовать функцию reduceString (2 способа решения: через цикл и reg exp)
Темы: работа со строками, регулярные выражения, суррогатные пары (пример: "😊").
🔗 Ссылка
2️⃣ Написать аналог Array.flat (2 способа решения: через рекурсию и через стэк в качестве массива)
Темы: рекурсия, замена рекурсии на кастомный стэк.
Примечание: часто на собеседованиях дают условие "реши задачу без использования рекурсии". Поэтому стоит уметь решать задачи разными способами. Решение Array.flat без рекурсии по ссылке ниже.
🔗 Ссылка
3️⃣ Поиск числа в матрице. Search a 2D Matrix
Темы: бинарный поиск, поиск в матрице.
Данная задача есть на LeetCode. Уровень Medium.
🔗 Ссылка
P.S. Не забываем, что на моем YouTube есть отдельный плейлист для Front-End разработчиков по подготовке к решению задач на собеседованиях 😉. Его можно найти здесь.
Кто еще не смотрел, ловите ссылки ниже 😉
1️⃣ Реализовать функцию reduceString (2 способа решения: через цикл и reg exp)
Темы: работа со строками, регулярные выражения, суррогатные пары (пример: "😊").
console.log(reduceStr('AAABBBBBBB😊😊😊😊FFFFFFFMMHELLO123333GGGK'))
//Должно получиться: A3B7😊4F7M2HEL2O1234G3K
🔗 Ссылка
2️⃣ Написать аналог Array.flat (2 способа решения: через рекурсию и через стэк в качестве массива)
Темы: рекурсия, замена рекурсии на кастомный стэк.
console.log(flat([
[[1, [100, [5]]], 2],
[[1]],
2
])); // [1, 100, 5, 2, 1, 2]
console.log(
flat([[1, 2], [[1]], 2])
); // [1, 2, 1, 2]
Примечание: часто на собеседованиях дают условие "реши задачу без использования рекурсии". Поэтому стоит уметь решать задачи разными способами. Решение Array.flat без рекурсии по ссылке ниже.
🔗 Ссылка
3️⃣ Поиск числа в матрице. Search a 2D Matrix
Темы: бинарный поиск, поиск в матрице.
console.log(searchMatrix([[1,3,5,7],[10,11,16,20],[23,30,34,60]], 3)) // true
Данная задача есть на LeetCode. Уровень Medium.
🔗 Ссылка
P.S. Не забываем, что на моем YouTube есть отдельный плейлист для Front-End разработчиков по подготовке к решению задач на собеседованиях 😉. Его можно найти здесь.
❤13🔥5
Помните я вам рассказывал, как рекрутер не успевала записывать мои ответы на технические вопросы под диктовку и перебивала меня, громко клацая по клавиатуре?
Так вот, у меня недавно был абсолютно противоположный опыт на HR скрининге в другую IT-компанию. После 20 минут разговора на звонке с рекрутером мне предложили ответить на 5 технических вопросов по JavaScript. Я конечно же нехотя согласился, ожидая снова клацанья клавиатуры и фраз "подожди, я не успеваю записывать".
Но нет, все было по-другому! 😱 Рекрутер задавала технические вопросы, которые подразумевают короткий ответ. Я спокойно отвечал, никакого клацанья клавиатуры не слышал.
После того как ответил на все вопросы, я спросил у рекрутера: "Ты вообще записывала мои ответы? Я не слышал клацанья клавиатуры, и ты ни разу не просила ничего повторить". Мне же ответили: "Да, я все записывала, но на телефон, так быстрее и удобнее. Да и меня тоже бесит, когда я говорю и параллельно слышу звуки клавиатуры от собеседника".
Я был приятно удивлен, что бывают такие понимающие и ответственные рекрутеры. Не часто таких встретишь. В общем и целом, HR скрининг прошел идеально!
Так вот, у меня недавно был абсолютно противоположный опыт на HR скрининге в другую IT-компанию. После 20 минут разговора на звонке с рекрутером мне предложили ответить на 5 технических вопросов по JavaScript. Я конечно же нехотя согласился, ожидая снова клацанья клавиатуры и фраз "подожди, я не успеваю записывать".
Но нет, все было по-другому! 😱 Рекрутер задавала технические вопросы, которые подразумевают короткий ответ. Я спокойно отвечал, никакого клацанья клавиатуры не слышал.
После того как ответил на все вопросы, я спросил у рекрутера: "Ты вообще записывала мои ответы? Я не слышал клацанья клавиатуры, и ты ни разу не просила ничего повторить". Мне же ответили: "Да, я все записывала, но на телефон, так быстрее и удобнее. Да и меня тоже бесит, когда я говорю и параллельно слышу звуки клавиатуры от собеседника".
Я был приятно удивлен, что бывают такие понимающие и ответственные рекрутеры. Не часто таких встретишь. В общем и целом, HR скрининг прошел идеально!
❤16🔥5
Недавно мне предложили 10_000 RUB за то, чтобы я другому человеку на алгоритмической секции в Т-Банк надиктовывал в наушник решение live-coding задач. Неплохой доход за 1 час "работы", не правда ли?
Как вы думаете, пополнился ли мой банковский счет на 10k? КонечноНЕТ !
Я всегда топлю за то, чтобы проходить собеседование своими силами. Я могу понять, что к алгоритмам нужно готовиться минимум 1 неделю, что такие задачи редко встречаются в работе Front-End разработчика. Тем не менее, это правила "игры". Многие бигтехи (да и другие айти компании) дают алгоритмические задачи на собеседованиях. Это просто нужно принять!
Да и на самом деле, алгоритмы не так уж и бесполезны во Front-End разработке. При решении багов и верстке формы их навряд ли применишь. Но в работе раз в 3 месяца встречаются задачи, где нужно проявить свою экспертизу на максимум и использовать знания алгоритмов для оптимального решения задачи.
При том базовые навыки решения алгоритмических задач помогут вам писать код с разумной сложностью алгоритма (big O). Навряд ли вашему коллеге во время код-ревью понравится, что вы написали функцию за O(N^2), когда это можно было сделать за O(N) либо O(logN).
Кстати, как раз в моем приватном ТГ канале вышел разбор всех 3-х этапов собеседований в Т-Банк! Там я вам конечно в наушник ничего не надиктую, но зато помогу самим подготовиться к собеседованиям и выйти на оффер!
Вы получите 3 видео (секции по JavaScript, алгоритмам и архитектуре) с записями моих собеседований в Т-Банк на ЗП 3000$. А также 5 видео-разборов live-coding задач на JavaScript и алгоритмы, которые мне давали в Т-Банк. Забирай все это в моем приватном ТГ канале 😉👇
@easy_jobinterivew_frontend_bot
Как вы думаете, пополнился ли мой банковский счет на 10k? Конечно
Я всегда топлю за то, чтобы проходить собеседование своими силами. Я могу понять, что к алгоритмам нужно готовиться минимум 1 неделю, что такие задачи редко встречаются в работе Front-End разработчика. Тем не менее, это правила "игры". Многие бигтехи (да и другие айти компании) дают алгоритмические задачи на собеседованиях. Это просто нужно принять!
Да и на самом деле, алгоритмы не так уж и бесполезны во Front-End разработке. При решении багов и верстке формы их навряд ли применишь. Но в работе раз в 3 месяца встречаются задачи, где нужно проявить свою экспертизу на максимум и использовать знания алгоритмов для оптимального решения задачи.
При том базовые навыки решения алгоритмических задач помогут вам писать код с разумной сложностью алгоритма (big O). Навряд ли вашему коллеге во время код-ревью понравится, что вы написали функцию за O(N^2), когда это можно было сделать за O(N) либо O(logN).
Кстати, как раз в моем приватном ТГ канале вышел разбор всех 3-х этапов собеседований в Т-Банк! Там я вам конечно в наушник ничего не надиктую, но зато помогу самим подготовиться к собеседованиям и выйти на оффер!
Вы получите 3 видео (секции по JavaScript, алгоритмам и архитектуре) с записями моих собеседований в Т-Банк на ЗП 3000$. А также 5 видео-разборов live-coding задач на JavaScript и алгоритмы, которые мне давали в Т-Банк. Забирай все это в моем приватном ТГ канале 😉👇
@easy_jobinterivew_frontend_bot
❤6👍5🔥1
⚛️ Хочу вам рассказать про рендеринг в React через реальную ситуацию с работы. Я отдал задачу на ревью, где был следующий код:
Мой коллега оставил приблизительно такой комментарий в Merge Request:
Что ж, давайте разбираться, в каком месте рендерить модалку?
Во-первых, React действительно смотрит на позицию компонент в JSX. Если у нас было <A/><B/>, а стало <B/><A/>, то React посмотрит, что первым стоял компонент A, а после обновления первым идет B. Тогда произойдет unmount у A и B.
Вернемся к нашему примеру. Кажется, что если isVisible будет false, то результат станет таким:
Но на самом деле он будет таким:
То есть вместо Modal первым в JSX будет стоять false, а значит позиция у Content и Table не поменяется. При этом false не будет отрендерен в реальный DOM, так как React обрабатывает такие моменты.
Давайте посмотрим пример, когда при открытии/закрытии Modal будет происходить unmount у Content и Table.
Как видите, теперь при условии isVisible === true первым в JSX стоит Modal, а при false первым идет Content. React сравнивает типы Modal и Content и понимает, что они не совпадают. Та же самая проверка будет и для Table: до обновлений на 2-й позиции стоял Content, а теперь Table. Поэтому React вызовет unmount у Modal, Content, Table и заново отрендерит Content и Table.
ИТОГО: не важно, где рендерить компоненту Modal. Главное - позиция компонент в JSX.
P.S. Сделал для вас CodeSandbox, где можно проверить примеры из этого поста 😉
{isVisbile && <Modal />}
<Content />
<Table />
Мой коллега оставил приблизительно такой комментарий в Merge Request:
Лучше поместить модалку в конец JSX, а не на самый вверх. Так React будет рендерить компоненты оптимальнее. В твоем случае при открытии/закрытии модалки произойдет unmount у Content и Table, так как первым в JSX стоял Modal, а после закрытия модалки стал Content.
Что ж, давайте разбираться, в каком месте рендерить модалку?
Во-первых, React действительно смотрит на позицию компонент в JSX. Если у нас было <A/><B/>, а стало <B/><A/>, то React посмотрит, что первым стоял компонент A, а после обновления первым идет B. Тогда произойдет unmount у A и B.
Вернемся к нашему примеру. Кажется, что если isVisible будет false, то результат станет таким:
<Content />
<Table />
Но на самом деле он будет таким:
{false}
<Content />
<Table />
То есть вместо Modal первым в JSX будет стоять false, а значит позиция у Content и Table не поменяется. При этом false не будет отрендерен в реальный DOM, так как React обрабатывает такие моменты.
Давайте посмотрим пример, когда при открытии/закрытии Modal будет происходить unmount у Content и Table.
{isVisible ? (
<>
<Modal />
<Content />
<Table />
</>
) : (
<>
<Content />
<Table />
</>
)}
Как видите, теперь при условии isVisible === true первым в JSX стоит Modal, а при false первым идет Content. React сравнивает типы Modal и Content и понимает, что они не совпадают. Та же самая проверка будет и для Table: до обновлений на 2-й позиции стоял Content, а теперь Table. Поэтому React вызовет unmount у Modal, Content, Table и заново отрендерит Content и Table.
ИТОГО: не важно, где рендерить компоненту Modal. Главное - позиция компонент в JSX.
P.S. Сделал для вас CodeSandbox, где можно проверить примеры из этого поста 😉
🔥14👍4❤2
Смешная история про общение с рекрутером
Мне написала рекрутер и предложила пройти собеседование в одну компанию. Главным преимуществом вакансии была 4-дневная рабочая неделя. То есть у всех выходные с пятницы по воскресенье.
Я долго думал, в чем же может быть подвох? 4-дневная рабочая неделя не частная практика. В итоге, скинул CV и мне обещали ответить до конца недели.
В ПЯТНИЦУ мне рекрутер пишет примерно следующее сообщение:
Как думаете, это совпадение, что именно сейчас им пришлось в пятницу поработать? Или у них на регулярной основе переработки в 4-х дневную рабочую неделю? 🙂
Мне написала рекрутер и предложила пройти собеседование в одну компанию. Главным преимуществом вакансии была 4-дневная рабочая неделя. То есть у всех выходные с пятницы по воскресенье.
Я долго думал, в чем же может быть подвох? 4-дневная рабочая неделя не частная практика. В итоге, скинул CV и мне обещали ответить до конца недели.
В ПЯТНИЦУ мне рекрутер пишет примерно следующее сообщение:
Максим, добрый день!
Пишу вам, чтобы извиниться и взять паузу до понедельника. У лида на этой неделе крайне высокая занятость. Он надеялся, что найдет время сегодня (тот редкий случай, когда пришлось поработать в пятницу), но я вижу, что вряд ли он ответит уже.
Я надеюсь на понимание 🙏 И также надеюсь, что в пн уже смогу точно вернуться с ответом.
Как думаете, это совпадение, что именно сейчас им пришлось в пятницу поработать? Или у них на регулярной основе переработки в 4-х дневную рабочую неделю? 🙂
😁29🤣5👍1
Какие алгоритмические задачи с собеседований я применял на коммерческих проектах?
Вас тоже бесит, когда на собеседованиях дают live-coding задачи, которые не используются в коммерческой разработке?
Я помню, как интервьюер дал мне задачу написать кастомный Stack с нуля, уверяя, что у них такая реализация используется в production коде. Когда я принял оффер, устроился в компанию и получил доступ к коду, то увидел... Что никакой кастомной реализации стека там нет.
Но на самом деле, многие задачи на алгоритмы применяются в коммерческой разработке. Расскажу вам про кейсы с моих мест работы.
1️⃣ mergeIntervals
Разбор решения задачи есть на моем YouTube.
Практический кейс был такой. Необходимо нарисовать график на основе данных с бэка. Бэк присылает массив объектов с параметрами dateStart, dateEnd и др. По оси Y идет цена в RUB, по оси X - интервалы дат.
Дело в том, что некоторые интервалы с датами пересекались. Не было смысла отображать отдельный столбец в графике, когда можно было схлопнуть несколько интервалов в один.
2️⃣ findClosestNumber (поиск ближайшего числа в массиве)
Разбор решения задачи есть в моем приватном ТГ канале.
Проект связан с оформлением кредитов онлайн. В интерфейсе приложения есть слайдер с инпутом для ввода суммы кредита. Кредит можно взять на конкретные суммы (например, 10_000, 30_000, 50_000, 100_000, 200_000 и тд). То есть если пользователь вводит в инпут число 47500, то его нужно заменить на ближайшее значение (в данном примере 50_000).
3️⃣ Observer / EventEmitter
Разбор реализации EventEmitter есть в моем приватном ТГ канале.
Проект все тот же, оформление кредитов онлайн. На клиенте есть форма из 20+ полей, где пользователю нужно заполнить персональные данные для регистрации кредита. У некоторых полей есть рядом блок с процентами (например, 5%, 8%, 20%). И вверху страницы расположена шкала от 0 до 100%. Если правильно заполняешь конкретное поле, то прибавляется процент к общей шкале.
Такой UI/UX мотивирует пользователя заполнять форму из большого количества полей.
Первое решение, которое приходит в голову, использовать useContext из React. Но его главный недостаток - лишние ререндеры. Зачем нам ререндерить всю форму при обновлении процентов шкалы, если можно воспользоваться паттернами EventEmitter или Observer?
В данном примере я применил Observer. Я повесил слушатель observer.subscribe в компоненте со шкалой процентов. А в каждом инпуте при валидном вводе значения делал observer.emit. Таким образом, ререндерилась только компонента со шкалой процентов.
----------
Конечно, примеров использования алгоритмических задач в моей практике было намного больше. Накидайте реакций, если вам понравился пост, сделаю еще одну часть.
А вы какие алгоритмические задачи с собеседований используете в коммерческой разработке? 🤔
Вас тоже бесит, когда на собеседованиях дают live-coding задачи, которые не используются в коммерческой разработке?
Я помню, как интервьюер дал мне задачу написать кастомный Stack с нуля, уверяя, что у них такая реализация используется в production коде. Когда я принял оффер, устроился в компанию и получил доступ к коду, то увидел... Что никакой кастомной реализации стека там нет.
Но на самом деле, многие задачи на алгоритмы применяются в коммерческой разработке. Расскажу вам про кейсы с моих мест работы.
1️⃣ mergeIntervals
Разбор решения задачи есть на моем YouTube.
Практический кейс был такой. Необходимо нарисовать график на основе данных с бэка. Бэк присылает массив объектов с параметрами dateStart, dateEnd и др. По оси Y идет цена в RUB, по оси X - интервалы дат.
Дело в том, что некоторые интервалы с датами пересекались. Не было смысла отображать отдельный столбец в графике, когда можно было схлопнуть несколько интервалов в один.
2️⃣ findClosestNumber (поиск ближайшего числа в массиве)
Разбор решения задачи есть в моем приватном ТГ канале.
Проект связан с оформлением кредитов онлайн. В интерфейсе приложения есть слайдер с инпутом для ввода суммы кредита. Кредит можно взять на конкретные суммы (например, 10_000, 30_000, 50_000, 100_000, 200_000 и тд). То есть если пользователь вводит в инпут число 47500, то его нужно заменить на ближайшее значение (в данном примере 50_000).
3️⃣ Observer / EventEmitter
Разбор реализации EventEmitter есть в моем приватном ТГ канале.
Проект все тот же, оформление кредитов онлайн. На клиенте есть форма из 20+ полей, где пользователю нужно заполнить персональные данные для регистрации кредита. У некоторых полей есть рядом блок с процентами (например, 5%, 8%, 20%). И вверху страницы расположена шкала от 0 до 100%. Если правильно заполняешь конкретное поле, то прибавляется процент к общей шкале.
Такой UI/UX мотивирует пользователя заполнять форму из большого количества полей.
Первое решение, которое приходит в голову, использовать useContext из React. Но его главный недостаток - лишние ререндеры. Зачем нам ререндерить всю форму при обновлении процентов шкалы, если можно воспользоваться паттернами EventEmitter или Observer?
В данном примере я применил Observer. Я повесил слушатель observer.subscribe в компоненте со шкалой процентов. А в каждом инпуте при валидном вводе значения делал observer.emit. Таким образом, ререндерилась только компонента со шкалой процентов.
----------
Конечно, примеров использования алгоритмических задач в моей практике было намного больше. Накидайте реакций, если вам понравился пост, сделаю еще одну часть.
А вы какие алгоритмические задачи с собеседований используете в коммерческой разработке? 🤔
🔥14❤5
Как найти все ред-флаги компании в процессе собеседования? 🚩🚩🚩
Многие разработчики в конце собеседования задают единственный вопрос вроде "ну даже не знаю, что спросить, про процессы в команде расскажи?". И после этого следует "да в принципе все понятно, вопросов больше нет".
Меня всегда удивляет, насколько разработчики инфантильно подходят к вопросам про компанию. Ведь тебе в этой компании работать! А вдруг что-то не понравится после трудоустройства? Лучше выявить все ред-флаги заранее.
В этом посте мы разберем топ вопросов, которые стоит задать интервьюеру в конце собеседования.
1️⃣ Какие сроки фидбека?
Интервьюер чаще всего сам принимает финальное решение и знает, сколько времени ему нужно на оценку. Многие этим вопросом пренебрегают, а потом жалуются "почему мне так долго не отвечают?".
2️⃣ Какого размера команда и кто в нее входит? (front-end, back-end, QA, дизайнеры и т.д.)
Чем больше юнитов, тем лучше в команде распределены роли. Допустим, нет тестировщика — будут баги на проде. Или нет дизайнера — будет много задач без макетов и куча правок от PM-а.
3️⃣ Какие процессы в команде? Расскажи, как задача проходит путь от тикета до прода.
Данный вопрос позволит понять, как будет проходить типичный спринт. Как по мне, идеальный процесс примерно такой.
На момент постановки задачи уже есть макеты и четкое описание бизнес-логики. После выполнения, задача проходит обязательный код-ревью. Далее тестировщики проверяют задачу как вручную, так и при помощи автотестов. В финальной стадии ответственный за релиз коллега подготавливает задачу к выкатке на прод.
4️⃣ Какие есть созвоны в течение спринта? В какое время проходят эти созвоны?
Большинство почему-то боятся задавать этот вопрос. Хотя он чрезвычайно важный. Представьте, что у команды дейлики начинаются в 8 утра, а вы привыкли вставать в 9-10. Будет уже поздно узнать это в процессе онбординга.
Важно, чтобы не было бесполезных звонков. Большинство вопросов по задачам решаются максимум за 1 час созвонов в день.
5️⃣ Как часто происходят релизы и кто за них отвечает? Есть ли дежурства?
Лично мне нравится, когда я как front-end разработчик не участвую в процессе релиза. То есть мне не нужно самому лезть в Jenkins, подготавливать релизную ветку, фиксать конфликты, раскатывать на прод несколько окружений. Круто, когда этим занимается отдельный человек или юнит. Например, процессом релиза могут заниматься QA.
6️⃣ Пишут ли front-end разработчики тесты?
Часто замечаю, как в командах пишут тесты ради тестов. И выхлоп от этого минимальный. Всегда круто, когда под автоматическое тестирование выделены специальные люди (QA, например).
7️⃣ Как у вас с переработками? Случается ли, что после 18:00 в будний день или в выходной приходит коллега и пишет "у нас пожар, срочно подключайся, нужно фиксать багу"?
Думаю, комментарии здесь излишни. Переработки — это ненормально. Работа вне графика по требованию PM-а или тимлида — тревожный знак.
---------
А какие вопросы вы задаёте на собеседованиях? Делитесь в комментариях 👇
Многие разработчики в конце собеседования задают единственный вопрос вроде "ну даже не знаю, что спросить, про процессы в команде расскажи?". И после этого следует "да в принципе все понятно, вопросов больше нет".
Меня всегда удивляет, насколько разработчики инфантильно подходят к вопросам про компанию. Ведь тебе в этой компании работать! А вдруг что-то не понравится после трудоустройства? Лучше выявить все ред-флаги заранее.
В этом посте мы разберем топ вопросов, которые стоит задать интервьюеру в конце собеседования.
1️⃣ Какие сроки фидбека?
Интервьюер чаще всего сам принимает финальное решение и знает, сколько времени ему нужно на оценку. Многие этим вопросом пренебрегают, а потом жалуются "почему мне так долго не отвечают?".
2️⃣ Какого размера команда и кто в нее входит? (front-end, back-end, QA, дизайнеры и т.д.)
Чем больше юнитов, тем лучше в команде распределены роли. Допустим, нет тестировщика — будут баги на проде. Или нет дизайнера — будет много задач без макетов и куча правок от PM-а.
3️⃣ Какие процессы в команде? Расскажи, как задача проходит путь от тикета до прода.
Данный вопрос позволит понять, как будет проходить типичный спринт. Как по мне, идеальный процесс примерно такой.
На момент постановки задачи уже есть макеты и четкое описание бизнес-логики. После выполнения, задача проходит обязательный код-ревью. Далее тестировщики проверяют задачу как вручную, так и при помощи автотестов. В финальной стадии ответственный за релиз коллега подготавливает задачу к выкатке на прод.
4️⃣ Какие есть созвоны в течение спринта? В какое время проходят эти созвоны?
Большинство почему-то боятся задавать этот вопрос. Хотя он чрезвычайно важный. Представьте, что у команды дейлики начинаются в 8 утра, а вы привыкли вставать в 9-10. Будет уже поздно узнать это в процессе онбординга.
Важно, чтобы не было бесполезных звонков. Большинство вопросов по задачам решаются максимум за 1 час созвонов в день.
5️⃣ Как часто происходят релизы и кто за них отвечает? Есть ли дежурства?
Лично мне нравится, когда я как front-end разработчик не участвую в процессе релиза. То есть мне не нужно самому лезть в Jenkins, подготавливать релизную ветку, фиксать конфликты, раскатывать на прод несколько окружений. Круто, когда этим занимается отдельный человек или юнит. Например, процессом релиза могут заниматься QA.
6️⃣ Пишут ли front-end разработчики тесты?
Часто замечаю, как в командах пишут тесты ради тестов. И выхлоп от этого минимальный. Всегда круто, когда под автоматическое тестирование выделены специальные люди (QA, например).
7️⃣ Как у вас с переработками? Случается ли, что после 18:00 в будний день или в выходной приходит коллега и пишет "у нас пожар, срочно подключайся, нужно фиксать багу"?
Думаю, комментарии здесь излишни. Переработки — это ненормально. Работа вне графика по требованию PM-а или тимлида — тревожный знак.
---------
А какие вопросы вы задаёте на собеседованиях? Делитесь в комментариях 👇
👍7❤5💯3⚡1