JavaScript: senior
JavaScript — это основной язык программирования для веб-разработки, позволяющий создавать интерактивные и высокопроизводительные приложения. В этой статье мы рассмотрим ключевые концепции и методы оптимизации, такие как tree shaking, Server-Side Rendering (SSR), виртуальный скроллинг, добавление элементов в DOM, а также управление асинхронными операциями. Эти знания помогут улучшить производительность приложений, оптимизировать работу с API и обеспечить плавную отрисовку контента в Single Page Applications (SPA).
👉🏻Навигация по всем материалам в Telegram
Вопрос 1
Какой из массивов НЕ будет выведен после выполнения этого кода?
["Мандарин", "Крыжовник", "Агрус"]["Мандарин", "Клубника", "Смородина"]["Мандарин", "Апельсин", "Слива"]["Яблоко", "Апельсин", "Слива"]- Будут выведены все массивы.
- Код создает две переменные
fruitsиfruits1. Обе изменяются в различных контекстах (глобальном и локальном). - В глобальной области
fruitsменяется на["Мандарин", "Апельсин", "Слива"]. - Функция
test()локально переопределяет обе переменные: const fruits1становится["Малина", "Клубника", "Смородина"], но не влияет на глобальныйfruits1.let fruitsстановится["Груша", "Крыжовник", "Агрус"]и изменяется на["Мандарин", "Крыжовник", "Агрус"].- После выполнения функции
test():
📌Правильный ответ:
2. ["Мандарин", "Клубника", "Смородина"]
Этот массив существует только в локальном контексте функции test() и нигде не выводится.
Вопрос 2
Перед вами поставили задачу написать веб-приложение «Крестики-нолики». По техническому заданию вам необходимо сохранять состояние игры. Например, вы можете сохранить текущего игрока, расположение крестиков и ноликов на игровом поле и другие данные, чтобы игра могла быть возобновлена после перезагрузки страницы. Какой тип хранилища вы подберете для данной задачи?
- Куки (cookies):
- Подходят для передачи данных между сервером и клиентом.
- Однако их использование для сохранения состояния игры неэффективно, так как они имеют ограничение по объему (4 КБ) и требуют отправки данных при каждом HTTP-запросе.
- LocalStorage:
- LocalStorage хранит данные на стороне клиента, доступно для одной и той же страницы, даже после перезагрузки.
- Объем данных (около 5 МБ) подходит для хранения состояния игры.
- Поддерживает быстрое сохранение и извлечение данных, что делает его идеальным для задач, подобных этой.
- Cache API:
- Используется для кеширования ресурсов (например, HTML, CSS, JS) и редко применяется для хранения структурированных данных.
- Не подходит для задачи сохранения текущего состояния игры.
- Web SQL Database:
- sessionStorage:
📌Правильный ответ:
2. LocalStorage
LocalStorage наиболее подходит для хранения состояния игры, так как позволяет сохранять данные между сессиями и имеет достаточный объем памяти.
Вопрос 3
Каким будет результат выполнения следующего выражения в JavaScript?
- Операции в JavaScript выполняются слева направо, но порядок вычислений определяется приоритетом операторов.
- Умножение (
*) имеет более высокий приоритет, чем сложение (+). - Затем происходит операция сложения:
Вопрос 4
Из массива чисел вам нужно сначала отфильтровать четные числа, затем удвоить их и, наконец, вычислить сумму. Какой код это выполняет?
numbers.map(n => n * 2).filter(n => n % 2 === 0).reduce((sum, n) => sum + n)numbers.reduce((sum, n) => sum + n * 2, 0).filter(n => n % 2 === 0)numbers.reduce((sum, n) => sum + n).map(n => n * 2).filter(n => n % 2 === 0)numbers.filter(n => n % 2).map(n => n + 2).reduce((sum, n) => sum + n)numbers.filter(n => n % 2 === 0).map(n => n * 2).reduce((sum, n) => sum + n, 0)
- Задача разбивается на три этапа:
- Фильтрация четных чисел: Используем метод
.filter(n => n % 2 === 0), который оставляет только числа, делящиеся на 2 без остатка. - Удвоение: Используем метод
.map(n => n * 2), который умножает каждое число на 2. - Суммирование: Используем метод
.reduce((sum, n) => sum + n, 0), который суммирует элементы массива, начиная с начального значения0. - Рассмотрим варианты:
- Вариант 1: Сначала удваивает числа, а потом фильтрует их на четность. Это неверно, так как фильтрация должна быть первым шагом.
- Вариант 2: Сначала пытается применить
reduce, а затем фильтровать — это неверный порядок действий, так какreduceуже преобразует массив в одно значение. - Вариант 3: Сначала суммирует, а затем пытается применить
mapиfilter— это неверно, так как послеreduceрезультат уже не является массивом. - Вариант 4: Фильтрует нечетные числа (
n % 2), а не четные. Это неправильно, так как требуется отфильтровать четные числа. - Вариант 5: Правильный порядок действий: сначала фильтрует четные числа, затем удваивает их и, наконец, суммирует.
📌Правильный ответ:
5. numbers.filter(n => n % 2 === 0).map(n => n * 2).reduce((sum, n) => sum + n, 0)
Вопрос 5
Какая из функций предназначена для создания объектов с заданными свойствами и методами?
- Генераторная функция (generator function)
- Рекурсивная функция (recursive function)
- Функция обратного вызова (callback)
- Функция-конструктор (constructor function)
- Стрелочная функция (arrow function)
- Генераторная функция (generator function):
- Предназначена для создания итераторов. Используется для управления последовательностями значений, но не подходит для создания объектов с заданными свойствами и методами.
- Рекурсивная функция (recursive function):
- Самовызванная функция, которая используется для решения задач, требующих многократного вызова себя с новыми параметрами. Она не связана с созданием объектов.
- Функция обратного вызова (callback):
- Используется в качестве параметра для других функций. Обычно применяется для обработки событий или асинхронных операций. Не используется для создания объектов.
- Функция-конструктор (constructor function):
- Специальная функция, используемая для создания объектов. Вызывается с помощью оператора
newи позволяет задавать свойства и методы объекта. Это как раз то, что требуется в вопросе. - Стрелочная функция (arrow function):
📌Правильный ответ:
4. Функция-конструктор (constructor function)
Она предназначена для создания объектов с заданными свойствами и методами.
Вопрос 6
Что будет выведено в консоль при выполнении данного кода?
- Функция
counter()возвращает замыкание — функцию, которая имеет доступ к переменнойcount, объявленной внутриcounter(). - При первом вызове
counter()создается новое окружение, в которомcountинициализируется значением0. - Вызов
increment()выполняет тело внутренней функции, увеличиваяcountна1(постфиксный инкремент++countвозвращает увеличенное значение) и возвращает результат. - Переменная
incrementхранит ссылку на эту внутреннюю функцию, поэтому каждый вызовincrement()продолжает работать с тем же значениемcount, увеличивая его.
- Первый вызов
increment()увеличиваетcountс0до1и возвращает1. - Второй вызов
increment()увеличиваетcountс1до2и возвращает2. - Третий вызов
increment()увеличиваетcountс2до3и возвращает3.
Вопрос 7
В какой последовательности выполняются операции в следующем коде, который включает асинхронные операции и взаимодействие с сетью? Учтите, что обработка запроса не будет закончена до того, как выполнится весь синхронный код программы.
- Начало, Конец, Данные получены: [данные], SetTimeout 1, SetTimeout 2.
- Конец, Начало, SetTimeout 1, SetTimeout 2, Данные получены: [данные].
- Начало, Конец, SetTimeout 1, Данные получены: [данные], SetTimeout 2.
- Начало, Выполнение SetTimeout 1, Выполнение SetTimeout 2, Конец, Данные получены: [данные].
- Начало, Конец, SetTimeout 1, SetTimeout 2, Данные получены: [данные].
- Синхронный код выполняется первым:
- Асинхронные операции помещаются в очередь событий (Event Loop):
setTimeoutс задержкой0не выполняется мгновенно, а добавляет колбэк в очередь задач.fetchвыполняется в фоне, и его результат обрабатывается в колбэке.then, который также попадает в очередь задач.- Очередность выполнения асинхронного кода:
"Начало"(синхронный код)."Конец"(синхронный код)."SetTimeout 1"(асинхронный колбэк таймера)."SetTimeout 2"(асинхронный колбэк таймера)."Данные получены: [данные]"(обработка ответаfetch).
📌Правильный ответ: 5. Начало, Конец, SetTimeout 1, SetTimeout 2, Данные получены: [данные].
Вопрос 8
Какое из следующих утверждений о конструкции Map в JavaScript является верным?
- Map гарантирует, что элементы будут храниться в порядке добавления.
- Ключи в Map могут быть только строками.
- Map автоматически сортирует свои элементы по возрастанию ключей.
- Метод
delete()в Map возвращает удаляемый элемент из коллекции. - Map не позволяет использовать объекты в качестве ключей.
- Map гарантирует, что элементы будут храниться в порядке добавления:
- Ключи в Map могут быть только строками:
- Неверно. В отличие от объектов, в
Mapключи могут быть любого типа, включая объекты, функции и числа. - Map автоматически сортирует свои элементы по возрастанию ключей:
- Метод
delete()в Map возвращает удаляемый элемент из коллекции: - Неверно. Метод
delete()возвращает булевое значение (trueилиfalse), указывающее, был ли элемент удален. - Map не позволяет использовать объекты в качестве ключей:
📌Правильный ответ: 1. Map гарантирует, что элементы будут храниться в порядке добавления.
Вопрос 9
Вам нужно создать неглубокую копию объекта и массива в JavaScript, при этом важно сохранить методы, определенные в самом объекте (без учета прототипной цепочки). Какой из следующих методов или подходов вы выберете?
const newObj = new Object(obj), newArr = new Array(arr)const newObj = { ...obj }, newArr = [...arr]const newObj = obj, newArr = arrconst newObj = Object.assign(obj), newArr = Array.copy(arr)const newObj = JSON.parse(JSON.stringify(obj)), newArr = JSON.parse(JSON.stringify(arr))
- Вариант 1:
new Object(obj)иnew Array(arr)создают объект и массив, но не копируют существующий объект или массив. Этот вариант не подходит.- Вариант 2:
- Использование операторов распространения
{ ...obj }и[...arr]создает неглубокие копии объекта и массива. При этом сохраняются методы, определенные непосредственно в объекте, что делает этот вариант подходящим. - Вариант 3:
- Присваивание
const newObj = objиconst newArr = arrсоздает ссылку на оригинальные объекты. Это не копия, а ссылка, поэтому изменения в одном объекте повлияют на другой. - Вариант 4:
Object.assign(obj)корректно создает неглубокую копию объекта, ноArray.copy(arr)не является существующим методом в JavaScript, что делает вариант неверным.- Вариант 5:
📌Правильный ответ: 2. const newObj = { ...obj }, newArr = [...arr]
Вопрос 10
У вас есть следующий код. Что будет выведено в консоль при его вызове?
- В консоли будет выведено «Rex makes a noise.» и затем «Rex barks.»
- В консоли будет выведено «Dog makes a noise.» и затем «Dog barks.»
- В консоли будет выведено только «Rex barks.»
- Произойдет ошибка из-за вызова
super.speak() - В консоли ничего не будет выведено, так как метод
speak()переопределен в классеDog.
- Класс
Animal: - Определяет конструктор, который принимает имя и сохраняет его в свойстве
this.name. - Метод
speak()выводит сообщение${this.name} makes a noise. - Класс
Dog: - Наследуется от
Animalс использованием ключевого словаextends. - Переопределяет метод
speak(), но внутри вызывает родительский метод с помощьюsuper.speak(). - После вызова
super.speak()добавляет свою собственную строку в консоль. - Создание объекта
dog: - При вызове
new Dog("Rex")используется конструктор родительского классаAnimal. Свойствоnameобъектаdogстановится равным"Rex". - Вызов
dog.speak():
📌Правильный ответ: 1. В консоли будет выведено «Rex makes a noise.» и затем «Rex barks.»
Вопрос 11
Вы разрабатываете интерфейс для интернет-магазина, который должен содержать витрину для тысячи товаров. Какой подход стоит использовать, чтобы обеспечить плавное и быстрое отображение этих товаров при прокрутке и избежать задержек и «зависаний» интерфейса?
- Применение техники «Debounce» для событий прокрутки.
- Реализовать виртуальный скроллинг для отображения товаров в текущем viewport (видимой области) браузера.
- Использовать кэширование данных на стороне клиента.
- Провести оптимизацию запросов к серверу через WebSockets для непрерывной загрузки данных о товарах.
- Применить «ленивую загрузку» для изображений товаров.
- Применение техники «Debounce» для событий прокрутки:
- Техника «Debounce» помогает оптимизировать частоту вызовов функции при частом срабатывании событий (например, при прокрутке).
- Это полезно, но само по себе не решает проблему плавного отображения большого количества данных.
- Реализовать виртуальный скроллинг:
- Виртуальный скроллинг отображает только те элементы, которые находятся в текущей видимой области экрана (viewport).
- Остальные элементы не рендерятся до тех пор, пока не станут видимыми.
- Это эффективный способ обработки тысяч элементов без задержек в интерфейсе.
- Использовать кэширование данных на стороне клиента:
- Кэширование ускоряет доступ к ранее загруженным данным, но не решает проблемы обработки большого количества элементов на экране.
- Оптимизация запросов через WebSockets:
- WebSockets обеспечивает непрерывный обмен данными между клиентом и сервером.
- Это полезно для обновлений данных, но не связано с рендерингом большого количества элементов.
- Применение «ленивой загрузки» для изображений:
📌Правильный ответ: 2. Реализовать виртуальный скроллинг для отображения товаров в текущем viewport (видимой области) браузера.
Вопрос 12
Проанализируйте фрагмент кода и определите порядок, в котором сообщения будут выведены в консоль.
Обоснование:
Чтобы определить порядок выполнения, нужно учитывать последовательность обработки синхронного кода, микрозадач (Promise), макрозадач (setTimeout), и других асинхронных механизмов (например, requestAnimationFrame).
- Синхронный код:
console.log('1')выполняется первым.console.log('6')выполняется после всех остальных синхронных операций.- Микрозадачи (Promise):
Promise.resolve().then()добавляет задачи в очередь микрозадач.- Микрозадачи выполняются после завершения текущего синхронного кода, перед макрозадачами.
- Следовательно,
console.log('4')будет выполнен до вызововsetTimeoutилиrequestAnimationFrame. - Макрозадачи:
setTimeoutдобавляет колбэки в очередь макрозадач.requestAnimationFrameвыполняется после завершения синхронного кода, но раньше, чем макрозадачи.- Обработка колбэков
setTimeoutи вложенного Promise: console.log('2')выполняется первым среди задачsetTimeout.- Затем
Promise.resolve().then()внутри этого таймера добавляетconsole.log('3')в очередь микрозадач, которая выполняется сразу после текущей макрозадачи. - Аналогично,
console.log('5')из второгоsetTimeoutвыполнится на следующей итерации очереди макрозадач.
1(синхронный код).6(синхронный код).4(микрозадача).7(requestAnimationFrame).2(первая макрозадача).3(микрозадача из первогоsetTimeout).5(вторая макрозадача).
📌Правильный ответ: 3. 1, 6, 4, 7, 2, 3, 5
Вопрос 13
Для чего применяется tree shaking в системах сборки JavaScript на прикладном уровне?
- Для асинхронного выполнения функций с использованием деревьев вызовов (call trees).
- Для автоматического определения и удаления неиспользуемого кода во время сборки.
- Для шифрования и обфускации JavaScript-кода с целью защиты интеллектуальной собственности.
- Для создания иерархии древовидной структуры данных в JavaScript-приложениях.
- Для отображения и визуализации иерархической структуры DOM-элементов веб-страницы.
- Tree shaking — это техника оптимизации, используемая в системах сборки (например, Webpack, Rollup) для устранения мертвого кода (dead code elimination). Она основывается на анализе импортов/экспорта модулей и удаляет код, который не используется в итоговом бандле.
- Термин "tree shaking" происходит от метафоры: представьте дерево кода, с которого "стряхиваются" ненужные ветви. Это позволяет уменьшить размер бандла и повысить производительность приложения.
- Асинхронное выполнение функций с использованием деревьев вызовов:
- Автоматическое определение и удаление неиспользуемого кода во время сборки:
- Шифрование и обфускация JavaScript-кода:
- Создание иерархии древовидной структуры данных:
- Отображение и визуализация иерархической структуры DOM-элементов:
📌Правильный ответ: 2. Для автоматического определения и удаления неиспользуемого кода во время сборки.
Вопрос 14
Как добавить новый элемент в DOM с помощью JavaScript после загрузки страницы?
- Используя комбинацию методов
document.createElm()иdocument.insert(). - Используя метод
document.write(). - Используя метод
document.createAndInsertElement(). - Используя комбинацию методов
document.create()иdocument.append(). - Используя комбинацию методов
document.createElement()иdocument.appendChild().
document.createElm()иdocument.insert():document.write():- Используется для записи содержимого в документ, но только в момент загрузки страницы. После загрузки страницы использование этого метода перезапишет существующий документ.
document.createAndInsertElement():document.create()иdocument.append():- Неверно.
document.create()не существует, ноdocument.createElement()— правильный метод для создания элемента. document.createElement()иdocument.appendChild():
📌Правильный ответ: 5. Используя комбинацию методов document.createElement() и document.appendChild().
Вопрос 15
Вы работаете над SPA (Single Page Application), которое активно обращается к API для обработки действий пользователя и вывода изменяющегося контента. Ваша задача — увеличить производительность приложения и сократить время полной первой отрисовки страницы. У вас в наличии мощный сервер, вы не ограничены пропускной способностью сетевого оборудования. Какой метод вы используете для достижения этой цели?
- Web Workers
- Кэширование ответов от API в localStorage
- Техника Lazy Loading
- Техники Throttling и Debouncing
- Server-Side Rendering (SSR)
- Web Workers:
- Используются для выполнения фоновых операций без блокировки основного потока.
- Это полезно для вычислительных задач, но не влияет на первую отрисовку страницы.
- Кэширование ответов от API в localStorage:
- Ускоряет повторные запросы, но не сокращает время полной первой отрисовки страницы, так как данные сначала должны быть загружены.
- Техника Lazy Loading:
- Уменьшает нагрузку за счёт отложенной загрузки ресурсов (например, изображений), но не оптимизирует время полной первой отрисовки.
- Техники Throttling и Debouncing:
- Ограничивают частоту выполнения функций (например, обработчиков событий), но не решают задачу, связанную с полной первой отрисовкой.
- Server-Side Rendering (SSR):
📌Правильный ответ: 5. Server-Side Rendering (SSR)
Заключение
Эффективное использование JavaScript требует понимания как базовых механизмов, так и современных практик оптимизации. Методы, такие как SSR для быстрой первой отрисовки, виртуальный скроллинг для работы с большими данными, tree shaking для удаления мертвого кода, а также грамотная работа с DOM, позволяют разработчикам создавать производительные и удобные веб-приложения. Знание этих техник поможет вам решать реальные задачи и достигать лучших результатов при разработке веб-интерфейсов.