100 теоретических вопросов по JavaScript
Доброго времени суток, друзья!
Представляю вашему вниманию список из первых 100 вопросов по основам JavaScript из этого репозитория с краткими ответами и ссылками на «Современный учебник по JavaScript» Ильи Кантора (JSR) и MDN. Также в конце имеются ссылки на статьи для пытливых умов.
Данный список, а также 300+ практических вопросов доступны в моем приложении, которое можно посмотреть и установить здесь (PWA Store) и здесь (Netlify). В приложении реализован механизм запоминания изученного вопроса, а также обеспечена работа в офлайн-режиме.
Приношу извинения за возможные ошибки и опечатки. При их обнаружении, не стесняйтесь писать в личку, буду весьма признателен.
Дополнительная литература
Как создать объект?
Существует множество способов это сделать. Вот некоторые из них:
Конструктор объекта.
Простейший способ создать объект — использовать контруктор объекта. Однако, данный способ использовать не рекомендуется.
const object = new Object()
Метод Object.create ()
При использовании данного метода ему в качестве аргумента передается объект, который станет прототипом нового объекта.
const object = Object.create(null)
Литерал объекта
Синтаксис объектного литерала эквивалентен методу Object.create (null).
const object = {}
Функция-конструктор
Создаем функцию-конструктор и применяем оператор «new» для создания экземпляра этой функции — объекта.
function Person (name) {
const object = { }
object.name = name
object.age = 30
return object
}
const object = new Person('John')
Функция-конструктор и прототип
Данный способ идентичен предыдущему, за исключением того, что для добавления новых свойств и методов используется прототип функции.
function Person ( ) { }
Person.prototype.name = 'John'
const object = new Person()
Это похоже на создание экземпляра с помощью Object.create () с прототипом функции и привязку данной функции к
экземпляру с параметрами в качестве аргументов.
function f {}
new f(x, y, z)
Или:
// создаем новый экземпляр, используя прототип функции
const newInstance = Object.create(f.prototype)
// привязываем функцию
const result = f.call(newInstance, x, y, z)
// если result не нулевой объект, используем ее, иначе, используем newInstance
console.log(result && typeof result === 'object' ? result : newInstance)
Класс
Для создания объектов могут использоваться классы.
class Person {
constructor(name) {
this.name = name
}
}
const object = new Person('John')
Шаблон проектирования синглтон (singleton)
Сингтон — это объект, допускающий создание только одного экземпляра. Повторные вызовы конструктора данного объекта будут возвращать один и тот же экземпляр. Это позволяет избежать случайного создания нескольких экземпляров одного объекта.
const object = new function () {
this.name = 'John'
}
JSR
MDN
Что такое прототипы?
Прототипы используется для создания новых объектов на основе существующих. Такая техника называется прототипным наследованием. Прототип экземпляра объекта доступен через Object.getPrototypeOf (object) или свойство __proto__ (внутреннее скрытое свойство [[Prototype]]), а прототип функции-конструктора доступен через Object.prototype.
JSR
MDN
В чем разница между Call, Apply и Bind?
Разницу между данными методами можно объяснить с помощью следующих примеров.
Метод call () вызывает функцию с указанным значением this и аргументами через запятую.
const employee1 = { firstName: 'Иван', lastName: 'Иванов' }
const employee2 = { firstName: 'Петр', lastName: 'Петров' }
function invite (greet1, greet2) {
onsole.log(`${greet1}, ${this.firstName} ${this.lastName}. ${greet2}`)
}
invite.call(employee1, 'Привет', 'Как дела?') // Привет, Иван Иванов. Как дела?
invite.call(employee2, 'Привет', 'Как дела?') // Привет, Петр Петров. Как дела?
Метод apply () вызывает функцию с указанным значением this и аргументами в виде массива.
const employee1 = { firstName: 'Иван', lastName: 'Иванов' }
const employee2 = { firstName: 'Петр', lastName: 'Петров' }
function invite (greet1, greet2) {
console.log(`${greet1}, ${this.firstName} ${this.lastName}. ${greet2}`)
}
invite.apply(employee1, ['Привет', 'Как дела?']) // Привет, Иван Иванов. Как дела?
invite.apply(employee2, ['Привет', 'Как дела?']) // Привет, Петр Петров. Как дела?
Метод bind () возвращает новую функцию с указанным значением this и позволяет передать ей массив или любое количество аргументов через запятую.
const employee1 = { firstName: 'Иван', lastName: 'Иванов' }
const employee2 = { firstName: 'Петр', lastName: 'Петров' }
function invite (greet1, greet2) {
console.log(`${greet1}, ${this.firstName} ${this.lastName}. ${greet2}`)
}
const inviteEmployee1 = invite.bind(employee1)
const inviteEmployee2 = invite.bind(employee2)
inviteEmployee1('Привет', 'Как дела?') // Привет, Иван Иванов. Как дела?
inviteEmployee2('Привет', 'Как дела?') // Привет, Петр Петров. Как дела?
Таким образом, методы call () и apply () вызывают функцию после ее привязки к объекту. Разница между ними состоит в способе передачи аргументов. Эту разницу легко запомнить при помощи первых букв методов: call — это запятая (comma, c), apply — массив (array, a). Метод bind () возвращает новую функцию, привязанную к указаному объекту.
JSR — Call/Apply
JSR — Bind
MDN — Call
MDN — Apply
MDN — Bind
Что такое JSON и какие у него есть методы?
JSON — это текстовый формат данных, основанный на синтаксисе объектов JavaScript, изобретенный Дугласом Крокфордом. Он используется для передачи данных по сети и, обычно, имеет расширение .json и MIME-тип application/json.
Разбор (парсинг): преобразует строку в формате JSON в объект.
JSON.parse(text)
Стрингификация: преобразует объект в строку в формате JSON для передачи по сети.
JSON.stringify(object)
JSR
MDN
Что делает метод Array.slice ()?
Метод slice () возвращает выбранные элементы массива в виде нового массива. Он возвращает элементы, начиная с индекса, указанного в первом аргументе, и заканчивая, но не включая, индексом, указанном во втором необязательном аргументе. Если второй аргумент отсутствует, то будут извлечены все элементы, начиная с индекса, указанного в первом аргументе.
const arrayIntegers = [1, 2, 3, 4, 5]
const arrayIntegers1 = arrayIntegers.slice(0, 2) // [1, 2]
const arrayIntegers2 = arrayIntegers.slice(2, 3) // [3]
const arrayIntegers3 = arrayIntegers.slice(4) // [5]
Обратите внимание, что данный метод не изменяет исходный массив, а лишь возвращает его подмножество в виде нового массива.
JSR
MDN
Что делаем метод Array.splice ()?
Метод splice () используется для добавления или удаления элементов в или из массива. Первый аргумент определяет начальную позицию для добавления или удаления элементов, второй опциональный аргумент — количество удаляемых элементов. Каждый последующий аргумент добавляется в массив:
let arrayOriginal1 = [1, 2, 3, 4, 5]
let arrayOriginal2 = [1, 2, 3, 4, 5]
let arrayOriginal3 = [1, 2, 3, 4, 5]
let array1 = arrayOriginal1.splice(0, 2) // возвращается [1, 2]; исходный массив = [3, 4, 5]
let array2 = arrayOriginal2.slice(3) // возвращается [4, 5]; исходный массив = [1, 2, 3]
let array3 = arrayOriginal3.slice(3, 1, 'a', 'b', 'c') // возвращается [4]; исходный массив = [1, 2, 3, 'a', 'b', 'c']
Обратите внимание, что метод splice () модифицирует исходный массив и возвращает массив извлеченных элементов.
JSR
MDN
В чем разница между slice () и splice ()?
Главные отличия состоят в следующем:
JSR
MDN — Slice
MDN — Splice
Как сравниваются объекты (objects) и карты (maps)?
Объекты похожи на карты в том, что оба позволяют устанавливать ключи для значений, извлекать значения, удалять ключи и определять наличие значения по ключу. По этой причине объекты раньше использовались как карты. Однако между ними существуют некоторые отличия, которые делают использование карт более предпочтительным в определенных случаях.
- Ключами объекта могут быть только строки и символы, а ключами карты — любые значения, включая функции и объекты
- Ключи карты упорядочены, а ключи объекта нет. Поэтому при итерации ключи карты возвращаются в порядке их добавления
- Вы можете получить размер карты с помощью свойства size, а количество свойств объекта определяется вручную
- Карта является итерируемой сущностью, т.е. перебираемой по умолчанию, а для итерации по объекту необходимо сначала каким-то образом получить его ключи, а затем их перебрать
- При использовании объекта в качестве карты следует помнить о том, что любой объект имеет прототип, поэтому собственные ключи такой карты могут пересекаться с вашими ключами. Поэтому для создания карты-объекта следует использовать Object.create (null), но сейчас такой способ используется крайне редко
- Объект уступает карте в плане производительности, когда речь идет о быстром добавлении/удалении ключей
JSR
MDN
В чем разница между операторами »==» и »===»?
JavaScript предоставляет два способа для сравнения значений: строгое (===, !==) и абстрактное (==, !==). При строгом сравнении значения сравниваются как есть, а при нестрогом при необходимости осуществляется неявное преобразование (приведение) типов значений. Строгие операторы используют следующие правила для сравнения различных типов значений:
- Две строки являются строго равными, когда они имеют одинаковый набор символов, одинаковую длину и одинаковые символы на одних и тех же позициях
- Два числа являются строго равными, если равны их значения. Существует два особых случая:
- NaN не равно ничему, включая NaN
- Положительный и отрицательный нули равны друг другу
Логические значение являются строго равными, когда оба являются истинными или ложными, т.е. true или false
Два объекта являются строго равными, если ссылаются на один и тот же объект (место в памяти)
null === undefined возвращает false, но null == undefined возвращает true
Несколько примеров:
0 == false // true
0 === false // false
1 == "1" // true
1 === "1" // false
null == undefined // true
null === undefined // false
'0' == false // true
'0' === false // false
[] == [] // или
[] === [] // false, ссылаются на разные места в памяти
{} == {} // или
{} === {} // false, ссылаются на разные места в памяти
JSR
MDN
Что такое лямбда- или стрелочные функции?
Стрелочные функции — это сокращенный способ записи функциональных выражений. Они не имеют собственных this, arguments, super и new.target. Эти функции служат хорошей альтернативой функциям, не имеющим методов, но не могут использоваться как конструкторы.
JSR
MDN
Почему функции называют объектами первого класса?
В JavaScript функции являются объектами первого класса. Это означает, что функции могут использоваться как обычные переменные.
Например, функция может передаваться в качестве аргумента другой функции, возвращаться как значение из другой функции и присваиваться переменной. В следующем примере функция присваиваивается обработчику:
const handler = () => console.log('Это функция обработки клика')
document.addEventListener('click', handler)
JSR
MDN
Что такое функция первого порядка?
Функция первого порядка — это функция, которая не принимает другую функцию в качестве аргумента и не возвращает функцию как значение:
const firstOrder = () => console.log('Я - функция первого порядка')
JSR
MDN
Что такое функция высшего порядка?
Функция высшего порядка — это функция, которая принимает другую функцию в качестве аргумента или возращает другую функцию как значение:
const firstOrderFunc = () => console.log('Я - функция первого порядка')
const higherOrder = returnFirstOrderFunc => returnFirstOrderFunc()
higherOrder(firstOrderFunc)
JSR
MDN
Что такое унарная функция?
Унарная функция (функция-монада) — это функция, принимающая только один аргумент:
const unaryFunction = a => console.log(a + 10) // прибавляем 10 к переданному аргументу и выводим результат в консоль
JSR
MDN
Что такое каррирование (currying)?
Каррирование — это процесс преобразования функции с несколькими параметрами в несколько функций с одним параметром. Данный процесс назван в четь математика Хаскелла Карри. Каррирование превращает одну n-арную функцию в несколько унарных функций (уменьшает арность функции):
const multiArgF = (a, b, c) => a + b + c
const curryUnaryF = a => b => c => a + b + c
curryUnaryF(1) // возвращает функцию: b => c => 1 + b + c
curryUnaryF(1)(2) // возвращает функцию: c => 3 + c
curryUnaryF(1)(2)(3) // возвращает число 6
Каррирование применяется в целях обеспечения возможности повторного использования кода и создания композиции из функций.
JSR
Что такое чистая функция?
Чистая функция — это функция, возвращаемое значение которой зависит только от передаваемых аргументов, без побочных эффектов. Проще говоря, если вы вызывается функцию n раз с n аргументами, и функция всегда возвращает одно и тоже значение, значит, она является чистой:
// не чистая
let numberArray = []
const impureAddNumber = number => numberArray.push(number)
// чистая
const pureAddNumber = number => argNumberArray => argNumberArray.concat([number])
// отображаем результаты
console.log(impureAddNumber(6)) // 1
console.log(numberArray) // [6]
console.log(pureAddNumber(7)(numberArray)) // [6, 7]
console.log(numberArray) // [6]
В приведенном примере impureAddNumber не является чистой функцией, поскольку метод push () возвращает новую длину массива, которая не зависит от передаваемого аргумента. Вторая функция является чистой, поскольку метод concat () объединяет два массива без побочных эффектов и возвращает новый массив. Чистые функции важны для юнит-тестирования и не нуждаются во внедрении зависимостей. Отсутствие побочных эффектов повышает надежность приложения за счет более слабых связей между его элементами. Одним из воплощений данного принципа является концепция неизменности (иммутабельности), представленная в ES6, и заключающаяся в предпочтении const перед let.
JSR
MDN
Для чего используется ключевое слово «let»?
Ключевое слово «let» служит для объявления локальной переменной, имеющей блочную область видимости. Область видимости такой переменной органичена блоком, оператором или выражением, в котором она используется. Переменные, объявленные с помощью ключевого слова «var», имеют глобальную область видимости или область видимости функции, в которой они определены:
let counter = 30
if (counter === 30) {
let counter = 31
console.log(counter) // 31
}
console.log(counter) // 30 (переменная counter, объявленная в блоке, здесь не существует)
JSR
MDN
В чем разница между let и var?
Основные отличия состоят в следующем:
Несколько примеров:
function userDetails(username) {
if (username) {
console.log(salary)
console.log(age)
let age = 30
var salary = 10000
}
console.log(salary) // 10000 (область видимости функции)
console.log(age) // ошибка: age не определена (блочная область видимости)
}
JSR
MDN — let
MDN — var
Почему в качестве ключевого слова было выбрано слово «let»?
Let (пусть) — это математический оператор, который использовался ранними языками программирования, такими как Scheme и Basic. В настоящее время let используется большим количеством языков программирования, так что данное слово является наиболее близкой альтернативой сокращению «var» (variable — переменная).
JSR
MDN
Как переопределить переменную в блоке switch?
Если вы попытаетесь переопределить переменную, объявленную с помощью ключевого слова «let» в блоке switch, то получите ошибку:
let counter = 1
switch(x) {
case 0:
let name
break
case 1:
let name // синтаксическая ошибка (SyntaxError)
break
}
Для решения данной задачи необходимо создать новый блок внутри case — новую лексическую область видимости:
let counter = 1
switch(x) {
case 0: {
let name
break
}
case 1: {
let name
break
}
}
JSR
MDN
Что такое временная мертвая зона?
При попытке доступа к переменным, объявленным с помощью ключевого слова «let» или «const» (но не «var»), до их определения (т.е. до присваивания им значения внутри текущей области видимости) будет выброшено исключение ReferenceError (ошибка ссылки). Другими словами, временной метрвой зоной называется время между созданием контекста (области видимости) переменной и ее определением:
function someMethod () {
console.log(counter1) // undefined
console.log(counter2) // ReferenceError
var counter1 = 1
const counter2 = 2
}
MDN
Что такое немедленно вызываемое функциональное выражение (IIFE, Immediately Invoked Function Expression)?
IIFE — это функция, которая вызывается сразу после определения. Синтаксис такой функции может выглядеть так (один из вариантов, наиболее распространенный):
(function () {
// код
})()
// или, если речь идет о стрелочной функции
(() => {
// код
})()
Главная причина использования IIFE заключается в обеспечении приватности переменных, поскольку доступ к переменным, объявленным внутри IIFE, нельзя получить из внешнего окружения:
(function () {
var message = 'IIFE'
console.log(message)
})()
console.log(message) // ошибка: message не определена
JSR
MDN
В чем заключаются преимущества использования модулей?
Среди прочего, можно назвать следующее:
- Повышение читаемости и облегчение поддержки кода
- Возможность повторного использования кода
- Сохранение чистоты глобального пространства имен
JSR
MDN
Что такое запоминание или мемоизация?
Мемоизация — это способ повышения производительности функции за счет сохранения в кэше ранее полученных результатов выполнения этой функции. При каждом вызове функции переданный ей аргумент становится индексом кэша. Если данные имеются в кэше, они возвращаются без повторного выполнения функции. В противном случае, функция выполняется, а результат записывается в кэш:
const memoizAddition = () => {
let cache = {}
return value => {
if (value in cache) {
console.log('Получение данных из кэша')
return cache[value] // в данном случае, cache.value не может быть использовано в качестве названия свойства, поскольку названия свойств в JS не могут начинаться с числа. Поэтому используется скобочная нотация
} else {
console.log('Результат вычисляется')
let result = value + 20
cache[value] = result
return result
}
}
}
// возвращаем функцию из memoizAddition
const addition = memoizAddition()
console.log(addition(20)) // Результат вычисляется 40
console.log(addition(20)) // Получения данных из кэша 40
Что такое поднятие переменных (hoisting)?
Поднятие — это процесс перемещения переменных и функциональных выражений в начало их области видимости перед выполнением кода. Запомните: поднимаются только сами переменные и выражения, а не их инициализация (т.е. поднимается объявление переменной, а не присваивание ей значения):
console.log(message) // undefined
var message = 'Хойстинг'
Для компилятора данный код выглядит так:
var message
console.log(message)
message = 'Хойстинг'
JSR
MDN
Что такое класс?
Классы, представленные в ES6, являются синтаксическим сахаром (оберткой, абстракцией или надстройкой) для протипного наследования (для прототипа функции-конструктора). Пример функции-конструктора:
function Bike(model, color) {
this.model = model
this.color = color
}
Bike.prototype.getDetails = function () {
return 'Эта ' + this.model + ' велосипеда имеет ' + this.color + ' цвет.'
}
Тот же пример с использованием класса:
class Bike {
constructor (color, model) {
this.color = color
this.model = model
}
getDetails () {
return `Эта ${this.model} велосипеда имеет ${this.color} цвет.`
}
}
JSR
MDN
Что такое замыкание?
Замыкание — это комбинация функции и ее лексического окружения. Проще говоря, замыкание — это когда внутренняя функция имеет доступ к переменным, объявленным во внешней функции. Замыкание имеет цепочку из трех областей видимости:
- Собственная область видимости
- Область видимости внешней функции
- Глобальная область видимости
function Welcome (name) {
var greetingInfo = function (message) {
console.log(message + ' ' + name)
}
return greetingInfo
}
var myFunction = Welcome('Иван')
myFunction('Добро пожаловать, ') // Добро пожаловать, Иван
myFunction('Привет, ') // Привет, Иван
JSR
MDN
Что такое модуль?
Модули — это небольшие части независимого переиспользуемого кода, лежащие в основе многих шаблонов проектирования. Большинство модулей экспортируется в качестве объектов, функций или конструкторов.
JSR
MDN
Зачем нужны модули?
Среди прочего, можно назвать следующее:
- Повышение читаемости и облегчение поддержки кода
- Возможность повторного использования кода
- Сохранение чистоты глобального пространства имен
JSR
MDN
Что такое область видимости?
Область видимости определяет доступность переменных, функций и объектов в разных местах кода во время его выполнения. Другими словами, область видимости — это видимость переменных и других ресурсов в текущем контексте выполнения кода.
MDN
Что такое сервис-воркер (service worker)?
Сервис-воркер — это скрипт, который выполняется независимо от веб-страницы, на которой он был запущен, и действий пользователя. Фактически сервис-воркер выполняет роль прокси-сервера между приложением и браузером. Основными возможностями сервис-воркеров является следующее: обеспечение работы приложения в режиме офлайн, периодическая фоновая синхронизация, пуш-уведомления, перехват и обработка сетевых запросов и программное управление кэшем.
MDN
Как взаимодействовать с объектной моделью документа (Document Object Model, DOM) с помощью сервис-воркеров?
Сервис-воркеры не имеют прямого доступа к DOM. Однако, они могут взаимодействовать со страницей через интерфейс postMessage, а страница — может изменять DOM.
MDN — ServiceWorker
MDN — postMessage
Как повторно использовать информацию при перезапуске сервис-воркера?
Одной из проблем сервис-воркеров является то, что их выполнение прекращается, когда они не используются, и повторно запускается при необходимости. Это не позволяет добавлять обработчики событий fetch и message глобально. Для повторного использования информации необходимо обеспечить взаимодействие сервис-воркеров с индексированной базой данных (IndexedDB) или локальным хранилищем (local storage).
MDN
Что такое индексированная база данных (IndexedDB)?
IndexedDB — это низкоуровневый прикладной интерфейс для хранения большого объема структурированных данных, включая файлы и blobs, на стороне клиента. Данный интерфейс использует индексы для высокопроизводительного поиска данных.
JSR
MDN
Что такое веб-хранилище (Web Storage)?
Веб-хранилище — это интерфейс, позволяющий хранить данные в виде пар ключ/значение локально, т.е. в браузере пользователя, более удобным способом, чем при использовании куки. Веб-хранилище предоставляет два механизма хранения данных:
- Локальное хранилище (local stotage) — хранит данные текущего пользователя неограниченное количество времени
- Сессионное хранилище (session storage) — хранит данные на протяжении текущей сессии, т.е. при закрытии вкладки браузера данные будут потеряны
JSR
MDN
Что такое postMessage?
postMessage — это способ коммуникации разных источников объекта window (например, страницы и генерируемого ею поп-апа (всплывающего окна) или страницы и встроенного в нее iframe). Обычно, скрипты одной страницы не имеют доступа к другой странице, если данная страница следует политике общего происхождения или, как еще говорят, одного источника (источники должны иметь одинаковый протокол, хост и порт).
Что такое куки (cookie)?
Куки — это небольшой фрагмент данных, который сохраняется на компьютере пользователя для последующего использования браузером. Куки сохраняются в виде пар ключ/значение:
document.cookie = 'username=John'
JSR
MDN
Зачем нужны куки?
Куки используются для сохранения информации о пользователе (не рекомендуется использовать для хранения конфиденциальной информации). Обычно, данный процесс состоит из двух этапов:
- При первом посещении страницы профиль пользователя сохраняется в куки
- При повторном посещении страницы профиль пользователя извлекается из куки
JSR
MDN
Какими возможностями обладают куки?
По умолчанию, куки удаляются при закрытии браузера, однако это можно изменить, установив время жизни (expires) (в формате UTC):
document.cookie = 'username=John; expires=Sat, 5 Sep 2020 12:00:00 UTC'
По умолчанию, куки принадлежат текущей странице, однако это также можно изменить, установив путь (path):
document.cookie = 'username=John; path=/services'
JSR
MDN
Как удалить куки?
Удалить куки можно, установив прошедшее время в качестве времени жизни. В этом случае не нужно определять значение куки:
document.cookie = 'username=; expires=Fri, 05 Jun 2020 00:00:00 UTC; path=/;'
Обратите внимание, что в данном случае необходимо определить путь для удаления правильного куки. Некоторые браузеры не позволяют удалить куки без указания этого параметра.
JSR
MDN
В чем разница между куки, локальным и сессионным хранилищами?
Основные отличия состоят в следующем:
JSR — Куки
MDN — Cookie
JSR — LocalStorage, SessionStotage
MDN — Web Storage
В чем главное отличие между локальным и сессионным хранилищами?
Локальное хранилище — это тоже самое, что и сессионное хранилище, за исключением того, что в первом данные сохраняются даже при закрытии и перезагрузке браузера, а во втором данные удаляются по окончании сессии страницы.
JSR
MDN
Как получить доступ к веб-хранилищу?
Объект window предоставляет объекты WindowLocalStorage и WindowSessionStorage, которые имеют свойства localStorage и sessionStorage, соответственно. Эти свойства создают экземпляр объекта Storage, с помощью которого можно записывать, извлекать и удалять данные для определенного домена и типа хранилища (сессионное или локальное):
// сохраняем данные
localStorage.setItem('data', document.querySelector('.data').value)
// получаем данные
localStorage.getItem('data')
JSR
MDN
Какие методы предоставляет сессионное хранилище?
Сессионное хранилище предоставляет методы для чтения, записи и удаления данных:
// записываем данные
sessionStorage.setItem('key', 'value')
// получаем данные
const data = sessionStorage.getItem('key')
// удаляем определенные данные
sessionStorage.removeItem('key')
// удаляем все данные
sessionStorage.clear()
JSR
MDN
Какое событие возникает при работе с веб-хранилищем?
При изменении хранилища в контексте другого документа возникает событие storage:
window.onstorage = function () {}
Пример обработки данного события:
window.onstorage = ev => {
console.log(`${ev.key} был изменен.\n Старое значение: ${ev.oldValue}.\n Новое значение: ${ev.newValue}.`)
}
Данное событие, в частности, позволяет реализовать своего рода чат.
JSR
MDN
Для чего используется веб-хранилище?
Веб-хранилище является более безопасным и может хранить больший объем данных, чем куки, что не влияет на производительность. Кроме того, данные не отправляются на сервер (в случае с куки данные включаются в заголовки запроса и ответа при каждом обращении клиента к серверу). Поэтому такой способ хранения данных является более предпочтительным, чем куки.
JSR
MDN
Как определить поддержку веб-хранилища браузером?
Перед использованием веб-хранилища рекомендуется проверить поддержку данного интерфейса браузерами:
if (typeof(Storage) !== 'undefined') {
// код
} else {
// веб-хранилище не поддерживается
}
// или
if ('Storage' in window) {
console.log('ok')
} else {
console.warn('!ok')
}
По данным CanIUse поддержка веб хранилища на сегодняшний день составляет 98%.
JSR
MDN
Как определить поддержку сервис-воркеров браузером?
Перед использованием сервис-воркеров рекомендуется проверить поддержку данного интерфейса браузерами:
if (typeof(Worker) !== undefined) {
// код
} else {
// сервис-воркеры не поддерживаются
}
// или
if ('Worker' in window) {
console.log('ok')
} else {
console.warn('!ok')
}
По данным CanIUse поддержка сервис-воркеров на сегодняшний день составляет 94,5%.
MDN
Приведите пример веб-воркера
Для использования веб-воркера необходимости сделать следующее.
Создать файл для воркера, например, counter.js:
let i = 0
function timedCount() {
i = i + 1
postMessage(i)
setTimeout('timedCount()', 500)
}
timedOut()
Метод postMessage () используется для отправки сообщения странице.
Создаем объект воркера:
const w = new Worker('counter.js')
После этого обрабатываем получение сообщений от воркера:
w.onmessage = ev => document.querySelector('.message').textContent = ev.data
Воркер будет продолжать обрабатывать событие message даже после того, как внешний скрипт выполнит свою работу, поэтому нужно принудительно его остановить:
w.terminate()
Если присвоить воркеру значение undefined, код можно будет использовать повторно:
w = undefined
MDN
Назовите ограничения веб-воркеров по работе с DOM
Поскольку веб-воркеры создаются в отдельном файле, они не имеют доступа к следующим объектам:
- window
- Document
- Родительский объект, т.е. объект, запустивший воркер
MDN
Что такое промис (обещание, promise)?
Промис — это объект, который либо выполняется с некоторым значением, либо отклоняется с ошибкой. Разрешение промиса происходит либо после истечения определенного времени, либо после возникновения определенного события. Промис может иметь одно из трех состояний: находится в режиме ожидания (pending), выполнен (fulfilled), отклонен (rejected).
Синтаксис промиса:
const promise = new Promise((resolve, reject) => {
// код
})
// или, когда мы уверены, что промис выполнится успешно
const promise = Promise.resolve(value)
promise.then(value => {
// код
})
Пример использования промиса:
const promise = new Promise(resolve => {
const timer = setTimeout(() => {
resolve('Привет от промиса!')
clearTimeout(timer)
}, 5000);
}, reject => {
reject('Что-то пошло не так!')
})
promise
.then(value => console.log(value))
.catch(error => console.error(error))
.finally(() => console.log('Мы закончили')) // в консоль будет выведено "Привет от промиса!" через 5 секунд и "Мы закончили"
Алгоритм выполнения промиса:
JSR
MDN
Зачем нужны промисы?
Промисы используются для работы с асинхронным кодом. Они представляют собой альтернативу функциям обратного вызова, позволяя избежать так называемого «ада колбеков», делают код более чистым и читаемым.
JSR
MDN
Назовите три возможных состояния промиса
У промисов существует три состояния:
- Ожидание: стадия перед началом выполнения операции
- Выполнен: успешное завершение операции
- Отклонен: неудачное выполнение операции. Выбрасывается исключение.
JSR
MDN
Что такое функция обратного вызова (колбек)?
Колбек — это функция, которая передается другой функции в качестве аргумента. Данная функция (внутренняя) вызывается внутри родительской (внешней) для выполнения определенной операции. Рассмотрим простой пример:
function callback(name) {
alert(`Привет, ${name}!`)
}
function outer(cb) {
const name = prompt('Пожалуйста, введите свое имя')
cb(name)
}
outer(callback)
В приведенном примере функция outer запрашивает имя пользователя и записывает его в переменную name. Затем данная функция передает name функции callback, которая выводит приветствие с именем пользователя.
JSR
MDN
Зачем нужны колбеки?
Колбеки нужны, поскольку JavaScript — язык, управляемый событиями. Это означает, что вместо ожидания ответа на запрос или обработки определенного события, JS продолжает реагировать на другие события. Рассмотрим пример, в котором одна функция обращается к интерфейсу, а другая — выводит сообщение в консоль:
function first () {
// имитируем обращение к API
setTimeout(() => console.log('Вызвана первая функция.'), 1000)
}
function second () {
console.log('Вызвана вторая функция.')
}
first()
second()
// сначала будет выведено "Вызвана вторая функция.", затем "Вызвана первая функция."
Как видите, JS не ожидает завершения первой функции, а продолжает выполнение кода. Поэтому колбеки используются для имитации асинхронности, предотвращая блокировку основного потока выполнения программы.
JSR
MDN
Что такое ад колбеков?
Ад колбеков — это антипаттерн, когда множество функций обратного вызова вложены друг в друга для реализации асинхронной логики. Такая структура кода сложна для восприятия и поддержки. Это выглядит примерно так:
function first () {
return function second () {
return function third () {
return function fourth () {
// и т.д.
}
}
}
}
Такой подход к написанию кода считается плохой практикой, кроме случаев каррирования (включая debounce и throttle) или частичного применения функций.
JSR
MDN
Что такое события, отправленные сервером (server-sent events, SSE)?
События, отправленные сервером — это технология пуш-уведомлений, позволяющая браузерам получать от сервера обновленные данные через HTTP-соединение без отправки запроса. Это один из способов коммуникации клиента и сервера, когда сообщения отправляются только сервером. Данная технология используется для обновления Facebook/Twitter, цен в магазинах, новостных лент и т.д.
JSR
MDN
Как получать сообщения (уведомления или события), отправленные сервером?
Для этого используется объект «EventSource»:
if('EventSource' in window) {
const source = new EventSource('sse_generator.js')
source.onmessage = event => document.querySelector('output').innerHTML += event.data + ''
}
JSR
MDN
Как проверить поддержку SSE браузером?
Это делается так:
if (typeof EventSource !== 'undefined') {
// код
} else {
// SSE не поддерживается
}
// или
('EventSource' in window)
? console.log('ok')
: console.warn('!ok')
По данным CanIUse на сегодняшний день SSE поддерживается 95% браузеров.
JSR
MDN
Какие события возникают при работе с SSE?
Вот список этих событий:
JSR
MDN
Назовите основные правила работы с промисами
Основными правилами работы с промисами является следующее:
- Промис — это объект, содержащий встроенный или стандартный метод then ()
- Стадия ожидания промиса, обычно, заканчивается стадией его выполнения или отклонения
- Состояние выполненного или отклоненного промиса не должно меняться после его разрешения
- После разрешения промиса его значение также не должно меняться
JSR
MDN
Что такое колбек в колбеке?
Вы можете вкладывать колбеки друг в друга с целью последовательного выполнения определенных операций:
loadScript('/script1.js', script => {
console.log(`Скрипт ${script} загружен.`)
loadScript('/script2.js', script => {
console.log(`Скрипт ${script} загружен.`)
loadScript('/script3.js', script => {
console.log(`Скрипт ${script} загружен.`)
})
})
})
JSR
MDN
Что такое цепочка из промисов?
Последовательное выполнение нескольких асинхронных задач с помощью промисов называется цепочкой промисов. Рассмотрим пример:
new Promise((resolve, reject) => {
const id = setTimeout(() => {
resolve(1)
clearTimeout(id)
}, 1000)
}).then(result => {
console.log(result) // 1
return result * 2
}).then(result2 => {
console.log(result2) // 2
return result2 * 3
}).then(result3 => {
console.log(result3) // 6
}).catch(error => console.err