Работа мечты в один клик 💼

💭Мечтаешь работать в Сбере, но не хочешь проходить десять кругов HR-собеседований? Теперь это проще, чем когда-либо!
💡AI-интервью за 15 минут – и ты уже на шаг ближе к своей новой работе.
Как получить оффер? 📌 Зарегистрируйся 📌 Пройди AI-интервью 📌 Получи обратную связь сразу же!
HR больше не тянут время – рекрутеры свяжутся с тобой в течение двух дней! 🚀
Реклама. ПАО СБЕРБАНК, ИНН 7707083893. Erid 2VtzquscAwp
21. Что такое DOM?

DOM (Document Object Model) – это модель, которая представляет HTML-документ в виде дерева тегов. Каждый HTML-тег в этом дереве является объектом.
Вложенные теги являются дочерними элементами по отношению к своему
родительскому элементу. Текст внутри тега также является объектом. Все эти объекты
доступны для любых манипуляций с помощью JavaScript, а эти манипуляции, в свою очередь, позволяют
динамически управлять содержимым страницы. В приведенном ниже примере мы
выбираем элемент с классом "story"
и сохраняем его в переменной
story.
Затем мы выбираем элементы с id "set-text"
и
"clear-text"
и добавляем им обработчики событий. Когда на элемент с
id "set-text"
нажимают, текстовое содержимое элемента с классом
"story"
меняется на "Ночь 31 декабря началась снегопадом и
метелью."
Когда нажимают на элемент с id "clear-text"
,
текстовое содержимое элемента с классом "story"
очищается:
<!DOCTYPE html>
<html>
<head>
<title>Пример манипуляции DOM</title>
</head>
<body>
<div class="story">Это текст новогодней истории.</div>
<button id="set-text">Изменить div-элемент story</button>
<button id="clear-text">Очистить div-элемент</button>
<script>
const story = document.body.querySelector(".story");
const setText = document.body.querySelector("#set-text");
setText.addEventListener("click", () => {
story.textContent = "Ночь 31 декабря началась снегопадом и метелью.";
});
const clearText = document.body.querySelector("#clear-text");
clearText.addEventListener("click", () => {
story.textContent = "";
});
</script>
</body>
</html>
22. Что такое цикл событий?

Цикл событий в JavaScript – это механизм, который управляет выполнением кода. Он обеспечивает обработку событий и выполнение задач в правильном порядке. Хотя JavaScript работает в однопоточной среде, цикл событий дает возможность обрабатывать асинхронные операции и предотвращает блокировку основного потока выполнения.
Когда асинхронная операция (например, запрос к серверу) завершается, она помещает соответствующее событие в очередь событий. Цикл событий обрабатывает задачи в порядке их поступления. Он берет событие из очереди и передает его для выполнения. Если событие содержит обратный вызов или обработчик – вызывается соответствующая функция для выполнения кода, связанного с этим событием. Цикл событий также обрабатывает задачи, связанные с таймерами и промисами. Благодаря циклу событий, JavaScript быстро реагирует на действия пользователя и эффективно использует ресурсы при работе с асинхронными операциями .
В приведенном ниже примере, несмотря на то, что setTimeout(firstTask, 0)
имеет время ожидания 0
секунд, первая задача firstTask()
все равно не будет выполнена сразу после secondTask()
. Это происходит потому, что JavaScript использует цикл событий для управления выполнением асинхронных операций. Когда мы вызываем setTimeout()
, функция firstTask()
помещается в очередь событий, – цикл событий начинает обрабатывать эту функцию, когда стек вызовов опустеет.
function firstTask() {
// Эмуляция долгой операции
let i = 1000000000;
while (i > 0) {
i--;
}
console.log('Первая задача выполнена');
}
function secondTask() {
console.log('Вторая задача выполнена');
}
setTimeout(firstTask, 0);
secondTask();
23. Что такое прототипное наследование?

Прототипное наследование в JavaScript – это механизм, который позволяет одному объекту наследовать свойства и методы другого объекта. Это основной способ наследования в JavaScript.
Каждый объект в
JavaScript имеет внутреннее скрытое свойство prototype
, которое ссылается на другой объект. Этот
другой объект называется прототипом первого объекта. При попытке получить доступ к свойству объекта,
JavaScript сначала проверяет, есть ли это свойство в самом объекте. Если нет,
он ищет его в прототипе объекта. Если и там его нет, то в прототипе прототипа и
так далее. Если свойство или метод отсутствуют в объекте и его прототипе, JavaScript вернет undefined
:
// Прототип TV
let TV = {
brand: 'Generic',
smart: true,
resolution: '4k',
turnOn() {
console.log(`Включаем ${this.brand}`);
},
turnOff() {
console.log(`Выключаем ${this.brand}`);
}
};
// Создаем экземпляр Samsung TV
let samsungTV = {
__proto__: TV,
brand: 'Samsung'
};
// Создаем экземпляр Sony TV
let sonyTV = {
__proto__: TV,
brand: 'Sony'
};
// Используем существующие методы экземпляров
samsungTV.turnOn(); // Включаем Samsung
sonyTV.turnOff(); // Выключаем Sony
// Вызываем существующее свойство
console.log(sonyTV.smart); // true
// Вызываем несуществующее свойство
console.log(samsungTV.size); // undefined
24. Для чего нужен оператор опциональной последовательности?

Оператор
опциональной последовательности ?.
позволяет получить безопасный доступ к
вложенным свойствам объекта – даже в том случае, когда промежуточное свойство
отсутствует. Оператор ?.
прекращает оценку и возвращает undefined
, если часть
после ?.
является либо undefined
, либо null
.
let user = {
name: 'Евгений',
lastname: 'Онегин',
address: {
street: 'Невский проспект',
city: 'Санкт-Петербург'
}
};
console.log(user.address?.street); // Невский проспект
console.log(user?.lastname); // Oнегин
console.log(user?.phone?.number); // undefined - нет ошибки
console.log(user.phone.number); // Uncaught TypeError: Cannot read properties of undefined (reading 'number')
25. Что такое теневой DOM?

Shadow DOM – это техника, позволяющая создавать изолированные фрагменты HTML и CSS в специальном сегменте DOM, который находится внутри определенного элемента. Такой подход позволяет исключить влияние инкапсулированных стилей на структуру и внешний вид основной страницы, и обеспечивает большую гибкость и контроль над представлением и поведением элементов.
В приведенном ниже примере инкапсулированные стили пользовательского элемента не влияют на внешний вид остальной части страницы:
<p>Привет, мир!</p>
<!-- Пользовательский элемент -->
<my-element></my-element>
<script>
class MyElement extends HTMLElement {
constructor() {
super();
// Создаем Shadow DOM
let shadow = this.attachShadow({mode: 'open'});
// Добавляем в Shadow DOM
shadow.innerHTML = `
<style> p { color: #fff; background-color: #000; font-family: Consolas ; font-size: 25px; } </style>
<p>Привет, мир!</p>
`;
}
}
// Регистрируем пользовательский элемент
customElements.define('my-element', MyElement);
</script>

26. Что такое рекурсия и как ее можно использовать в JavaScript?

Рекурсия в программировании – это процесс, в котором функция вызывает саму себя. Рекурсия обычно используется для решения задач, которые можно разбить на более простые подзадачи. В JavaScript рекурсию можно использовать, например, для работы с многоуровневыми массивами и обхода деревовидных структур данных.
Так можно преобразовать вложенный массив в одномерный:
const flattenArray = arr => arr.reduce((accumulator, value) => accumulator.concat(
Array.isArray(value) ? flattenArray(value) : value
), []);
let result = flattenArray([1, [2, [3, [4, 5], [6, 7, 8, [9, 10]]]]]);
console.log(result); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
А так можно обойти и визуализировать дерево:
<!DOCTYPE html>
<html>
<body>
<pre id="result"></pre>
<script>
let tree = {
value: '-root',
children: [
{
value: '|child1',
children: [
{ value: '-|grandchild1' },
{ value: '-|grandchild2' }
]
},
{
value: '|child2',
children: [
{ value: '-|grandchild3' },
{
value: '-|grandchild4',
children: [
{ value: '--|great-grandchild1' },
{ value: '--|great-grandchild2' }
]
}
]
}
]
};
function traverseTree(node, level = 0) {
let indent = ' '.repeat(level * 2);
let resultDiv = document.getElementById('result');
resultDiv.innerHTML += `${indent}${node.value}\n`;
if (node.children) {
node.children.forEach(childNode => traverseTree(childNode, level + 1));
}
}
traverseTree(tree);
</script>
</body>
</html>
Результат:

27. В чем разница между объявлением функции и функциональным выражением?
Объявление функции и функциональное выражение – это два способа определения функций в JavaScript.
Объявление функции – это традиционный способ определения функции. Функция создается и присваивается переменной, как любое другое значение. При этом объявленные функции доступны во всем коде, даже до того, как программа достигает того участка, где они определены:
console.log(typeof myFunction); // Вывод: function
function myFunction() {
console.log('Привет, админ!');
}
Функциональное выражение – это альтернативный способ определения функции:
let greet = function() {
console.log('Привет, админ!');
}
greet(); // Привет, админ!
В отличие от традиционной функции, функциональное выражение нельзя вызывать до определения в коде – это приведет к ошибке . Функциональные выражения могут:
- быть анонимными;
- формировать замыкания;
- передаваться в качестве аргументов другим функциям;
- использоваться как немедленно вызываемые функциональные выражения (IIFE).
28.Что такое функции-конструкторы?

Конструкторы в JavaScript – это специальные функции, используемые для создания объектов. Вот два основных правила при работе с конструкторами:
- Имя конструктора должно начинаться с заглавной буквы.
- Конструктор вызывается при помощи оператора
new
.
Когда мы вызываем
конструктор через new
, происходит следующее:
- Создается новый пустой объект и присваивается в
this
. - Выполняется код внутри конструктора. Обычно он модифицирует объект
this
, добавляя в него свойства. - Значение
this
возвращается из конструктора как результат.
Например:
function User(name) {
this.name = name;
this.sayHi = function() {
alert(`Привет, меня зовут ${this.name}!`);
};
}
let user = new User("Вася");
user.sayHi(); // Привет, меня зовут Вася!
Здесь User – функция-конструктор. Когда мы
вызываем конструктор через new User("Вася")
,
создается объект user
с
указанным именем и методом sayHi
. Таким образом с помощью конструкторов можно многократного создавать
объекты по одному шаблону.
29. Как получить список ключей и значений объекта?

В JavaScript для получения списка ключей и значений объекта используются методы Object.keys() и Object.values().
Object.keys() возвращает массив со всеми ключами объекта:
const person = {
name: "Егор",
age: 30,
city: "Ростов"
};
const keys = Object.keys(person);
console.log(keys); // ['name', 'age', 'city']
Object.values() возвращает массив со всеми значениями свойств объекта:
const person = {
name: "Егор",
age: 30,
city: "Ростов"
};
const values = Object.values(person);
console.log(values); // ['Егор', 30, 'Ростов']
Используя Object.keys() и Object.values(), можно сделать
перебор:
const person = {
name: "Егор",
age: 30,
city: "Ростов"
};
Object.keys(person).forEach(key => {
console.log(key, person[key]);
});
Или вывести ключи и значения в виде объекта:
const person = {
name: "Егор",
age: 30,
city: "Ростов"
};
Object.entries(person).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
30. Приведите примеры нововведений, добавленных в JavaScript в версии ES6

В ES6 множество нововведений, вот всего несколько примеров.
Деструктуризация объектов и массивов:
// Деструктуризация объекта
const user = {
name: "Алиса",
age: 20
}
const {name, age} = user;
// name = "Алиса"; age = 20
// Деструктуризация массива
const array = [1, 2, 3];
let [x, y] = array;
// x = 1; y = 2
Шаблонные строки:
const name = "Инна";
console.log(`Привет, ${name}!`); // Привет, Инна!
Стрелочные функции:
const sum = (a, b) => a + b;
console.log(sum(1, 2)); // 3
Параметры по умолчанию:
function volume(x = 1, y = 2, z = 3) {
return x * y * z;
}
volume(); // 6
volume(2); // 12
Объявление переменных с помощью let и const.
let – аналог var, но с блочной областью видимости:
{
let x = 1;
}
console.log(x); // Ошибка, x не видна за пределами блока
const – объявление константы (переменной, которую нельзя изменить):
const PI = 3.14;
PI = 2; // Ошибка, нельзя изменить значение
Оператор распространения (позволяет распаковывать элементы массива или объекта для аргументов функции или создания новых массивов/объектов):
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 2, c: 3, d: 4 }
31. Как происходит наследование классов в ES6?
Наследование классов в ES6 осуществляется с помощью ключевого слова extends
, которое следует за именем родительского класса. Родительский класс часто называют базовым классом, а класс, который наследует базовый/родительский класс, называется производным или дочерним:
class Parent {
constructor(name) {
this.name = name;
}
greeting() {
return `Добрый день, ${this.name}!`;
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // вызывает конструктор родительского класса
this.age = age;
}
greeting() {
let greeting = super.greeting(); // вызывает метод родительского класса
return `${greeting} Как поживаешь?`;
}
}
let vasya = new Child('Вася', 10);
console.log(vasya.greeting()); // Добрый день, Вася! Как поживаешь?
32. Что такое микрозадачи и макрозадачи?

В JavaScript микрозадачи и макрозадачи относятся к типам задач, которые должны выполняться в цикле событий.
Микрозадачи – это задачи, которые должны быть выполнены в текущем цикле событий перед тем, как браузер перерисует страницу. Они обычно добавляются в очередь выполнения с помощью методов, таких как Promise.then(), process.nextTick() (в Node.js) или MutationObserver. Примеры микрозадач – выполнение обработчиков промисов и мутации DOM.
С другой стороны, макрозадачи – это задачи, которые должны быть выполнены после окончания текущего цикла событий и перед тем, как изменения будут отрендерены на экране. Это включает задачи, добавленные в очередь событий с помощью setTimeout, setInterval, requestAnimationFrame, а также обработку входных событий и сетевых запросов. Макрозадачи выполняются после того, как завершается обработка всех микрозадач в текущем цикле событий.
Разница между микрозадачами и макрозадачами определяет порядок выполнения и позволяет управлять приоритетами различных задач в JavaScript. Микрозадачи имеют более высокий приоритет и выполняются до макрозадач, что позволяет быстрее обновлять интерфейс и предотвращает блокировку основного потока выполнения JavaScript. В приведенном ниже примере setTimeout является макрозадачей, а Promise.then() – микрозадачей. Поскольку микрозадачи имеют более высокий приоритет, Promise.then() выполняется перед setTimeout, и поэтому promise появляется в первую очередь:
setTimeout(() => alert("я - макрозадача"));
Promise.resolve()
.then(() => alert("я - микрозадача"));
alert("я - синхронная операция основного кода");
33. Что такое генераторы?

Генераторы в JavaScript представляют собой специальный тип функций, которые генерируют последовательность значений по одному за раз по мере необходимости, и позволяют приостанавливать и возобновлять свое выполнение (в отличие от обычных функций, которые выполняются до завершения). Генераторы хорошо работают с объектами и упрощают создание потоков данных.
Чтобы объявить генератор, используют специальный синтаксис – функцию-генератор. Функция-генератор определяется с помощью символа *
после ключевого слова function:
function* generateNumbers() {
yield 1;
yield 2;
yield 3;
yield 4;
return 5;
}
let gen = generateNumbers();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: 4, done: false }
console.log(gen.next()); // { value: 5, done: true }
Генератор возвращает итератор, который можно использовать для контроля над выполнением функции. Основной метод итератора – next(). Когда вызывается next(), выполнение кода продолжается до ближайшего оператора yield. Когда достигнут yield, выполнение функции приостанавливается, и соответствующее значение возвращается во внешний код.
34. Какие существуют методы для сохранения данных в браузере?

Есть 3 основных метода хранения данных в браузере:
- LocalStorage и SessionStorage используются для хранения пар ключ-значение. Данные, сохраненные в них, сохраняются после обновления страницы. При этом только LocalStorage может сохранять данные после перезапуска браузера. Оба хранилища могут использовать только строки в качестве ключей и значений, поэтому объекты необходимо преобразовать с помощью JSON.stringify().
- Cookie – небольшие строки данных, которые хранятся в браузере. Cookie обычно устанавливаются веб-сервером с использованием заголовка Set-Cookie. Браузер затем автоматически добавляет их почти ко всем запросам на тот же домен с использованием заголовка Cookie. Один экземпляр cookie может содержать до 4 кб данных. В зависимости от браузера, допускается более 20 cookie на сайт.
- IndexedDB – встроенная база данных, более мощная, чем localStorage. Это NoSQL-хранилище данных в формате JSON внутри браузера, где доступны несколько типов ключей, а значения могут быть практически любым. IndexedDB поддерживает асинхронный доступ, транзакции для обеспечения согласованности данных и создание индексов для эффективного поиска. Позволяет хранить больше данных, чем localStorage, может быть связана с Service Workers и другими технологиями, которые обеспечивают функционирование PWA в оффлайне.
Вот примеры использования каждого из этих методов хранения данных:
// LocalStorage
localStorage.setItem('фрукт', 'апельсин');
console.log(localStorage.getItem('фрукт')); // Выводит: апельсин
// SessionStorage
sessionStorage.setItem('тема', 'dark');
console.log(sessionStorage.getItem('тема')); // Выводит: dark
// Cookie
document.cookie = "username=Вася Пупкин; SameSite=None; Secure";
console.log(document.cookie); // Выводит: username=Вася Пупкин
// IndexedDB
let indexedDB =
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB;
if (!indexedDB) {
console.log("IndexedDB не поддерживается");
} else {
let open = indexedDB.open("myDatabase");
open.onupgradeneeded = function() {
let db = open.result;
db.createObjectStore("notes", {keyPath: "id"});
};
open.onsuccess = function() {
console.log("База данных создана");
};
open.onerror = function() {
console.log("Ошибка!");
};
}
35. В чем заключается разница между sessionStorage и localStorage?
Сессионное хранилище sessionStorage и локальное хранилище localStorage позволяют сохранять данные в формате ключ-значение в браузере. Оба они используются для хранения данных на стороне клиента, но имеют некоторые отличия:
- Объем хранимых данных – localStorage может хранить до 10 МБ данных, в то время как sessionStorage может хранить только до 5 МБ данных.
- Срок хранения данных – в localStorage данные не удаляются, когда закрывается браузер или вкладка. И напротив, данные в sessionStorage удаляются, когда закрывается вкладка или окно браузера.
- Доступность данных – из localStorage данные доступны в любом окне браузера, в то время как данные из sessionStorage доступны только из того же окна браузера, где они были сохранены.
Пример использования localStorage:
// Установка значения
localStorage.setItem('жанр', 'триллер');
// Получение значения
let value = localStorage.getItem('жанр');
console.log(value);
// Удаление значения
localStorage.removeItem('жанр');
// Очистка всего хранилища
localStorage.clear();
Пример использования sessionStorage:
// Установка значения
sessionStorage.setItem('стиль', 'индастриал');
// Получение значения
let value = sessionStorage.getItem('стиль');
console.log(value);
// Удаление значения
sessionStorage.removeItem('стиль');
// Очистка всего хранилища
sessionStorage.clear();
36. Что такое регулярные выражения?

Регулярные выражения (regex) – это паттерны, которые используются для поиска и замены текста в строках. В паттернах используются комбинации специальных символов (шаблоны) и флаги (модификаторы, которые определяют поведение поиска). Регулярные выражения часто используют в комбинации с этими методами:
- test() – проверка на соответствие шаблону
- match() – поиск соответствий
- replace() – замена по шаблону
- search() – поиск индекса
Составлять регулярные выражения можно с помощью конструктора RegExp или литеральной нотации.
Пример определения email-адреса в строке с помощью конструктора RegExp:
const emailRegex = new RegExp('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}', 'gi');
const email = 'Присылайте резюме на test@example.com, отвечу всем';
console.log(emailRegex.test(email)); // true
Пример извлечения телефонного номера с помощью литеральной нотации:
const phone = 'Теперь у меня новый номер +79876543210, на связи с 09:00 до 21:00';
const phoneRegex = /\+7\d{10}/gi;
const found = phone.match(phoneRegex);
if (found) {
console.log(found[0]);
} else {
console.log('Совпадения не найдены');
}
37. В чем заключается разница между WeakSet, WeakMap и обычными Set и Map?
WeakSet и WeakMap – это специальные структуры данных в JavaScript, которые отличаются особенностью хранения ссылок на объекты.
В обычных Set и Map хранятся сильные ссылки на объекты. Это значит, что пока существует ссылка на объект в этих структурах, сборщик мусора не удалит этот объект из памяти, даже если больше нигде в коде нет ссылок на него.
И напротив, в WeakSet и WeakMap хранятся слабые ссылки. Это означает, что если объект, на который есть ссылка в этих структурах, больше недоступен в коде (т.е. нигде больше нет сильных ссылок на него), то сборщик мусора может удалить этот объект из памяти, даже если в WeakSet или WeakMap все еще есть ссылка на него. Таким образом, использование слабых ссылок позволяет не держать в памяти ненужные больше объекты и экономить память.
Кроме того, в WeakMap в качестве ключей могут использоваться только объекты, а не примитивные значения. А в WeakSet хранятся только объекты, без ключей.
38. Почему два объекта с одинаковыми полями возвращают false при сравнении?
В JavaScript объекты сравниваются по ссылкам на область памяти, где они хранятся. Два разных объекта, даже если у них одинаковые поля и значения этих полей, располагаются в разных областях памяти. Например, у нас есть:
const example1 = {fruit: 'яблоко'};
const example2 = {fruit: 'яблоко'};
console.log(example1 == example2); // false
Хотя у этих двух объектов одно и то же поле fruit
со значением 'яблоко'
, на самом деле это два абсолютно разных объекта, которые хранятся в разных ячейках памяти. Поэтому если мы сравним их, результат будет false
. Для того чтобы два объекта считались равными, нужно, чтобы это был один и тот же объект, то есть чтобы обе переменные ссылались на одну и ту же область памяти.
39. Как в JavaScript реализованы методы примитивных типов данных?
JavaScript позволяет работать с примитивными типами данных – строками, числами, логическими значениями – как с объектами, поскольку у них тоже есть методы. Например, у строк есть методы toUpperCase() и toLowerCase(), у чисел есть методы toFixed() и toPrecision() и т.д.
Эта возможность реализована благодаря специальным оберточным объектам для каждого примитивного типа данных. Эти объекты называются:
- String – для строк
- Number – для чисел
- Boolean – для логических значений
- Symbol – для символов
Когда мы вызываем метод у примитивного значения, например "test".toUpperCase()
, происходит следующее:
- Создается временный оберточный объект типа
String
со значением"test"
. - У этого объекта вызывается метод
toUpperCase()
. - Результат возвращается обратно в примитивное значение.
- Временный оберточный объект удаляется.
Таким образом реализуется возможность использовать методы у примитивных типов данных. Благодаря этому механизму, примитивы в JavaScript ведут себя как объекты.
40. Как проверить, из какого класса был создан объект?

Для этого в JavaScript используется оператор instanceof. Он позволяет проверить, из какого класса был создан объект, учитывая наследование.
Например, есть базовый класс Animal и классы-наследники Dog и Cat:
class Animal {
constructor() {}
}
class Dog extends Animal {
constructor() {
super();
}
bark() {}
}
class Cat extends Animal {
constructor() {
super();
}
meow() {}
}
Создадим объект класса Dog и проверим с помощью оператора instanceof
, является ли объект экземпляром указанного класса или классов-родителей:
const dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Cat); // false
Комментарии