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 = arr
const 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, позволяют разработчикам создавать производительные и удобные веб-приложения. Знание этих техник поможет вам решать реальные задачи и достигать лучших результатов при разработке веб-интерфейсов.