JS Flashcards

1
Q

Что такое приведение или преобразование типов (type coercion)

A

Приведение (или преобразование) типов — это процесс преобразования значения из одного типа в другой.
В JavaScript преобразование типов может быть явным и неявным. Преобразование с помощью функции-конструктора является явным. Пример:
const num = Number("123");
const boolValue = Boolean(0);

Так как JavaScript — это язык со слабой типизацией, значения в нем могут быть конвертированы между различными типами автоматически - это называется неявным преобразованием типов. Пример:
if (value) {…};
const result = "3" * 2;
const comparison = 5 == "5";

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Что такое apply?

A

apply() - это метод встроенного объекта функции в JavaScript. Он позволяет вызвать функцию с заданным значением this и аргументами, переданными в виде массива (или массивоподобного объекта).

function.apply(thisArg, [argsArray])

Пример использования apply():
function greeting(greet, punctuation) {
return ${greet}, ${this.name}${punctuation};
}
const person = {
name: ‘Alice’

console.log(greeting.apply(person, [‘Hello’, ‘!’]));
// Вывод: Hello, Alice!

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Как отфильтровать массив?

A

Метод filter(), который позволяет отфильтровать элементы массива на основе заданного условия и создать новый массив с соответствующими элементами.

Формат метода filter():
array.filter(callback(element[, index[, array]])[, thisArg])

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evenNumbers = numbers.filter((num) => {
return num % 2 === 0;
});
console.log(evenNumbers); // Выводит: [2, 4, 6, 8, 10]

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Базовые операторы в JS

A

Операнд – то, к чему применяется оператор. Например, в умножении 5 * 2 есть два операнда: левый операнд равен 5, а правый операнд равен 2. Иногда их называют «аргументами» вместо «операндов».

Унарным называется оператор, который применяется к одному операнду. Например, оператор унарный минус “-“ меняет знак числа на противоположный

Бинарным называется оператор, который применяется к двум операндам. Тот же минус существует и в бинарной форме.

Поддерживаются следующие математические операторы:
Сложение +,
Вычитание -,
Умножение *,
Деление /,
Взятие остатка от деления %,
Возведение в степень **.
Если бинарный оператор ‘+’ применить к строкам, то он их объединяет в одну - это называется конкатенация.
Унарным плюсом можно быстро привести операнд к числу.

Одной из наиболее частых числовых операций является увеличение или уменьшение на единицу.
Инкремент ++ увеличивает переменную на 1
Декремент – уменьшает переменную на 1
Операторы ++ и – могут быть расположены не только после, но и до переменной. Префиксная форма возвращает новое значение, в то время как постфиксная форма возвращает старое (до увеличения/уменьшения числа).
Когда оператор идёт после переменной — это «постфиксная форма»: counter++.
«Префиксная форма» — это когда оператор идёт перед переменной: ++counter.
Оператор «запятая» предоставляет нам возможность вычислять несколько выражений, разделяя их запятой ,. Каждое выражение выполняется, но возвращается результат только последнего.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Типы данных в JS

A

Есть восемь основных типов данных в JavaScript.

  1. Числовой тип данных (number) представляет как целочисленные значения, так и числа с плавающей точкой.
    Кроме обычных чисел, существуют так называемые «специальные числовые значения», которые относятся к этому типу данных: Infinity, -Infinity (можем получить в результате деления на ноль) и NaN (NaN означает вычислительную ошибку. Это результат неправильной или неопределённой математической операции).
  2. BigInt числовой примитив, который позволяет использовать большие числа с высокой точностью. Чтобы создать значение типа BigInt, необходимо добавить n в конец числового литерала: const bigInt = 1234567890123456789012345678901234567890n;
  3. Строка (string) в JavaScript должна быть заключена в кавычки. Обратные кавычки имеют расширенную функциональность - позволяют нам встраивать выражения в строку, заключая их в ${…}.
  4. Булевый тип (boolean) может принимать только два значения: true (истина) и false (ложь).
    Такой тип, как правило, используется для хранения значений да/нет: true значит «да, правильно», а false значит «нет, не правильно».
  5. null формирует отдельный тип, который содержит только значение null. Это специальное значение, которое представляет собой «ничего», «пусто» или «значение неизвестно».
  6. Специальное значение undefined означает, что «значение не было присвоено». Если переменная объявлена, но ей не присвоено никакого значения, то её значением будет undefined.
  7. Symbol примитивный тип данных, использующийся для создания уникальных идентификаторов. Даже если символы имеют одно и то же имя, это – разные символы.
  8. Тип object (объект) не является примитивным типом данных. Объекты используются для хранения коллекций различных значений и более сложных сущностей. Объект может быть создан с помощью фигурных скобок {…} с необязательным списком свойств. Свойство – это пара «ключ: значение», где ключ – это строка (также называемая «именем свойства»), а значение может быть чем угодно.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Расскажи про тип данных число.

A

Тип данных “число” (number) в JavaScript представляет числовые значения. Также для этого типа данных существуют специальные значения Infinity, -Infinity и NaN (Not a Number).

В диапазоне от -2 в 53 степени до 2 в 53 степени, числа могут быть представлены точно.

Для типа данных “число” определены стандартные арифметические операции, такие как сложение (+), вычитание (-), умножение (*), деление (/), взятие остатка от целочисленного деления (%), возведение в степень (**), а также операции сравнения (>, <, >=, <=, ==, ===, !=, !==).

Числа также могут быть представлены в различных системах исчисления, таких как двоичная, восьмеричная или шестнадцатеричная системы. Чтобы перевести число в какую-то систему исчисления можно использовать метод toString:
let decimalNumber = 10;
let binaryNumber = decimalNumber.toString(2);
console.log(binaryNumber); // Выводит: 1010

Стандарт IEEE-754 определяет три специальных значения. Эти значения принадлежат типу number, но не работают, как обычные числа:
- бесконечность Infinity;
- минус бесконечность -Infinity;
- не число (not a number) NaN.

Значение NaN используется, чтобы сообщить об операции, результатом которой оказалось не число. В JavaScript существует пять операций, которые могут вернуть NaN:
1. ошибка парсинга числа (например, при попытке превратить строку в число parseInt('привет')).
2. результат математической операции не находится в полей действительных чисел (например, взятие корня от -1).
3. один из операндов в арифметической операции — NaN (5 + NaN)
4. результат арифметической операции не определён для переданных операндов undefined + undefined.
5. арифметическая операция со строкой, кроме сложения 'привет' * 5

Согласно спецификации, NaN не равен самому себе. Для проверки на NaN пользуйтесь функцией Number.isNaN(), которая возвращает true если переданное значение — NaN
Number.isFinite() проверяет и на Infinity, и на Nan - на них возвращается false.

Для округления, взятия корней и других математических операций в JavaScript существует отдельный модуль Math.
round() — округление по обычным правилам;
floor() — округление вниз;
ceil() — округление вверх;
trunc() — отбрасывание дробной части, не обращая внимания на знак аргумента.

Сам по себе примитивный тип «число» не имеет методов. Когда происходит вызов метода у числа, оно автоматически оборачивается в специальную обёртку Number, которая и содержит методы:
- проверки на специальные значения isNaN(), isFinite().
- toString(2) переводит в строку с определенной системой исчисления
- toFixed() форматирует число, обрезая значения после запятой. Возвращает строку, а не число.
Если после округления нужно производить другие арифметические операции, то лучше распарсить число с помощью parseFloat().

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Тип данных BigInt

A

Тип большого целого BigInt — примитивный тип, который представляет целые числа больше 2 в степени 53 - 1. Эти числа уже не помещаются в стандартный примитив «число».

Создать BigInt можно двумя способами.
1️⃣ Добавить суффикс n в конец записи числа:
const biggy = 9997000254740991n
2️⃣ Вызвать конструктор BigInt:
const alsoBig = BigInt(9997000254999999)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Тип данных строка

A

Строки представляют собой последовательность символов. Созданная строка является иммутабельной (immutable) и не может быть изменена.

Есть несколько способов создать строку:
- одинарными кавычками ‘;
- двойными кавычками “;
- шаблонной строкой через обратный апостроф ` - бэктик.

Если в записи одинарными кавычками нужно поставить апостроф, то символ экранируют обратным слэшем . Так мы даём JavaScript понять, что это просто символ, а не закрывающая кавычка.

Строки можно сравнивать между собой, для сравнения используется лексикографический порядок. Это означает, что первые символы алфавита считаются меньше последних.
Алгоритм посимвольно сравнивает строки до первого несовпадения, либо пока не закончится одна из строк.
console.log(‘А’ < ‘Я’) // true
console.log(‘Кот’ > ‘Код’) // true
console.log(‘Код’ < ‘Кодер’) // true
console.log(‘Код’ === ‘Код’) // true
Сравнение учитывает регистр букв, если необходимо регистронезависимое сравнение, то обе строки приводятся к верхнему или нижнему регистру с помощью методов toUpperCase или toLowerCase.

Сам по себе примитивный тип «строка» не имеет методов. Когда происходит вызов метода, оно автоматически оборачивается в специальную обёртку, которая и содержит методы.
length: Возвращает длину строки.
toUpperCase(): Возвращает новую строку, содержащую все символы исходной строки в верхнем регистре.
toLowerCase(): Возвращает новую строку, содержащую все символы исходной строки в нижнем регистре.
substring(startIndex, endIndex): Возвращает подстроку, которая начинается со значения индекса startIndex до значения индекса endIndex (не включительно).
const str = "Hello, world!"; console.log(str.substring(0, 5)); // Вывод: "Hello"
indexOf(substring): Возвращает индекс первого вхождения подстроки substring в строке. Если подстрока не найдена, возвращает -1.
replace(oldSubstring, newSubstring): Заменяет первое вхождение подстроки oldSubstring на подстроку newSubstring и возвращает новую строку.
const str = “Hello, world!”;
console.log(str.replace(“world”, “JavaScript”)); // Вывод: “Hello, JavaScript!” **split(separator)**: Разбивает строку на массив подстрок с использованием указанного разделителя separator`.
trim(): Удаляет пробельные символы с начала и конца строки и возвращает новую строку.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Тип данных Boolean

A

Логический или булев тип boolean может принимать лишь истинное (true) и ложное (false) значения. Значения этого типа используются в условных выражениях.

Создать булевое значение можно несколькими способами.
- явно указать значение, используя ключевые слова true и false:
const truthyValue = true // «Истина»
- использовать метод Boolean:
const falsyValue = Boolean(‘’) // «Ложь»
- использовать выражения, значениями которых будут «истина» или «ложь».
const anotherTruthy = 4 < 5
- Если вызвать одинарное ! или двойное отрицание !!, можно быстро привести любое выражение к логическому типу.

Обычно логическим переменным дают названия, начинающиеся с английских глаголов is, should, does, can и подобных.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Тип данных Undefined

A

**Undefined — это примитивный тип данных, состоящий из одного значения undefined. Оно используется, чтобы обозначить неопределённое значение. **
- JavaScript автоматически устанавливает значение undefined объявленным переменным, которые не были проинициализированы значением.
- JavaScript автоматически устанавливает значение undefined в аргумент функции, если значение не передали при вызове
hello('Витя') // Привет, Витя hello() // Привет, undefined

Вручную установленное undefined используют, чтобы обозначить неизвестное значение:
const person = { name: 'Пётр', lastName: 'Романов', age: undefined }

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Тип данных Null

A

Null — это примитивный тип данных, который состоит из единственного значения null. Значение null используют, когда нужно обозначить намеренное отсутствие значения.

null обозначает понятия «отсутствует», «ничего», «пусто» или «значение неизвестно». Оно всегда явно задаётся программистом, JavaScript автоматически не устанавливает его.
В JavaScript null используется только для обозначения конца цепочки прототипов, чтобы показать, что следующий прототип отсутствует.
В языке существует похожий примитив undefined, он обозначает, что значение ещё не установлено. Их можно легко спутать, потому что оба обозначают отсутствие значения. Разница состоит в том, что null обозначает намеренное отсутствие, а undefined — неявное.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Тип данных Symbol

A

Символ (Symbol) — примитивный тип, значения которого создаются с помощью вызова функции Symbol. Каждый созданный символ уникален. Символы могут использоваться в качестве имён свойств в объектах. Символьные свойства могут быть прочитаны только при прямом обращении и не видны при
обычных операциях.

Для создания символа нужно вызвать функцию Symbol:
const sym = Symbol() const symTwo = Symbol() console.log(sym === symTwo) // false

Символы используются для создания скрытых свойств объектов. В отличие от свойств, ключом которых является строка, символьные свойства может читать только владелец символа. Скрытые свойства не видны при его обходе с помощью for…in. Это может пригодиться, когда необходимо добавить свойства объекту, который могут модифицировать другие части программы. Таким образом только вы сможете читать созданное свойство, а гарантия уникальности символов гарантирует и отсутствие конфликтов имён.

Созданный символ уникален, но как быть, если он нужен в нескольких местах программы? Для решения этой проблемы существует глобальный реестр символов, он хранит символы по строковым ключам. При обращении по ключу всегда будет возвращаться один и тот же символ.

Работа с реестром символов организована с помощью двух методов:
- Symbol.for(ключ) — возвращает символ, хранящийся по ключу. Если символа ещё не существует, он создаётся автоматически.
- Symbol.keyFor(символ) — возвращает строковый ключ, который хранит переданный символ или undefined, если символ не
хранится в реестре.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Что такое массив?

A

**Массив - упорядоченная коллекция данных, в которой присутствуют 1-й, 2-й, 3-й элементы и т.д. Например, она понадобится нам для хранения списка чего-либо:
пользователей, товаров, элементов HTML и т.д. Массив – это особый подвид объектов.*

let arr = new Array(); let arr = [];

В массиве могут храниться элементы любого типа. Элементы нумеруются и хранятся в том порядке, в котором их поместили в массив. Элементы массива нумеруются, начиная с нуля. Мы можем получить элемент, указав его номер в квадратных скобках.

Получение последних элементов массива:
fruits[fruits.length - 1] fruits.at (-1)

Методы:
slice: Создает новый массив, содержащий копию части исходного массива. Можно указать начальный и конечный индексы для выбора нужной части массива.
const arr = [1, 2, 3, 4, 5]; const slicedArr = arr.slice(1, 4); console.log(slicedArr); // Вывод: [2, 3, 4]
splice: Изменяет содержимое массива, удаляя, заменяя или добавляя элементы на указанной позиции.
const arr = [1, 2, 3, 4, 5]; arr.splice(2, 2, 'a', 'b'); // индекс, с которого начинается изменение массива, количество элементов, которые нужно удалить из массива, элементы, которые нужно добавить в массив на место удаленных элементов console.log(arr); // Вывод: [1, 2, 'a', 'b', 5]
push добавляет элемент в конец.
pop удаляет последний элемент.
unshift добавляет элемент в начало массива:
shift удаляет элемент в начале, сдвигая очередь, так что второй элемент становится первым.
Методы push и unshift могут добавлять сразу несколько элементов. Методы push/pop выполняются быстро, а методы shift/unshift – медленно, потому что работать с концом массива всегда быстрее, чем с началом.
Используйте indexOf(), чтобы найти, под каким индексом хранится элемент.
Используйте includes(), чтобы проверить, что элемент есть в массиве
concat: Объединяет два или более массивов и возвращает новый массив, содержащий элементы из всех объединенных массивов.
forEach: Выполняет указанную функцию один раз для каждого элемента массива.
map: Создает новый массив, содержащий результаты вызова указанной функции для каждого элемента исходного массива.

Многомерные массивы
Массивы могут содержать элементы, которые тоже являются массивами. Это можно использовать для создания многомерных массивов, например, для хранения матриц:
let matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; alert( matrix[1][1] ); // 5, центральный элемент

В современном JavaScript очень популярна деструктуризация массивов. Этот подход позволяет создавать переменные из элементов массива в одну строку:
const catProfile = [ 'Maru', 'Scottish Fold', true, 'https://youtu.be/ChignoxJHXc' ] const [name, breed] = catProfile console.log(name) // Maru

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Тип данных Object

A

Объект (object) — это особый тип данных, он единственный не является примитивом в JS.
Каждое свойство состоит из ключа и значения. Ключ может быть строкой или символом, а значение может быть любым.
Чаще всего объекты создают с помощью литеральной записи.
const cat = {}
Создать объект также можно с помощью конструктора Object. Это объектно-ориентированный стиль программирования:
const book = new Object({ title: 'Война и мир', author: 'Лев Толстой' })

Чтение свойств с помощью точки:
console.log(На полке стоит «${book.title}») // На полке стоит «Война и мир»
Альтернативно для чтения можно использовать квадратные скобки:
console.log(На полке стоит «${book[‘title’]}»)

Для добавления и изменения свойств используется одинаковый синтаксис. Нужно обратиться к свойству и присвоить в него значение с помощью стандартного оператора присваивания =.
Если свойство не существует, оно будет создано:
const book = { title: 'Капитанская дочка' } book.author = 'А.С.Пушкин' // добавляем новое свойство book.title = 'Сказка о царе Салтане' // изменяем существующее console.log(book) // { title: 'Сказка о царе Салтане', author: 'А.С.Пушкин'} delete book['author'] delete book.title

Чаще всего свойства не удаляют, а сбрасывают значение, устанавливая undefined или подходящее по смыслу:
book.title = undefined book['author'] = undefined
Для проверки, есть ли свойство у объекта, используйте оператор in:
const user = { firstName: 'Марина', username: 'zloyDuh' } console.log('firstName' in user) // true console.log('age' in user) // false

Преобразование в массив:
Object.keys(user) = [“name”, “age”]
Object.values(user) = [“John”, 30]
Object.entries(user) = [ [“name”,”John”], [“age”,30] ]
Преобразование обратно в объект:
Object.fromEntries

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Методы объектов

A

Статические методы объекта - методы, которые предварительно определены и вызываются в классе объектов.
Object.keys(obj) – возвращает массив ключей.
Object.values(obj) – возвращает массив значений.
Object.entries(obj) – возвращает массив пар [ключ, значение].

Обойти их
for (let value of Object.values(user)) {
alert(value); // John, затем 30
}

Object.fromEntries(array) - преобразует массив в объект
// преобразовать в массив, затем map, затем fromEntries обратно объект

Object.fromEntries( Object.entries(prices).map(([key, value]) => [key, value * 2]) );

Методы экземпляра — это методы, встроенные в объекты, которые работают с конкретным экземпляром объекта, а не с классом объекта.
Object.prototype.hasOwnProperty() возвращает логическое значение, указывающее, имеет ли объект указанное свойство.
const object1 = {};
object1.property1 = 42;
console.log(object1.hasOwnProperty(‘property1’));
// Expected output: true

Метод Object.create() создаёт новый объект с указанным прототипом и свойствами.
Метод Object.assign() используется для копирования перечисляемых и собственных свойств из одного или более исходных объектов в целевой объект. После копирования он возвращает целевой объект.
var o1 = { a: 1 };
var o2 = { [Symbol(‘foo’)]: 2 };
var obj = Object.assign({}, o1, o2);
console.log(obj); // { a: 1, [Symbol(“foo”)]: 2 }

Object.preventExtensions(объект) предотвращает добавление новых свойств к объекту (то есть, предотвращает расширение этого объекта в будущем).
Object.isExtensible(объект) определяет, является ли объект расширяемым (то есть, можно ли к нему добавлять новые свойства).

Метод Object.freeze() предотвращает модификацию свойств и значений объекта и добавление или удаление свойств объекта.
Метод Object.isFrozen() позволяет определить, был ли объект заморожен или нет, и возвращает логическое значение.

Метод Object.seal() предотвращает добавление новых свойств объекта, но позволяет изменять существующие свойства.
// Возвращает true, если объект “запечатан”
Object.isSealed(объект) определяет, является ли объект запечатанным.

Метод Object.getPrototypeOf() используется для получения внутреннего скрытого [[Prototype]] объекта, также доступного через свойство __proto__.
Существует также связанный с ним метод Object.setPrototypeOf(), который добавляет один прототип к другому объекту. Но вместо этого рекомендуется использовать Object.create(), поскольку он быстрее и эффективнее.

Метод Object.defineProperty() определяет новое или изменяет существующее свойство непосредственно на объекте, возвращая этот объект.
// Добавить или изменить свойство объекта
Object.defineProperty(объект, свойство, описатель)
Object.defineProperty(объект, свойство, {value : значение})
Object.defineProperty(person, “language”, {writable:false}); // делаем свойство language только для чтения

// Добавить или изменить несколько свойств объекта
Object.defineProperties(объект, описатель)

  • writable : true // Значение свойства можно изменять
  • enumerable : true // Свойство может перечисляться
  • configurable : true // Свойство может настраиваться

// Определение геттера
get: function() {
return language
}
// Определение сеттера
set: function(value) {
language = value
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Полифил map

A

// Добавляем новый метод myMap в прототип массива
Array.prototype.myMap = function(callback) {
// Проверяем, что this является массивом или строкой
// Если тип неверный, выбрасываем ошибку TypeError
if (!this instanceof Array || !this instanceof String) {
throw new TypeError(‘Wrong type’);
}
// Проверяем, что callback является функцией
// Если не является, выбрасываем ошибку TypeError
if (typeof callback !== “function”) {
throw new TypeError(‘Callback isn't function’);
}
const result = [];
// Проходим по каждому элементу исходного массива/строки
// Вызываем callback для каждого элемента и добавляем результат в новый массив
for (let i = 0; i < this.length; i += 1) {
result.push(callback(this[i], i, this));
}
return result;
};

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Полифил reduce

A

Метод reduce применяет функцию reducer к каждому элементу массива (слева-направо), возвращая одно результирующее значение.
Пример reduce:
let people = [
{ name: “Ira”, age: 10 },
{ name: “Kira”, age: 20 },
{ name: “Lira”, age: 30 },
{ name: “Vira”, age: 40 },
];
const amount = people.reduce((total, elem) => (total += elem.age), 0); //100

Полифил reduce:

// Добавляем новый метод myReduce в прототип массива
Array.prototype.myReduce = function(callback, accumulator) {
// Проверяем, что this является массивом или строкой
// Если тип неверный, выбрасываем ошибку TypeError
if (!this instanceof Array || !this instanceof String) {
throw new TypeError(‘Wrong type’);
}
// Проверяем, что callback является функцией
// Если не является, выбрасываем ошибку TypeError
if (typeof callback !== “function”) {
throw new TypeError(‘Callback isn't function’);
}

// Инициализируем аккумулятор значением, переданным в качестве аргумента или первым элементом массива
// Устанавливаем начальное значение индекса в зависимости от наличия аргумента аккумулятора
let acc = arguments.length > 1 ? accumulator : this[0];
let iStart = arguments.length > 1 ? 0 : 1;

// Проходим по каждому элементу массива/строки, начиная с определенного индекса
// Вызываем callback с текущим значением аккумулятора, текущим элементом, индексом и самим массивом
for (let i = iStart; i < this.length; i += 1) {
acc = callback(acc, this[i], i, this);
}
return acc;
};

let arr = [0, 1, 3, 5, 7, 8];
const myReduceResult = arr.myReduce((acc, curr, idx, arr) => {
// Используем myReduce для расчета суммы элементов массива
acc += curr;
return acc;
});
console.log(myReduceResult);
// Вывод: 24

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Что такое Big-О

A

В программировании Big-О показывает эффективность конкретного алгоритма. Всегда рассматривается наихудший вариант расчета. Конcтанты откидываются.

Одними из самых часто встречаемых сложностей являются:

  • О(1) - самый эффективный. Вывод 10 элемента в массиве, т.к. это делается по индексу, нам не важно 100 там элементов или миллион, на задачу требуется всегда один шаг. Также хэш-таблицы имеют эту сложность для поиска, индексации и добавления.
  • O(log n) - поиск в отсортированном массиве через бинарный поиск. Сюда относятся все функции, где входящий массив или структура данных делится пополам для поиска.
  • O(n) - поиск элемента в несортированном массиве - для получения результата, вам придется перебрать весь список. Или суммирование элементов массива - тоже нужно пройти все элементы. Чем больше массив, тем больше операций. Можно сказать «сложность порядка n (order n)». Так же такой тип алгоритмов называют «линейными» или что алгоритм «линейно масштабируется». Интересно: Если мы знаем, что массив начинается с 1, отсортирован и не имеет пропусков, для суммирования можно применить формулу S = n(n+1)/2 (где n последний элемент массива) - такая запись будет уже более эффективной O(1).
  • O(n log n) - n раз logn. Как пример сортировка слиянием. Получается O(n * log n), или O(n log n).
  • O(n²) - пузырьковая сортировка, поиск дублей в цикле, который в другом цикле. Массив из 4 элементов требует 16 шагов, массив из 10 элементов – 100 шагов.
  • O(2^n) - поиск всех подмножеств массива. Каждый элемент множества может быть либо включен, либо исключен из подмножества. Набор из четырех элементов [A,B,C,D, E] будет иметь 2^5 или 32 подмножеств.
    [A], [B], [C], [D], [E]
    [A,B], [A,C], [A,D], [A, E], [B,C], [B,D], [C,D] и т.д.
  • O(n!) - самый неэффективный. К примеру, поиск всех возможных вариантов расположения элементов в массиве [A, B, C, D] потребует 4! или 24 шага.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Что такое bind, когда он используется, полифил bind

A

Метод bind() является одним из методов встроенного объекта функции в JavaScript. Он используется для создания новой функции, привязанной к определенному контексту выполнения. Это позволяет явно указать значение this внутри функции и также позволяет привязать предопределенные аргументы.
Привязанная функция, созданная с помощью bind(), не вызывается немедленно. Вместо этого, она возвращается, чтобы ее можно было вызвать позднее.

bind() принимает один или более аргументов:
- thisArg: Объект, который будет использоваться в качестве значения this внутри функции, которая будет создана при вызове bind().
- аргументы: Предопределенные аргументы, которые будут переданы в функцию при вызове.

Пример использования bind():
const obj = { x: 42, getY: function() { return this.x; } }; const boundGetY = obj.getY.bind(obj); // Создаем новую функцию, в которой this будет ссылаться на объект obj console.log(boundGetY()); // Вывод: 42 const anotherObj = { x: 99 }; const boundGetY2 = obj.getY.bind(anotherObj); console.log(boundGetY2()); // Вывод: 99

Function.prototype.myBind = function (...args) { var callback = this, ctx = args.splice(1); return function (...a) { //параметр …a содержит все аргументы, которые можно передать result2()методу. callback.call(args[0], ...[...ctx, ...a]); // args[0] является первым аргументом, переданным myBind()методу (то есть объекту myName), // ctx содержит все остальные аргументы, переданные нашему myBind()методу }; }; const result2 = printName.myBind(myName, "Palia"); result2("India");

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Что такое call, когда он используется, полифил call

A

call() - это метод встроенного объекта функции в JavaScript. Он используется для вызова функции, устанавливая указанное значение this и передавая аргументы в виде списка. При использовании call(), функция вызывается с установленным значением this, которое передается в thisArg, и аргументами, которые передаются в списке после thisArg.

Синтаксис метода call():
function.call(thisArg, arg1, arg2, ...)

  • thisArg: Объект, который будет использоваться в качестве значения this внутри функции.
  • arg1, arg2, …: Аргументы, которые будут переданы в функцию при вызове.

Пример использования call():
const person1 = { name: 'Alice', greeting: function() { return 'Hello, ' + this.name + '!'; } }; const person2 = { name: 'Bob' }; console.log(person1.greeting.call(person2)); // Output: Hello, Bob!
Полифил:
Function.prototype.myCall = function (context, ...args) { let currentContext = context || globalThis; let randomProp = Math.random(); while (currentContext[randomProp] !== undefined) { randomProp = Math.random(); } // генерируем случайное свойство, используя Math.random,чтобы убедиться, что свойство уникально. currentContext[randomProp] = this; let result = currentContext[randomProp](...args); delete currentContext[randomProp]; return result; }; printName.myCall(myName, "Palia", "India");

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Объект Date

A

Объект JavaScript Date — это глобальный объект, который используется для работы с датами и временем. Объекты Date основаны на значении времени, которое представляет собой количество миллисекунд с 1 января 1970 года по всемирному координированному времени.
Важно: при установке месяца, отчёт идёт с 0, где 0 — это январь. При выводе дня недели возвращаемое значение также начинается с 0 и означает воскресенье.

Основные методы
new Date() создаёт экземпляр Date с текущей датой и временем.
new Date(значение) создаёт Date с переданным значением времени. Значение должно быть в формате, который распознается методом Date.parse(), то есть быть совместимым с IETF RFC 2822 или с ISO8601.
new Date(год, месяц, день, часы, минуты, секунды, миллисекунды) создаёт класс Date в местной часовой зоне. Год и месяц являются обязательными параметрами. Остальные параметры, начиная с часов, будут по умолчанию равны 0, а день — 1.
new Date(миллисекунды) создаёт Date со временем в миллисекундах. Количество миллисекунд измеряется с 1 января 1970 года UTC.
getFullYear() — возвращает год;
getMonth() — возвращает месяц с 0 до 11;
getDate() — возвращает день месяца с 1 до 31;
getDay() — возвращает порядковый номер дня недели с 0 до 6;
getHours() — возвращает часы с 0 до 23;
getMinutes() - возвращает минуты от 0 до 59;
getSeconds() - возвращает секунды от 0 до 59;
getMilliseconds() - возвращает миллисекунды от 0 до 999.
getTime() возвращает значение в миллисекундах, прошедших с 1 января 1970 года, соответствующее указанной дате по UTC.
getTimezoneOffset() возвращает смещение в минутах между текущей часовой зоной и UTC.
setFullYear(год, месяц, день) устанавливает год, значения месяца и дня необязательны.
setMonth(месяц, день) устанавливает месяц, передавать день необязательно.
setDate(день) устанавливает день месяца.
setHours(часы, минуты, секунды, миллисекунды) устанавливает часы. Значения минут, секунд, миллисекунд необязательны.
setMinutes(минуты, секунды, миллисекунды) - устанавливает минуты. Секунды и миллисекунды необязательны.
setSeconds(секунды, миллисекунды) устанавливает секунды. Миллисекунды передавать необязательно.
setMilliseconds(миллисекунды) - устанавливает миллисекунды.
Для UTC аналогичные методы, только добавляем UTC после set. Например, setUTCMilliseconds(миллисекунды).
И метод, который относится только к UTC:
setTime(значение) устанавливает значение, которое равно количеству миллисекунд, прошедших с 1 января 1970 года.

Метод Date.parse(значение) используется для разбора (ещё говорят парсинга) строкового представления даты.
Возвращает значение, равное количеству миллисекунд, прошедших с 1 января 1970 года.

Для отображения Date в различных форматах существует метод toLocaleDateString(локаль, опции).
Локаль — это необязательный параметр, который является строкой или массивом строк с языковой меткой BCP 47.
Например, en-US или de-DE. Локаль хранит региональные настройки о формате дат, номеров, адресов.
Опции — необязательный параметр с объектом настроек. Доступные свойства:
localeMatcher — алгоритм поиска локали, используется для выбора подходящей локали. Принимает значения lookup или best fit. По умолчанию best fit.
timeZone — значение используемого часовой зоны. Все браузеры должны принимать значение UTC, значение по умолчанию равно значению часовой зоны среды выполнения. Формат принимаемого значения может различаться в различных браузерах.
hour12 — значение, которое определяет, использовать ли 12-часовой формат вывода. Принимает true или false.
formatMatcher — алгоритм поиска формата, используется для выбора формата отображения. Принимает значения basic или best fit. По умолчанию best fit.
timeZoneName — формат названия часовой зоны. Принимает long или short.
weekday — значение дня недели. Принимает narrow, short и long.
era — значение эры. Принимает narrow, short и long.
year — значение года. Принимает numeric и 2-digit.
month — значения месяца. Принимает numeric, 2-digit, narrow, short и long.
day — значения дня. Принимает numeric и 2-digit.
hour — значения часа. Принимает numeric и 2-digit.
minute — значения минут. Принимает numeric и 2-digit.
second — значения секунд. Принимает numeric и 2-digit.
Браузеры обязаны поддерживать следующие наборы настроек отображения:
weekday, year, month, day, hour, minute, second weekday, year, month, day year, month, day year, month month, day hour, minute, second hour, minute

Date.now() — метод, который возвращает текущее время в миллисекундах, прошедших с 1 января 1970 года UTC.
Если вам не хватает функциональности, представленной классом Date, например, недостаточно его возможностей форматирования или парсинга, то можно посмотреть в сторону библиотек day.js или date-fns.

В следующей таблице перечислены стандартные свойства объекта Date.

prototype - Позволяет добавлять новые свойства и методы к объекту Date.
Примечание. У каждого объекта в JavaScript есть свойство конструктора, которое ссылается на функцию конструктора, которая использовалась для создания экземпляра этого объекта.

В следующей таблице перечислены стандартные методы объекта Date.
- getDate(): Возвращает день месяца (от 1 до 31).
- getDay(): Возвращает день недели (от 0 до 6).
- getFullYear(): Возвращает год (четыре цифры).
- getHours(): Возвращает час (от 0 до 23).
- getMilliseconds(): Возвращает миллисекунды (от 0 до 999).
- getMinutes(): Возвращает минуты (от 0 до 59).
- getMonth(): Возвращает месяц (от 0 до 11).
- getSeconds(): Возвращает секунды (от 0 до 59).
- getTime(): Возвращает количество миллисекунд, прошедших с полуночи 1 января 1970 года.
- getTimezoneOffset(): Возвращает разницу во времени между временем UTC и местным временем в минутах.
- getUTCDate(): Возвращает день месяца по всемирному времени (от 1 до 31).
- getUTCDay(): Возвращает день недели по всемирному времени (от 0 до 6).
- getUTCFullYear(): Возвращает год по всемирному времени.
- getUTCHours(): Возвращает час по всемирному времени (от 0 до 23).
- getUTCMilliseconds(): Возвращает миллисекунды по всемирному времени (от 0 до 999).
- getUTCMinutes(): Возвращает минуты по всемирному времени (от 0 до 59).
- getUTCMonth(): Возвращает месяц по всемирному времени (от 0 до 11).
- getUTCSeconds(): Возвращает секунды по всемирному времени (от 0 до 59).

  • now() - Возвращает количество миллисекунд, прошедших с полуночи 1 января 1970 года.
  • parse() - Разбирает строку даты и возвращает количество миллисекунд с 1 января 1970 года.
  • setDate() - Устанавливает день месяца объекта даты.
  • setFullYear() - Устанавливает полный год объекта даты.
  • setHours() - Устанавливает часы объекта даты.
  • setMilliseconds() - Устанавливает миллисекунды объекта даты.
  • setMinutes() - Устанавливает минуты объекта даты.
  • setMonth() - Устанавливает месяц объекта даты.
  • setSeconds() - Устанавливает секунды объекта даты.
  • setTime() - Устанавливает дату на указанное количество миллисекунд после/до 1 января 1970 года.
  • setUTCDate() - Устанавливает день месяца объекта даты по всемирному времени
  • setUTCFullYear() - Устанавливает год объекта даты по всемирному времени.
  • setUTCHours() - Устанавливает часы объекта даты по всемирному времени.
  • setUTCMilliseconds() - Устанавливает миллисекунды объекта даты по всемирному времени.
  • setUTCMinutes() - Установите минуты объекта даты по всемирному времени.
  • setUTCMonth() - Устанавливает месяц объекта даты по всемирному времени.
  • setUTCSeconds() - Установите секунды объекта даты по всемирному времени.
  • toDateString() - Преобразует часть даты объекта Date в удобочитаемую форму.
  • toISOString(): Возвращает дату в виде строки, отформатированной в соответствии со стандартом ISO.
  • toJSON(): Возвращает дату в виде строки в формате даты JSON.
  • toLocaleDateString(): Возвращает часть даты объекта Date в виде строки локального формата.
  • toLocaleTimeString(): Возвращает временную часть объекта Date в виде строки локального формата.
  • toLocaleString(): Преобразует объект Date в строку локального формата.
  • toString(): Преобразует объект Date в строку.
  • toTimeString(): Преобразует временную часть объекта Date в строку.
  • toUTCString(): Преобразует объект Date в строку по всемирному времени.
  • UTC(): Возвращает количество миллисекунд в объекте Date с 00:00:00 (полночь) 1 января 1970 года по всемирному времени.
  • valueOf(): Возвращает примитивное значение объекта Date.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Что такое DOM Events?

A

Событие – это сигнал от браузера о том, что что-то произошло. Все DOM-узлы подают такие сигналы (хотя события бывают и не только в DOM).

События мыши:
- click – происходит, когда кликнули на элемент левой кнопкой мыши (на устройствах с сенсорными экранами оно происходит при касании).
- contextmenu – происходит, когда кликнули на элемент правой кнопкой мыши.
- mouseover / mouseout – когда мышь наводится на / покидает элемент.
- mousedown / mouseup – когда нажали / отжали кнопку мыши на элементе.
- mousemove – при движении мыши.

События на элементах управления:
- submit – пользователь отправил форму <form>.
- focus – пользователь фокусируется на элементе, например нажимает на <input></input>.

Клавиатурные события:
- keydown и keyup – когда пользователь нажимает / отпускает клавишу.

События документа:
- DOMContentLoaded – когда HTML загружен и обработан, DOM документа полностью построен и доступен.

CSS events:
- transitionend – когда CSS-анимация завершена.

Существует множество других событий.

Событию можно назначить обработчик, то есть функцию, которая сработает, как только событие произошло.
Именно благодаря обработчикам JavaScript-код может реагировать на действия пользователя.
1. Обработчик может быть назначен прямо в разметке, в атрибуте, который называется on<событие>.
<input value="Нажми меня" onclick="alert('Клик!')" type="button"
2. Можно назначать обработчик в JS, используя свойство DOM-элемента on<событие>.
elem.onclick = function() { alert('Спасибо'); };
Так как у элемента DOM может быть только одно
свойство с именем onclick, то назначить более одного обработчика так нельзя.
3. И последний и более гибкий способ: addEventListener и removeEventListener. С помощью него можно повесить более одного обработчика на одно событие, а также можно удалить обработчик, вызвав его и передав ту же функцию. Есть несколько типов событий, которые работают только через него, к примеру transitionend и DOMContentLoaded. Также addEventListener поддерживает объекты в качестве обработчиков событий. В этом случае вызывается метод объекта handleEvent.
Не важно, как вы назначаете обработчик – он получает объект события первым аргументом. Этот объект содержит подробности о том, что произошло.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Расскажи про Form и Input Events

A

Form и Input Events - это события, которые позволяют отслеживать и реагировать на действия пользователя, связанные с отправкой формы, изменением значений полей ввода и другими взаимодействиями с элементами форм и ввода.

Некоторые из наиболее часто используемых событий форм и ввода включают:

  • submit: Событие submit возникает, когда пользователь отправляет форму. Это может произойти, например, при нажатии на кнопку отправки или использовании клавиши Enter в текстовом поле. Для отслеживания отправки формы вы можете прослушивать событие submit на элементе.
  • input: Событие input возникает, когда значение в поле ввода изменяется пользователем. Это может быть поле ввода текста, чекбокс, переключатель и другие элементы. Событие input позволяет реагировать на моментальные изменения значения в поле ввода.
  • change: Событие change возникает, когда значение в поле ввода изменяется и пользователь уводит фокус с элемента. Это позволяет отслеживать изменения значения и принимать соответствующие действия, когда пользователь закончил ввод.
  • cut: Событие cut возникает, когда пользователь вырезает выделенный текст или другой выделенный контент с использованием горячих клавиш или контекстного меню. Вы можете использовать это событие для выполнения дополнительных действий при вырезании данных пользователем. Это событие можно предотвратить, чтобы запретить вырезание содержимого.
  • copy: Событие copy возникает, когда пользователь копирует выделенный текст или другой выделенный контент с использованием горячих клавиш или контекстного меню. Подобно событию cut, вы можете использовать эту возможность для выполнения дополнительных действий при копировании данных пользователем. Можно предотвратить это действие, чтобы запретить копирование содержимого.
  • paste: Событие paste возникает, когда пользователь вставляет данные из буфера обмена в поле ввода или другой элемент на странице. Вы можете использовать это событие для выполнения дополнительной обработки данных, вставленных пользователем. Можно предотвратить вставку данных, чтобы запретить вставку содержимого.
  • focus: Событие focus возникает, когда элемент ввода или другой элемент получает фокус. Например, когда пользователь щелкает на текстовое поле.
  • blur: Событие blur возникает, когда элемент ввода или другой элемент теряет фокус. Например, когда пользователь переключается на другой элемент или щелкает вне текстового поля.
  • select: Событие select возникает, когда пользователь выбирает (выделяет) текст в элементе ввода или другом элементе на странице.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Как перебрать ключи и значения объекта в JavaScript?

A
  1. Цикл for...in используется для перебора ключей объектов, массивов и строк.
  2. Метод Object.keys() возвращает массив ключей объекта.
  3. Метод Object.values() возвращает значения всех свойств объекта в виде массива.
  4. Метод Object.entries() возвращает массив пар ключ-значение объекта.

Метод Object.keys() был добавлен в ES6, тогда как Object.entries() и Object.values() методы были добавлены в ES8. Эти методы преобразуют объект в массив, а затем используют методы цикла массива для перебора этого массива.

Самый простой и популярный способ перебора ключей и значений объекта — использование for…in цикла:

const birds = { 
owl: '🦉', 
eagle: '🦅', 
duck: '🦆', 
chicken: '🐔' 
}
for (const key in birds) { 
 console.log(`${key} -> ${birds[key]}`) 
}
// owl -> 🦉 
// eagle -> 🦅 
// duck -> 🦆 
// chicken -> 🐔

Единственная проблема с for…in циклом заключается в том, что он перебирает свойства в цепочке прототипов. Поскольку объект JavaScript наследует свойства своего прототипа , for…in цикл также будет перебирать эти свойства. Однако вы можете использовать этот hasOwnProperty() метод для исключения унаследованных свойств :
for (const key in birds) { if (birds.hasOwnProperty(key)) { console.log(${key} -> ${birds[key]}) } }

Метод Object.keys() принимает объект в качестве входных данных и возвращает массив имен собственных перечисляемых свойств объекта:
const birds = { owl: '🦉', eagle: '🦅', duck: '🦆', chicken: '🐔' } const keys = Object.keys(birds) console.log(keys) // [ 'owl', 'eagle', 'duck', 'chicken' ]

Теперь мы можем использовать forEach()цикл для перебора массива и получения значения каждого свойства:

keys.forEach(key => { console.log(${key} -> ${birds[key]}) }) // owl -> 🦉 // eagle -> 🦅 // duck -> 🦆 // chicken -> 🐔

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Как остановить распространение событий ( stopPropagation() / stopImmediatePropagation() )?
Метод `stopPropagation()` интерфейса Event предотвращает дальнейшее распространение текущего события на этапах захвата и всплытия. Однако это не предотвращает появление поведения по умолчанию; например, клики по ссылкам все еще обрабатываются. Если вы хотите остановить такое поведение, используйте метод `preventDefault()`. Это также не препятствует немедленному распространению на другие обработчики событий. Если вы хотите остановить их, используйте метод `stopImmediatePropagation()`. Ниже приведен пример кода, который демонстрирует использование метода `stopPropagation()`: const outerElement = document.querySelector("#outer"); const innerElement = document.querySelector("#inner"); outerElement.addEventListener("click", function(event) { console.log("Внешний элемент"); }); innerElement.addEventListener("click", function(event) { console.log("Внутренний элемент"); event.stopPropagation(); }); innerElement.click(); // Вывод: // Внутренний элемент // Внешний элемент Объяснение: При клике на внутреннем элементе inner, обработчик события вызывает event.stopPropagation(), что приводит к прекращению распространения события дальше по вложенным элементам. В результате сначала вызывается обработчик внутреннего элемента inner, а затем обработчик внешнего элемента outer. Метод `stopImmediatePropagation()` интерфейса Event предотвращает вызов других слушателей того же события. Если к одному и тому же элементу для одного и того же типа события прикреплено несколько слушателей, они вызываются в том порядке, в котором были добавлены. Если `stopImmediatePropagation()` вызывается во время одного такого вызова, остальные прослушиватели вызываться не будут. Ниже приведен пример кода, который демонстрирует использование метода `stopImmediatePropagation()`: const button = document.querySelector("#myButton"); button.addEventListener("click", function(event) { console.log("Обработчик клика 1"); event.stopImmediatePropagation(); }); button.addEventListener("click", function(event) { console.log("Обработчик клика 2"); }); button.addEventListener("click", function(event) { console.log("Обработчик клика 3"); }); button.click(); // Вывод: // Обработчик клика 1 Объяснение: При клике на кнопке myButton, первый обработчик события прерывает дальнейшее распространение события, вызывая метод event.stopImmediatePropagation(). Поэтому второй и третий обработчики не выполняются, и в консоли выводится только сообщение из первого обработчика. Итого: `stopPropagation()` позволяет выполнять другие обработчики событий для того же элемента, но `stopImmediatePropagation()` предотвращает это. `stopPropagation()` и `stopImmediatePropagation()` предотвращают выполнение обработчиков событий на этапах захвата и всплытия.
25
Расскажи про HTMLCollection и NodeList
HTMLCollection и NodeList — это очень похожие на массив коллекции. Они хранят элементы веб-страницы (узлы DOM). NodeList может хранить любые типы узлов, а HTMLCollection — только узлы HTML элементов. К элементам коллекций можно обращаться по индексу, но у них нет привычных методов массива. HTMLCollection возвращают методы `getElementsByTagName()` и `getElementsByClassName()`. NodeList возвращают метод `querySelectorAll()` и свойство `childNodes`. Полученная один раз HTMLCollection всегда остаётся актуальной — JavaScript будет обновлять её в случае, если на странице появляется подходящий элемент. Поэтому HTMLCollection называют «живой» коллекцией. NodeList работает почти так же, как и HTMLCollection. Разница: 1. NodeList может хранить любые типы узлов, например текстовые узлы и комментарии, а HTMLCollection — только узлы HTML элементов. 2. HTMLCollection позволяет обращаться к элементам не только по индексу, но и по имени с помощью метода `namedItem`. 3. NodeList может быть не только «живой» коллекцией, но и статической. Такая коллекция не обновляется при появлении на странице новых элементов. «Живой» NodeList возвращают метод getElementsByName() и свойство childNodes. Статический NodeList возвращает метод querySelectorAll(). Если очень нужны методы массива, то преобразуйте HTMLCollection или NodeList в массив с помощью Array.from().
26
Интернационализация в JavaScript
Общая проблема строк, дат, чисел в JavaScript – они «не в курсе» языка и особенностей стран, где находится посетитель. - Строки- при сравнении сравниваются коды символов, а это неправильно, к примеру, в русском языке оказывается, что "ё" > "я", хотя я – последняя буква алфавита и это она должна быть больше любой другой. - Даты в разных странах принята разная запись дат. Где-то пишут 31.12.2014 (Россия), а где-то 12/31/2014 (США), где-то иначе. - Числа в одних странах выводятся цифрами, в других – иероглифами, длинные числа разделяются где-то пробелом, где-то запятой. Все современные браузеры, кроме IE10 (но есть библиотеки и для него) поддерживают стандарт ECMA 402, предназначенный решить эти проблемы навсегда. Интерфейс Intl является встроенным объектом в JavaScript, предоставляющим поддержку интернационализации (i18n) и локализации (l10n) при работе с различными языками и региональными настройками. Он предоставляет различные функциональности для форматирования чисел, дат, времени, валюты и текста с учетом локализации. Вот некоторые из хорошо известных возможностей Intl в JavaScript: - **Intl.Collator**: умеет правильно сравнивать и сортировать строки. `const names = ['Иван', 'Алексей', 'Ян', 'Джон', 'Élodie']; const collator = new Intl.Collator('ru', { sensitivity: 'base' }); const sortedNames = names.sort(collator.compare); console.log(sortedNames); // ["Алексей", "Джон", "Иван", "Élodie", "Ян"]` - **Intl.NumberFormat**: позволяет форматировать числа в соответствии с языковыми представлениями и локальными настройками. С помощью этого класса вы можете задать количество десятичных знаков, разделители тысячных и дробных частей, а также символы для обозначения валюты. `const number = 12345.6789; const formattedNumber = new Intl.NumberFormat('en-US', { style: 'decimal' }).format(number); console.log(formattedNumber); // "12,345.6789"` - **Intl.DateTimeFormat**: позволяет форматировать даты и времена с учетом языковых представлений и локальных настроек. С помощью этого класса вы можете установить формат даты и времени, использовать определенные календарные системы и часовые пояса. `const date = new Date(); const formattedDate = new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'short' }).format(date); console.log(formattedDate); // "Friday, October 29, 2021 at 2:30 PM"` - **Intl.ListFormat**: предоставляет возможность форматирования списков значений с использованием языковых представлений. Он позволяет задать разделитель между элементами списка и перед последним элементом списка для более понятного отображения. `const fruits = ['яблоко', 'банан', 'киви', 'апельсин']; const listFormat = new Intl.ListFormat('ru', { style: 'long', type: 'conjunction' }); const formattedList = listFormat.format(fruits); console.log(formattedList); // "яблоко, банан, киви и апельсин"` - **Intl.PluralRules**: позволяет определить правила склонения для различных числовых значений в соответствии с языковыми представлениями. Это полезно при отображении правильной формы слова для разных числовых значений, таких как единственное число, множественное число и другие. `const count = 5; const pluralRules = new Intl.PluralRules('en'); const plural = pluralRules.select(count); const message = `У вас ${count} ${plural} яблок${plural === 'one' ? 'о' : 'а'}`; console.log(message); // "У вас 5 яблок"` - **Intl.RelativeTimeFormat**: позволяет форматировать относительное время (например, "5 минут назад" или "через 3 дня") с учетом языковых представлений. Это полезно для создания дружественных для пользователя сообщений времени. `const minutesAgo = -5; const relativeTimeFormat = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }); const formattedRelativeTime = relativeTimeFormat.format(minutesAgo, 'minute'); console.log(formattedRelativeTime); // "5 minutes ago"` Все эти методы при запуске создают соответствующий объект Intl.* и передают ему опции, можно рассматривать их как укороченные варианты вызова. _Локаль_ – первый и самый важный аргумент всех методов, связанных с интернационализацией. Локаль описывается строкой из трёх компонентов, которые разделяются дефисом: - Код языка. - Код способа записи. - Код страны. На практике не всегда указаны три, обычно меньше: ru – русский язык, без уточнений. en-GB – английский язык, используемый в Англии (GB). en-US – английский язык, используемый в США (US). zh-Hans-CN – китайский язык (zh), записываемый упрощённой иероглифической письменностью (Hans), используемый в Китае. Все методы принимают локаль в виде строки или массива, содержащего несколько локалей в порядке предпочтения. Если локаль не указана или undefined – берётся локаль по умолчанию, установленная в окружении (браузере). _Подбор локали localeMatcher_ localeMatcher – вспомогательная настройка, которую тоже можно везде указать, она определяет способ подбора локали, если желаемая недоступна. У него два значения: 1. "lookup" – означает простейший порядок поиска путём обрезания суффикса, например zh-Hans-CN → zh-Hans → zh → локаль по умолчанию. 2. "best fit" – использует встроенные алгоритмы и предпочтения браузера (или другого окружения) для выбора подходящей локали. По умолчанию стоит "best fit".
27
Local Storage vs Session Storage vs Cookie
**Local Storage (локальное хранилище)** - Хранит данные бессрочно. - Очищается только с помощью JavaScript или очистки кэша браузера. - Хранит данные объёмом до 5 МБ, это самый большой объём из трёх вариантов хранилища. - Не поддерживается старыми браузерами, например, IE 7 и ниже. - Работает по правилу ограничения домена (same origin policy). То есть сохранённые данные доступны только для одного источника. - браузеры на основе движка WebKit, например Safari, очищают localStorage, если к нему не обращались в течение 7 дней **Session Storage (сессионное хранилище)** - Хранит данные, пока продолжается текущая сессия. Когда пользователь закрывает браузер, данные становятся недоступными. - Используется контекст браузера верхнего уровня, поэтому каждая вкладка браузера хранит уникальные данные. - Объём данных больше чем в Cookie. - Не поддерживается старыми браузерами, например, IE 7 и ниже. **Cookie** - Хранит данные, которые можно передавать на сервер через заголовки. Локальное и сессионное хранилище доступны только на клиентской стороне. - Срок хранения устанавливается при создании cookie. - Объём данных не превышает 4 Кбайт. - Cookie могут быть защищёнными, в этом случае их содержимое нельзя получить на стороне клиента. Это важно для аутентификации при хранении пользовательских токенов. **Основные отличия:** 1. В отличие от куки, объекты веб-хранилища не отправляются на сервер при каждом запросе. Именно поэтому мы можем хранить гораздо больше данных. Большинство современных браузеров могут выделить как минимум 5 мегабайтов данных (или больше), и этот размер можно поменять в настройках. 2. Ещё одно отличие от куки – сервер не может манипулировать объектами хранилища через HTTP-заголовки. Всё делается при помощи JavaScript. 3. Хранилище привязано к источнику (домен/протокол/порт). Это значит, что разные протоколы или поддомены определяют разные объекты хранилища, и они не могут получить доступ к данным друг друга. Объекты хранилища localStorage и sessionStorage предоставляют одинаковые методы и свойства: `window.localStorage.setItem('name', 'Дока Дог')` - сохранение `window.localStorage.getItem('name')` - чтение `window.localStorage.removeItem('name')` - удаление `window.localStorage.clear()` - очистка хранилища `key(index)` – получить ключ на заданной позиции. `length` – количество элементов в хранилище. Иногда нам нужно сохранить не просто текст, а целую структуру данных, и в этом нам поможет JSON.stringify() и после парсим JSON.parse(userJSON). Значения хранятся в виде строк. При попытке сохранения других типов данных, они будут приведены к строке. Например, если записать число, то при чтении нам вернётся число, записанное в строку. Как видим, интерфейс похож на Map (setItem/getItem/removeItem), но также позволяет получить доступ к элементу по индексу – key(index).
28
null и undefined различия и сходства
**Сходства:** - они принадлежат к 7 «примитивам» JS - являются ложными значениями при преобразовании в булевое значение **Различия:** undefined («неопределенный») представляет собой значение по умолчанию: - переменной, которой не было присвоено значения, т.е. объявленной, но не инициализированной переменной; - функции, которая ничего не возвращает явно, например, console.log(1); - несуществующего свойства объекта. null — это «значение отсутствия значения». null — это значение, которое присваивается переменной явно. При сравнении null и undefined мы получаем true, когда используем оператор "==", и false при использовании оператора "===".
29
Что такое Object.is?
Новая функция для проверки равенства значений. Возвращает true, если значения value1 и value2 равны, иначе false. Она похожа на обычное строгое равенство ===, но есть отличия: // Сравнение +0 и -0 alert( Object.is(+0, -0)); // false alert( +0 === -0 ); // true // Сравнение с NaN alert( Object.is(NaN, NaN) ); // true alert( NaN === NaN ); // false Отличия эти в большинстве ситуаций некритичны, так что не похоже, чтобы эта функция вытеснила обычную проверку ===. Что интересно – этот алгоритм сравнения, который называется SameValue, применяется во внутренних реализациях различных методов современного стандарта.
30
Что такое return?
return возвращает результат из функции и используется только в функциях. Благодаря return можно использовать результат работы функции где угодно. Например, в условиях или при формировании новых значений. Пример ниже использует функцию с return для проверки условия — действительно ли счёт игрока больше 100: `function checkScore(score) { return score > 100 } const s1 = 10 const s2 = 15 const s3 = 20 if (checkScore(s1)) alert('игрок 1 проходит') if (checkScore(s2)) alert('игрок 2 проходит') if (checkScore(s3)) alert('игрок 3 проходит')` return останавливает выполнение функции. Обычно это ожидаемое поведение, но если про это забыть — возможны баги.
31
Объект Set
Объект Set – это вид коллекции уникальных значений без ключей. Основные методы: - `new Set(iterable)` – создаёт Set, и если в качестве аргумента был предоставлен итерируемый объект (обычно это массив), то копирует его значения в новый Set. - `set.add(value)` – добавляет значение (если оно уже есть, то ничего не делает), возвращает тот же объект Set. - `set.delete(value)` – удаляет значение, возвращает true, если value было в множестве на момент вызова, иначе false. - `set.has(value)` – возвращает true, если значение присутствует в множестве, иначе false. - `set.clear()` – удаляет все имеющиеся значения. - `set.size` – возвращает количество элементов в множестве. Пример использования объекта Set: `const set = new Set(); set.add('apple'); set.add('banana'); set.add('apple'); // не будет добавлено, так как значение уже присутствует в множестве console.log(set.size); // 2 console.log(set.has('apple')); // true console.log(set.delete('banana')); // true console.log(set.size); // 1 set.clear(); console.log(set.size); // 0` Перебор объекта Set: 1. **`for..of`**: `const set = new Set(['apple', 'banana', 'orange']); for (const value of set) { console.log(value); }` 2. **`forEach`**: `const set = new Set(['apple', 'banana', 'orange']); set.forEach((value, valueAgain, set) => { console.log(value); });` Функция в `forEach` у объекта Set имеет 3 аргумента: значение `value`, затем снова то же самое значение `valueAgain`, и только потом целевой объект `set`. Это действительно так, значение появляется в списке аргументов дважды. Это сделано для совместимости с объектом Map, в котором колбэк `forEach` имеет 3 аргумента. Выглядит немного странно, но в некоторых случаях может помочь легко заменить Map на Set и наоборот. Set имеет те же встроенные методы, что и Map: - `set.keys()` – возвращает перебираемый объект для значений. - `set.values()` – то же самое, что и `set.keys()`, присутствует для обратной совместимости с объектом Map. - `set.entries()` – возвращает перебираемый объект для пар вида `[значение, значение]`, присутствует для обратной совместимо
32
setTimeout и setInterval
Мы можем вызвать функцию не в данный момент, а позже, через заданный интервал времени. Это называется «планирование вызова». Для этого существуют два метода: - `setTimeout` позволяет вызвать функцию один раз через определённый интервал времени. `setTimeout(() => alert('Привет'), 1000);` Вызов setTimeout возвращает «идентификатор таймера» timerId, который можно использовать для отмены дальнейшего выполнения. `let timerId = setTimeout(...); clearTimeout(timerId);` - `setInterval` позволяет вызывать функцию регулярно, повторяя вызов через определённый интервал времени. `let timerId = setInterval(() => alert('tick'), 2000);` Чтобы остановить дальнейшее выполнение функции, необходимо вызвать clearInterval(timerId). `setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);` Эти методы не являются частью спецификации JavaScript. Но большинство сред выполнения JS-кода имеют внутренний планировщик и предоставляют доступ к этим методам. Вложенный setTimeout `let timerId = setTimeout(function tick() { alert('tick'); timerId = setTimeout(tick, 2000); // (*) }, 2000);` `Методы setInterval(func, delay, ...args) и setTimeout(func, delay, ...args)` позволяют выполнять func регулярно или только один раз после задержки delay, заданной в мс. Для отмены выполнения необходимо вызвать clearInterval / clearTimeout со значением, которое возвращают методы setInterval/setTimeout. Вложенный setTimeout – более гибкий метод, чем setInterval. С его помощью последующий вызов может быть задан по-разному в зависимости от результатов предыдущего. Вложенный setTimeout позволяет задать задержку между выполнениями более точно, чем setInterval. Планирование с нулевой задержкой setTimeout(func,0) или, что то же самое, setTimeout(func) используется для вызовов, которые должны быть исполнены как можно скорее, после завершения исполнения текущего кода. Браузер ограничивает 4-мя мс минимальную задержку между пятью и более вложенными вызовами setTimeout, а также для setInterval, начиная с 5-го вызова. Обратим внимание, что все методы планирования не гарантируют точную задержку. Например, таймер в браузере может замедляться по многим причинам: - Перегружен процессор. - Вкладка браузера в фоновом режиме. - Работа ноутбука от аккумулятора. Всё это может увеличивать минимальный интервал срабатывания таймера (и минимальную задержку) до 300 или даже 1000 мс в зависимости от браузера и настроек производительности ОС.
33
Агрегация данных
**Data aggregation** Агрегация используется со строками и числами и относится к динамической генерации строк. `repeat ('icecream', 3); // icecreamicecreamicecream`
34
Аксессоры (геттеры и сеттеры)
**Accessor properties** Геттеры и сеттеры — это методы, задача которых контролировать доступ к полям. Геттер считывает и возвращают значение поля, а сеттер — наоборот, принимает в качестве аргумента значение и записывает в поле. Сеттер при записи значения в поле объекта, может проверить тип, или входит ли значение в диапазон допустимых (валидация). В геттер же можно добавить, ленивую инициализацию или кэширование, если актуальное значение на самом деле лежит в базе данных. `const person = { firstName: 'John', lastName: 'Doe', // Геттер для получения полного имени get fullName() { return this.firstName + ' ' + this.lastName; }, // Сеттер для установки полного имени set fullName(value) { const [firstName, lastName] = value.split(' '); this.firstName = firstName; this.lastName = lastName; } }; console.log(person.fullName); // "John Doe" person.fullName = 'Jane Smith'; console.log(person.firstName); // "Jane" console.log(person.lastName); // "Smith"`
35
Бесконечный цикл
**Infinite loop (endless loop)** Бесконечный цикл - это конструкция в программировании, которая выполняется бесконечно, без завершения. Такой цикл не содержит условия или логики, которая могла бы привести к его завершению. Бесконечные циклы обычно являются ошибкой программирования и приводят к "зависанию" программы, потреблению ресурсов процессора и памяти, и, как следствие, к зависанию или падению программы.
36
В каких случаях используются анонимные функции?
Анонимные функции чаще всего используются в качестве функций обратных вызовов. Также, каждая стрелочная функция является анонимной.
37
В чем разница между Array.prototype.forEach и Array.prototype.map?
.forEach проходится по массиву с выполнением переданного обратного вызова на каждой итерации. .map создает и возвращает новый массив на основе исходного, выкладывая по кирпичику на каждой итерации. Он нужен, если мы хотим преобразовать текущий массив в какой-то новый.
38
В чем разница между Call, Apply и Bind?
Функции в JavaScript никак не привязаны к своему контексту this, с одной стороны, здорово – это позволяет быть максимально гибкими, одалживать методы и так далее. Но с другой стороны – в некоторых случаях контекст может быть потерян. Способы явно указать this - методы bind, call и apply. **call** - вызывает функцию с заданным this значением и аргументами, предоставленными один за другим через запятую. Синтаксис метода call: func.call(context, arg1, arg2, ...) При этом вызывается функция func, первый аргумент call становится её this, а остальные передаются «как есть». Вызов func.call(context, a, b...) – то же, что обычный вызов func(a, b...), но с явно указанным this(=context). `const employee1 = { firstName: "John", lastName: "Rodson" }; const employee2 = { firstName: "Jimmy", lastName: "Baily" }; function invite(greeting1, greeting2) { console.log( greeting1 + " " + this.firstName + " " + this.lastName + ", " + greeting2 ); } invite("Hello", "How are you?"); // Hello undefined undefined, How are you? invite.call(employee1, "Hello", "How are you?"); // Hello John Rodson, How are you? invite.call(employee2, "Hello", "How are you?"); // Hello Jimmy Baily, How are you?` **apply** - вызов функции с переменным количеством аргументов в виде массива и с подменой контекста. Если нам неизвестно, с каким количеством аргументов понадобится вызвать функцию, можно использовать более мощный метод: apply. Вызов функции при помощи func.apply работает аналогично func.call, но принимает массив аргументов вместо списка. func.call(context, arg1, arg2) идентичен вызову func.apply(context, [arg1, arg2]); `function testForApply() { console.log(this); for (let i = 0; i < arguments.length; i++) { console.log(arguments[i]); } } testForApply(1, 2, 3); // [object Window], 1, 2, 3 testForApply.apply("abc", [1, 2, 3, 4]); // abc, 1, 2, 3, 4` **bind** - создаёт "обёртку" над функцией, которая подменяет контекст этой функции. Поведение похоже на call и apply, но, в отличие от них, bind не вызывает функцию, а лишь возвращает "обёртку", которую можно вызвать позже. Синтаксис встроенного bind: var wrapper = func.bind(context, [arg1, arg2...]) Методы bind и call/apply близки по синтаксису, но есть важнейшее отличие. Методы call/apply вызывают функцию с заданным контекстом и аргументами. А bind не вызывает функцию. Он только возвращает «обёртку», которую мы можем вызвать позже, и которая передаст вызов в исходную функцию, с привязанным контекстом. `function testForBind() { console.log(this); } let wrapped = testForBind.bind("abc"); testForBind(); // [object Window] wrapped(); // abc` Также bind умеет подменять не только контекст, но и аргументы функции, осуществляя каррирование: `function add(a, b) { return a + b; } var addOne = add.bind(null, 1); alert(add(1, 2)); // 3 alert(addOne(2)); // 3`
39
В чем разница между операторами == и ===?
Оператор == сравнивает на равенство, а === на идентичность. Плюс оператора === состоит в том, что он не приводит два значения к одному типу. Важно, при ===: NaN ничему не равен, в том числе и NaN. Положительные и отрицательные нули равны друг другу. Два объекта строго равны, если они ссылаются на один и тот же объект. Типы Null и Undefined не равны ===, но равны ==. т. е. null===undefined → false, но null==undefined → true Интересно: Есть еще Object.is (новшество из ECMAScript 6). Object.is ведёт себя так же, как и тройное равно, но со специальной обработкой для NaN, -0 и +0, возвращая false при сравнении -0 и +0, и true для операции Object.is(NaN, NaN). (В то время как двойное или тройное равенство вернут false согласно стандарту IEEE 754.)
40
В чем разница между методами event.preventDefault() и event.stopPropagation()?
Метод `event.preventDefault()` отключает поведение элемента по умолчанию. Если использовать этот метод в элементе form, то он предотвратит отправку формы (submit). Если использовать его в contextmenu, то контекстное меню будет отключено (данный метод часто используется в keydown для переопределения клавиатуры, например, при создании музыкального/видео плеера или текстового редактора — прим. пер.). Чтобы узнать применен ли к элементу `event.preventDefault()` можно использовать `event.defaulPrevented`, возвращающее логическое значение, служащее индикатором применения к элементу метода `event.preventDefault`. Метод `event.stopPropagation()` отключает распространение события (его всплытие или погружение).
41
Виртуальный метод (виртуальная функция)
**Virtual function** Виртуальный метод (виртуальная функция) — в объектно-ориентированном программировании метод (функция) класса, который может быть переопределён в классах-наследниках так, что конкретная реализация метода для вызова будет определяться во время исполнения. Таким образом, программисту необязательно знать точный тип объекта для работы с ним через виртуальные методы: достаточно лишь знать, что объект принадлежит классу или наследнику класса, в котором объявлен метод. Одним из переводов слова virtual с английского языка может быть «фактический», что больше подходит по смыслу. Члены класса - это переменные состояния и методы этого класса, иными словами членами класса могут быть как переменные, так и функции. Функции и переменные, объявленные внутри объявления класса, становятся членами этого класса. Функции-члены класса будем называть методами этого класса. Пример с самолетами: Параметры унифицированного самолета, которые заданы, но еще не определенны, или действия, которые он должен делать (взлет/посадка) это виртуальные методы и члены класса.
42
Вложенные функции
**Nested functions** Вложенной называется функция, созданная внутри функции. Она может быть возвращена в качестве свойства нового объекта или сама по себе. `function sayHiBye(firstName, lastName) { // helper nested function to use below function getFullName() { return firstName + " " + lastName; } alert( "Hello, " + getFullName() ); alert( "Bye, " + getFullName() ); }` Здесь вложенная функция `getFullName()` сделана для удобства. Он может обращаться к внешним переменным и поэтому может возвращать полное имя. Вложенные функции довольно распространены в JavaScript. Что гораздо интереснее, вложенную функцию можно вернуть: либо как свойство нового объекта, либо как результат сам по себе. Затем его можно использовать в другом месте. Независимо от того, где, он по-прежнему имеет доступ к одним и тем же внешним переменным. `function makeCounter() { let count = 0; return function() { return count++; }; } let counter = makeCounter(); alert( counter() ); // 0 alert( counter() ); // 1 alert( counter() ); // 2` **Замыкание** — это функция , которая запоминает свои внешние переменные и может обращаться к ним. В некоторых языках это невозможно, или функция должна быть написана особым образом, чтобы это произошло. Но, как объяснялось выше, в JavaScript все функции по своей природе являются замыканиями (есть только одно исключение, которое будет рассмотрено в синтаксисе «новой функции» ).
43
Внутреннее и внешнее лексическое окружение
Внутреннее и внешнее лексическое окружение (или "scope") являются важными концепциями в JavaScript, определяющими доступность переменных и функций во время выполнения кода. **Внутреннее лексическое окружение (Inner Lexical Environment)** - это область видимости, в которой происходит определение и доступ к переменным и функциям внутри функции. Каждый раз, когда вызывается функция, создается новое внутреннее лексическое окружение для этой функции. Пример внутреннего лексического окружения: `function greeting() { const message = 'Hello'; console.log(message); } greeting(); // Выводит "Hello"` В примере выше, внутреннее лексическое окружение функции `greeting` содержит переменную `message`, которая объявлена внутри функции и доступна только в этой функции. **Внешнее лексическое окружение (Outer Lexical Environment)** - это область видимости, которая окружает текущее внутреннее лексическое окружение. Внешнее окружение предоставляет доступ к переменным и функциям, объявленным вне текущего внутреннего окружения. Пример внешнего лексического окружения: `const count = 10; function increment() { console.log(count); } increment(); // Выводит 10, так как функция имеет доступ к переменной count из внешнего лексического окружения` В примере выше, функция `increment` имеет доступ к переменной `count`, которая объявлена во внешнем лексическом окружении.
44
Всплытие и погружение (перехват) событий DOM
Распространение события - Event Propagation Когда какое-либо событие происходит в элементе DOM, оно на самом деле происходит не только в нем. Событие «распространяется» от объекта Window до вызвавшего его элемента (event.target). При этом событие последовательно пронизывает (затрагивает) всех предков целевого элемента. Распространение события имеет три стадии или фазы: - фаза погружения (capturing phase) – событие сначала идёт сверху вниз; - фаза цели (target phase) – событие достигло целевого(исходного) элемента; - фаза всплытия (bubbling stage) – событие начинает всплывать. При наступлении события, элемент, на котором оно произошло, помечается как «целевой» (`event.target`). Затем событие сначала двигается сверху вниз от корня документа (`document`) к целевому элементу, на каждом уровне (дочернем элементе) вызывая обработчики, назначенные через `addEventListener(type, listener, true)`, где `true` – это сокращение для `{capture: true}`. При достижении целевого элемента, обработчики вызываются на самом целевом элементе (`event.target`). Затем событие начинает "всплывать", т.е. двигается от целевого элемента вверх к корню документа, по пути вызывая обработчики, назначенные с префиксом `on` (например, `onclick`) или через `addEventListener(type, listener, false)` (или без третьего аргумента). Каждый обработчик имеет доступ к свойствам события `event`: - `event.target` – самый глубокий элемент, на котором произошло событие. - `event.currentTarget` (== `this`) – элемент, на котором в данный момент сработал обработчик (тот, которому назначен конкретный обработчик). - `event.eventPhase` – фаза, на которой сработал обработчик (1 - погружение, 2 - цели, 3 - всплытие). Любой обработчик может остановить событие вызовом методов: - `event.stopPropagation()` - препятствует дальнейшему всплытию события дальше по цепочке элементов. - `event.stopImmediatePropagation()` - не только предотвращает всплытие, но и останавливает обработку событий на текущем элементе. **Всплытие событий DOM** - это процесс последовательного срабатывания обработчиков события вверх по цепочке "родителей", начиная с объекта DOM, инициировавшего событие, до объекта document. Пример (learn.javascript.ru): Например, есть 3 вложенных элемента FORM > DIV > P с обработчиком на каждом: `
FORM
DIV

P

` При использовании модулей каждый модуль реализует свою функциональность и экспортирует её. Затем мы используем `import`, чтобы напрямую импортировать эту функциональность туда, где она необходима. Браузер загружает и анализирует скрипты модулей автоматически. В реальной жизни часто используется сборщик Webpack для объединения модулей. Это позволяет улучшить производительность и получить другие "плюшки" в работе с модулями. Вот все варианты export, которые мы разобрали в этой и предыдущих главах. Вы можете проверить себя, читая их и вспоминая, что они означают: Перед объявлением класса/функции/...: - `export [default] class/function/variable ...` Отдельный экспорт: - `export {x [as y], ...}` Реэкспорт: - `export {x [as y], ...} from "module"` - `export * from "module"` (не реэкспортирует `export default`) - `export {default [as y]} from "module"` (реэкспортирует только `export default`) Импорт: Именованные экспорты из модуля: - `import {x [as y], ...} from "module"` Импорт по умолчанию: - `import x from "module"` - `import {default as x} from "module"` Все сразу: - `import * as obj from "module"` Только подключить модуль (его код запустится), но не присваивать его переменной: - `import "module"` Мы можем разместить операторы import/export в начале или в конце скрипта, это не имеет значения.
92
Модули пакеты
Packages К модулям-пакетам относятся папки с кодом, описываемые при помощи находящегося в них файла package.json. С модулями-пакетами удобно работать при помощи менеджеров пакетов, таких как npm или yarn. Если мы хотим использовать уже написанные кем-то модули-пакеты (частый способ использования кода других разработчиков), их нужно установить, затем подключить, затем использовать. Установка модуля-пакета при помощи npm осуществляется командой `npm install <имя модуля>`
93
Модуль Path
Модуль Path Одним из стандартных модулей является path. Модуль path предназначен для того, чтобы работать с путями в Node.js. При помощи него можно получить имя файла, расширение файла, имя папки, указать путь к файлу. Чтобы использовать path, его необходимо подключить: `const path = require('path'); console.log(path.basename(__filename)); // index.js - имя файла на Windows, полный путь к файлу на POSIX-системах console.log(path.dirname(__filename)); // C:\Users\Admin\Desktop\nodejs-basic - название папки console.log(path.extname(__filename)); // .js - расширение файла console.log(path.parse(__filename)); // возвращает объект в котором указывается корень диска, имя папки, имя файла, расширение файла, имя файла без расширения path.join() объединяет заданные сегменты пути вместе, используя в качестве разделителя разделитель данной конкретной платформы (для Linux - прямой слэш, для Windows - обратный слэш), результат - относительный путь console.log(path.join(__dirname, 'test', 'second.html')); // вернет C:\Users\Admin\Desktop\nodejs-basic\test\second.html path.resolve() преобразует последовательность путей или сегментов пути в абсолютный путь справа налево и нормализует его: если в некоторых сегментах пути указываются слэши, а в некоторых нет, всё равно будет сгенерирован правильный путь. console.log(path.resolve(__dirname, './test', '/second.html'));`
94
NaN
Не число - NaN (not a number) Говорит о том, что выполнена бессмысленная операция (деление строки на число, бесконечности на бесконечность и тд). Относится к типу Number
95
Область видимости
Variable scope Область видимости в JavaScript определяет, какие переменные доступны вам. Существуют два типа областей видимости: глобальная и локальная. Глобальная область видимости — переменные и функции, объявленные в глобальном пространстве имен, имеют глобальную область видимости и доступны из любого места в коде. Существует вероятность пересечения имен, когда двум или более переменным присваивают одинаковое имя. Если переменные объявляются через const или let, то каждый раз, когда будет происходить пересечение имён, будет показываться сообщение об ошибке. Если объявлять переменные через var, то вторая переменная после объявления перепишет первую. Локальная область видимости включает: Функциональная область видимости (область видимости функции) — переменные, функции и параметры, объявленные внутри функции, доступны только внутри этой функции. Блочная область видимости — переменные (объявленные с помощью ключевых слов «let» и «const») внутри блока ({ }), доступны только внутри него. Блочная область видимости является частным случаем области видимости функции, т.к. функции объявляются с фигурными скобками (кроме случаев использования стрелочных функций с неявным возвращением значения). Область видимости — это также набор правил, по которым осуществляется поиск переменной. Если переменной не существует в текущей области видимости, ее поиск производится выше, во внешней по отношению к текущей области видимости. Если и во внешней области видимости переменная отсутствует, ее поиск продолжается вплоть до глобальной области видимости. Если в глобальной области видимости переменная обнаружена, поиск прекращается, если нет — выбрасывается исключение. Поиск осуществляется по ближайшим к текущей областям видимости и останавливается с нахождением переменной. Это называется цепочкой областей видимости (Scope Chain). Функции, объявленные как «function declaration» (прим. перев.: функция вида function имя(параметры) {...}), всегда поднимаются наверх в текущей области видимости. Если же функция объявляется как «function expression» (функциональное выражение) (прим. перев.: функция вида const f = function (параметры) {...}), то такая функция не поднимается в текущей области видимости. Вложенные области видимости Когда функция объявляется в другой функции, то внутренняя функция имеет доступ к переменным внешней функции. Такой поведение называется разграничением лексических областей видимости. В тоже время внешняя функция не имеет доступа к переменным внутренней функции. `function outerFunction () { const outer = `I'm the outer function!`; // function innerFunction() { const inner = `I'm the inner function!`; console.log(outer); // I'm the outer function! } // console.log(inner); // Ошибка, inner не определена }` Всякий раз, когда вы вызываете функцию внутри другой функции, вы создаете замыкание. Говорят, что внутренняя функция является замыканием. Результатом замыкания обычно является то, что в дальнейшем становятся доступными переменные внешней функции. `function outerFunction () { const outer = `I see the outer variable!`; // function innerFunction() { console.log(outer); } // return innerFunction; } // outerFunction()(); // I see the outer variable!` Так как внутренняя функция является возвращаемым значением внешней функции, то можно немного сократить код, совместив возврат значения с объявлением функции. `function outerFunction () { const outer = `I see the outer variable!`; // return function innerFunction() { console.log(outer); } } // outerFunction()(); // I see the outer variable!` Благодаря замыканиям появляется доступ к внешней функции, поэтому они обычно используются для двух целей: - контроля побочных эффектов; - создания приватных переменных.
96
Объявить переменную
declare variable Переменная - способ сохранить информацию и дать ей имя для последующего использования в коде. `const name = "Nastya"; let age = 31;`
97
В чем разница между let, const и var?
**VAR*** - Поддерживает повторное объявление переменных. Количество объявлений не ограничено. `var greeting = 'Hello world!'; var greeting = 'Hello Mary!'; // значение переменной теперь 'Hello Mary!'` - Игнорирует блочную область видимости. Переменные, объявленные с помощью `var`, будут видны за пределами блока. `{ var varVrb = 2; } console.log(varVrb); // 2` - Поддерживает hoisting. Объявление переменной перемещается вверх в пределах области видимости во время компиляции кода, поэтому можно использовать переменную до ее фактического объявления. `varVrb = 3; var varVrb;` - Значение может быть определено позже. Переменную можно объявить без присваивания значения, а затем присвоить значение позже. `var greeting; greeting = 'Hello world!';` - Значение может быть переопределено в будущем. После объявления переменную с помощью `var` можно переопределить, присвоив ей новое значение. `var greeting = 'Hello world!'; greeting = 'Hello Mary!'; // значение теперь 'Hello Mary!'` **`const` и `let`** - Не поддерживают повторное объявление. Если попытаться повторно объявить переменную с использованием `const` или `let`, будет выдана ошибка: `let greeting = 'Hello world!'; let greeting = 'Hello Karl!'; // SyntaxError: Identifier 'greeting' has already been declared` - Соблюдают блочную область видимости. Переменные, объявленные с помощью `const` или `let`, видны только внутри блока, в котором они объявлены: `{ const constVrb = 1; let letVrb = 2; } console.log(constVrb); // ReferenceError: constVrb is not defined console.log(letVrb); // ReferenceError: letVrb is not defined` - Не поддерживают hoisting. Объявление переменных с помощью `const` или `let` остается в области видимости, где они были объявлены. Поэтому необходимо сначала объявить переменную, а затем присвоить ей значение: `letVrb = 2; // ReferenceError: Cannot access 'letVrb' before initialization let letVrb; console.log(constVrb); // ReferenceError: constVrb is not defined console.log(letVrb); // ReferenceError: letVrb is not defined` - Значение должно быть определено при объявлении переменной (сразу) для константной переменной `const`: `const greeting = 'Hello world!';` - Значение константной переменной `const` не может быть переопределено после инициализации: `const greeting = 'Hello world!'; greeting = 'Hello Marry!'; // TypeError: Assignment to constant variable.` Итого: - Переменные, объявленные через var, могут быть глобальными или иметь область видимости в рамках функции; let и const имеют блочную область видимости. - var-переменные могут быть как обновлены, так и переопределены внутри области видимости; let-переменные можно обновлять, но не переопределять; const-переменные нельзя ни обновлять, ни переопределять. - Со всеми ними осуществляется поднятие наверх области видимости. Но если var-переменные при этом инициализируются как undefined, let и const не инициализируются. - В то время как var и let можно объявить, но не инициализировать, const необходимо инициализировать во время объявления.
98
Объясни разницу между изменяемыми и неизменяемыми значениями
Значения примитивных типов (например, строка или число) не могут быть изменены после того, как попали в память. Значения объектных типов (объекты, массивы) могут изменяться в ходе работы программы.
99
Объясни разницу между синхронными и асинхронными функциями
JavaScript - это однопоточный язык, то-есть функции выполняются в синхронном порядке. Приложение блокируется на время выполнения каждой конкретной функции. Так происходит по той причине, что JavaScript имеет только один стек вызовов. С другой стороны, есть асинхронный способ выполнения функций, когда мы не блокируем весь интерфейс благодаря тому, что не дожидаемся выполнения функции, а подписываемся на событие с передачей обратного вызова. Ну, или мы можем иметь дело с обещанием или с прочими внешними API вроде setTimeout. В таком случае браузер помещает обработчик события в очередь задач, а когда наступает время его вызвать, он перемещает его в стек вызовов.
100
Оператор и операнд
Operator and operand console.log(2 + 8); Оператор +, -, / и тд. Операнды 2 и 8.
101
Операторы инкремент и декремент
Increment and decrement operators Унарные операторы, которые добавляют или вячитают единицу от своего операнда. ++ инкремент -- декремент -- 1 предекремент ++1 преинкремент 1 -- постдекремени 1 ++ постинкремент
102
Операторы сравнения
Comparison operators Операторы сравнения возвращают значения логического типа. Строки сравниваются посимвольно в лексикографическом порядке. Значения разных типов при сравнении приводятся к числу. Исключением является сравнение с помощью операторов строгого равенства/неравенства. Значения null и undefined равны == друг другу и не равны любому другому значению. Будьте осторожны при использовании операторов сравнений вроде > и < с переменными, которые могут принимать значения null/undefined. Хорошей идеей будет сделать отдельную проверку на null/undefined. Значение NaN считается не равным никакому другому значению, включая само себя. При его наличии оператор равенства всегда возвращает false, а оператор неравенства – true. Если оба операнда являются объектами, то они сравниваются, чтобы выяснить, один ли это объект. Если да, возвращается true, иначе – false. - `==` - оператор равенства. Сравнивает значения с приведением типов, поэтому может быть неоднозначным. Например, `1 == '1'` вернет `true`, так как значения равны после приведения типов. - `===` - оператор строгого равенства. Сравнивает значения без приведения типов. В этом случае, `1 === '1'` вернет `false`, так как значения имеют различные типы. - `!=` - оператор неравенства. Сравнивает значения с приведением типов. Например, `1 != '1'` вернет `false`, так как значения равны после приведения типов. - `!==` - оператор строгого неравенства. Сравнивает значения без приведения типов. В этом случае, `1 !== '1'` вернет `true`, так как значения имеют различные типы. - `>` - оператор больше. Проверяет, является ли первое значение больше второго. - `<` - оператор меньше. Проверяет, является ли первое значение меньше второго. - `>=` - оператор больше или равно. Проверяет, является ли первое значение больше или равно второму. - `<=` - оператор меньше или равно. Проверяет, является ли первое значение меньше или равно второму. - `&&` - оператор логического И. Возвращает true, если оба операнда истинны. - `||` - оператор логического ИЛИ. Возвращает true, если хотя бы один из операндов истинен. - `!` - оператор логического отрицания. Инвертирует значение операнда.
103
Очередь (Структуры данных)
Queue Как и стек очереди могут быть реализованы с помощью связного списка или массива. Очереди — это FIFO-структуры данных (first in, first out). Аналог очереди - очередь в магазине: первого покупателя обслужат первым Элементы удаляются из головы, а добавляются в хвост. Эффективность списка, стека, очереди («О» большое): Индексирование: O(n). Поиск: O(n). Двоичный поиск: O(n). Вставка: O(1). Основные операции: добавление нового элемента в конец очереди (enqueue); удаление элемента из начала очереди (dequeue); чтение элемента из начала очереди без удаления (peek). Как и стек, очередь может быть реализована как на базе массива, так и на базе связного списка. И опять же, массивы в JavaScript фактически могут работать как очереди, благодаря встроенным методам. `class Queue { constructor() { this.linkedList = new LinkedList(); } // isEmpty() { return !this.linkedList.head; } // peek() { if (!this.linkedList.head) { return null; } // return this.linkedList.head.value; } // enqueue(value) { this.linkedList.append(value); } // dequeue() { const removedHead = this.linkedList.deleteHead(); return removedHead ? removedHead.value : null; } }`
104
Параметры по умолчанию
Default Parameters Параметры функции по умолчанию позволяют инициализировать параметры со значениями по умолчанию, если значение не передано или не определено. `function add(a = 0, b = 0){ return a + b } // если мы не присвоим переменным "a" и "b" какие-нибудь значения, они будут равняться 0 add(1) // 1` Инициализаторы параметров по умолчанию живут в своей собственной области, которая является родителем области, созданной для тела функции. `function f(a = go()) { function go() { return ":P"; } } f(); // ReferenceError: go is not defined`
105
Параметры функции
В JavaScript параметры — это переменные, которые мы перечисляем в объявлении функции. Когда мы создаем функцию, мы берем параметры, которые включены в определение функции. Внутри круглых скобок можно добавить несколько параметров, разделенных запятыми, как показано в приведенном ниже синтаксисе: `имя функции ( параметр1 , параметр2 , параметр3 ) { // тело функции }` Значение, которое мы передаем функции, называется аргументом функции. Аргументы передаются функции, когда мы ее вызываем. Вы можете определить любое количество параметров для вашей функции JavaScript, и количество добавляемых аргументов не должно совпадать с количеством параметров. Вы также можете определить значения параметров по умолчанию при объявлении функций. Параметры оцениваются в JavaScript слева направо. Функция JavaScript не выдаст никакой ошибки, если переданные вами аргументы больше или меньше количества параметров. Недостающие параметры будут определены как undefined,а лишние - будут проигнорированы. Существует еще один метод для доступа к аргументам внутри нашей функции, который называется « Arguments Object ». Объект Arguments содержит значения аргументов в объекте с механизмом, подобным массивам.
106
Передать динамическое количество параметров функции
В JavaScript есть несколько способов передачи динамического количества параметров функции: 1. Аргументы переменной длины с помощью объекта `arguments`: Внутри тела функции вы можете использовать объект `arguments`, который представляет все аргументы, переданные в функцию. `arguments` похож на массив, но не является полноценным массивом. Вы можете получить доступ к каждому аргументу по индексу. Например: `function sum() { let total = 0; for (let i = 0; i < arguments.length; i++) { total += arguments[i]; } return total; } // console.log(sum(1, 2, 3)); // Output: 6 console.log(sum(1, 2, 3, 4, 5)); // Output: 15` 2. Оператор "Rest" (`...`): Можно использовать оператор "Rest" для сбора дополнительных аргументов в виде массива. Оператор "Rest" должен быть последним параметром функции. Например: `function sum(...numbers) { let total = 0; for (let i = 0; i < numbers.length; i++) { total += numbers[i]; } return total; } // console.log(sum(1, 2, 3)); // Output: 6 console.log(sum(1, 2, 3, 4, 5)); // Output: 15`
107
Передача параметров по значению и по ссылке
**Передача параметров по значению** Строки, числа, логические значения передаются в функцию по значению. Иными словами при передаче значения в функцию, эта функция получает копию данного значения. Рассмотрим, что это значит в практическом плане: `function change(x){ x = 2 * x; console.log("x in change:", x); } // var n = 10; console.log("n before change:", n); // n before change: 10 change(n); // x in change: 20 console.log("n after change:", n); // n after change: 10` Функция `change` получает некоторое число и увеличивает его в два раза. При вызове функции change ей передается число n. Однако после вызова функции мы видим, что число n не изменилось, хотя в самой функции произошло увеличение значения параметра. Потому что при вызове функция change получает копию значения переменной n. И любые изменения с этой копией никак не затрагивают саму переменную n. **Передача по ссылке** Объекты и массивы передаются по ссылке. То есть функция получает сам объект или массив, а не их копию. `function change(user){ user.name = "Tom"; } // var bob ={ name: "Bob" }; console.log("before change:", bob.name); // Bob change(bob); console.log("after change:", bob.name); // Tom` В данном случае функция change получает объект и меняет его свойство name. В итоге мы увидим, что после вызова функции изменился оригинальный объект bob, который передавался в функцию. Однако если мы попробуем переустановить объект или массив полностью, оригинальное значение не изменится. `function change(user){ // полная переустановка объекта user= { name:"Tom" }; } // var bob ={ name: "Bob" }; console.log("before change:", bob.name); // Bob change(bob); console.log("after change:", bob.name); // Bob` То же самое касается массивов.
108
Перемешивание Фишера-Ейтса
Fisher-Yates shuffle algorithm `function fyShuffle(arr) { let i = arr.length; while (--i > 0) { let randIndex = Math.floor(Math.random() * (i + 1)); [arr[randIndex], arr[i]] = [arr[i], arr[randIndex]]; } return arr; }`
109
Поверхностное и глубокое копирование
**Shallow or deep clone** _Глубокое копирование_ Если необходимо полностью скопировать сложную структуру данных, например, массив с объектами, то нужно делать глубокое (deep) или полное копирование данных. 1. JavaScript не содержит функций для глубокого копирования, лучший вариант сделать глубокую копию — сериализовать структуру в JSON и тут же распарсить. `const newArr = JSON.parse(JSON.stringify(arr))` 2. Для глубокой копии можно написать функцию `const deepClone = obj => { if (obj === null) return null; // Создаем поверхностный клона оригинала. let clone = Object.assign({}, obj); // // Определяем, какие пары ключ-значение // необходимо глубоко клонировать. Object.keys(clone).forEach( key => (clone[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key]) ); // // Проверяем является ли obj массивом и не пустой ли он. return Array.isArray(obj) && obj.length // Если obj массив и он не пуст, тогда // указываем объекту clone длину исходного массива что бы // конвертировать clone в массив и вернуть его. ? (clone.length = obj.length) && Array.from(clone) // Если obj пустой массив, : Array.isArray(obj) // то возвращаем его ? Array.from(obj) // в других случаях obj это объект и мы возвращаем копию clone. : clone; }; // // Пример: const a = { foo: "bar", obj: { a: 1, b: 2 } }; const b = deepClone(a); // a !== b true // a.obj !== b.obj true` 3. Воспользоваться готовой библиотекой. Например, функцию глубокого копирования содержит популярная библиотека утилит lodash. 4. Использовать structuredClone `const obj = { name: "Mike", friends: [{ name: "Sam" }] }; const clonedObj = structuredClone(obj); console.log(obj === clonedObj); // false console.log(obj.friends === clonedObj.friends); // false` _Неглубокое копирование_ 1. Spread syntax, появившийся в ES6, позволяет «вытаскивать» перебираемые элементы из своего контейнера. 2. Клонировать/копировать содержимое массива, можно через метод slice, передав 0 в качестве первого аргумента: `var clone = myArray.slice(0);` Код выше создает копию исходного массива; имейте в виду, если в вашем массиве существуют объекты - они хранятся как ссылки; т.е. код выше не делает “deep” клонирование содержимого массива. 3. Function.prototype.apply() `var first = [1, 2, 3]; var second = [4, 5]; Array.prototype.push.apply(first, second); console.log(first); //[1,2,3,4,5] console.log(second); // [4,5]` 4. Другой способ с использованием Array.from: `const cloneSheeps = Array.from(sheeps)`
110
Поднятие (всплытие)
Hoisting Механизм, из-за которого переменные и объявления функций передвигаются вверх своей области видимости перед тем, как код будет выполнен. Код выполняется в рамках контекста выполнения (среды, в которой выполняется код). Контекст выполнения имеет две фазы — компиляция и выполнение. 1. В фазе компиляции function declaration и переменные, объявленные с помощью ключевого слова «var» поднимаются в самый верх глобальной (или функциональной) области видимости. 2. В фазе выполнения переменным присваиваются значения, а функции вызываются или выполняются. _temporal dead zone_ Временная мертвая зона (TDZ) — это область блока, в которой переменная недоступна до момента, когда ей будет инициализировано значение. Блок представляет собой пару фигурных скобок ({...}), используемых для группировки. Инициализация происходит, когда вы присваиваете начальное значение переменной. Предположим, вы пытаетесь получить доступ к переменной до ее полной инициализации. В таком случае JavaScript выдаст ошибку ReferenceError. Если коротко, мы должны сначала инициализировать переменную значением, а потом уже обращаться к ней в коде. `let bestFood = "Vegetable Fried Rice"; // bestFood’s TDZ ends here console.log(bestFood);` Основное различие между временной мертвой зоной переменной var, let, и constпеременной заключается в том, когда заканчивается их TDZ. TDZ переменной let(или const) заканчивается, когда JavaScript полностью инициализирует ее значением, указанным при ее объявлении. А var TDZ переменной заканчивается сразу после ее подъема, а не тогда, когда переменная полностью инициализируется значением, указанным при ее объявлении. Область видимости — это место, где (или откуда) мы имеем доступ к переменным или функциям. JS имеем три типа областей видимости: глобальная, функциональная и блочная (ES6).
111
Преобразование типов
1. **undefined**: - В строку: `String(undefined)` дает результат `"undefined"`. - В число: `Number(undefined)` дает результат `NaN`. - В логический тип: `Boolean(undefined)` дает результат `false`. 2. **null**: - В строку: `String(null)` дает результат `"null"`. - В число: `Number(null)` дает результат `0`. - В логический тип: `Boolean(null)` дает результат `false`. 3. **Булевый тип (true/false)**: - В строку: `String(true)` дает результат `"true"`. `String(false)` дает результат `"false"`. - В число: `Number(true)` дает результат `1`. `Number(false)` дает результат `0`. - В логический тип: `Boolean(true)` дает результат `true`. `Boolean(false)` дает результат `false`. - При преобразовании в объектный тип: `Object(true)` и `Object(false)` создают обертки объектов Boolean. 4. **Числовой тип**: - В строку: `String(123)` дает результат `"123"`. - В число: `Number("123")` дает результат `123`. Если строка содержит некорректные символы или является пустой, результат будет `NaN`. - В логический тип: Любое число, кроме `0`, `NaN` или пустой строки, преобразуется в `true`. Число `0`, `NaN` или пустая строка преобразуются в `false`. - При преобразовании в объектный тип: `Object(123)` создает обертку объекта Number. 4.1. **NaN**: - В строку: `String(NaN)` дает результат `"NaN"`. - В число: `Number(NaN)` дает результат `NaN`. - В логический тип: `Boolean(NaN)` дает результат `false`. - При преобразовании в объектный тип: `Object(NaN)` создает объектное представление числа NaN. 4.2. **Infinity**: - В строку: `String(Infinity)` дает результат `"Infinity"`. - В число: `Number(Infinity)` дает результат `Infinity`. - В логический тип: `Boolean(Infinity)` дает результат `true`. - При преобразовании в объектный тип: `Object(Infinity)` создает объектное представление числа Infinity. 4.3. **-Infinity**: - В строку: `String(-Infinity)` дает результат `"-Infinity"`. - В число: `Number(-Infinity)` дает результат `-Infinity`. - В логический тип: `Boolean(-Infinity)` дает результат `true`. - При преобразовании в объектный тип: `Object(-Infinity)` создает объектное представление числа -Infinity. 5. **Строковый тип**: - В число: `Number("123")` дает результат `123`. Если строка содержит некорреткные символы или является пустой, результат будет `NaN`. - В логический тип: Пустая строка преобразуется в `false`, а непустая строка преобразуется в `true`. - При преобразовании в объектный тип: `Object("Hello")` создает обертку объекта String со значением "Hello". 6. **Объектный тип**: - В строку: Объект вызывает метод `toString()`. Если метод `toString()` не переопределен, то возвращается строка "[object Object]". - В число: Объект вызывает метод `valueOf()`. Если метод `valueOf()` не переопределен, то вызывается метод `toString()`, а затем результат пытается быть преобразован в число. Если преобразование не удается, результат будет `NaN`. - В логический тип: Любой объект, кроме `null` и `undefined`, преобразуется в `true`.
112
Потеря контекста
Как только метод передаётся отдельно от объекта – this теряется. `let user = { firstName: "Вася", sayHi() { alert(Привет, ${this.firstName}!); } }; setTimeout(user.sayHi, 1000); // Привет, undefined!` setTimeout получил функцию sayHi отдельно от объекта user (именно здесь функция и потеряла контекст). Метод setTimeout в браузере имеет особенность: он устанавливает this=window для вызова функции. Таким образом, для this.firstName он пытается получить window.firstName, которого не существует. В других подобных случаях this обычно просто становится undefined. **Решения:** 1. функция обертка (user достаётся из замыкания, а затем вызывается его метод sayHi) - хуже `setTimeout(function() { user.sayHi(); // Привет, Вася! }, 1000); 2. или setTimeout(() => user.sayHi(), 1000); // Привет, Вася!` 2. привязать контекст с помощью bind - лучше В современном JavaScript у функций есть встроенный метод bind, который позволяет зафиксировать this. `let user = { firstName: "Вася", sayHi() { alert(Привет, ${this.firstName}!); } }; // let sayHi = user.sayHi.bind(user); // берём метод user.sayHi и привязываем его к user.Теперь sayHi – это «связанная» функция, которая может быть вызвана отдельно или передана в setTimeout (контекст всегда будет правильным). // sayHi(); // Привет, Вася! // setTimeout(sayHi, 1000); // Привет, Вася!` _Интересно:_ Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле через метод: bindAll
113
Полифилы
Полифил — это фрагмент кода (в сети — обычно JavaScript), который позволяет использовать современную функциональность в более старых браузерах, которые не поддерживают ее по умолчанию.
114
Полифил forEach
`if (!Array.prototype.forEach) { // Проверяем, не был ли уже добавлен метод forEach // Array.prototype.myForEach = function(callback) { // Добавляем свой собственный метод myForEach в прототип массива // if (!this instanceof Array) { // Проверяем, является ли текущий объект массивом throw new Error("Isn't array"); // Если объект не является массивом, выбрасываем ошибку } // if (typeof callback !== "function") { // Проверяем, является ли callback функцией throw new Error("Callback isn't function"); // Если callback не является функцией, выбрасываем ошибку } // for (let i = 0; i < this.length; i += 1) { // Итерируем по элементам массива callback(this[i], i, this); // Вызываем callback для каждого элемента массива, передавая значение элемента, индекс и сам массив } }; }`
115
Преобразование числа в двоичное и обратно в десятеричное
Двоичная система счисления — это позиционная система счисления с основанием 2. В этой системе счисления числа записываются с помощью двух символов: 0 и 1. **Из десятеричного в двоичное:** Функция `toString(2)` при использовании с числовым объектом возвращает двоичный эквивалент числового значения, как показано в примерах ниже. `(8).toString(2)// "1000" (25).toString(2)// "11001" (235).toString(2)// "11101011"` 8..toString(2) - если нам надо вызвать метод непосредственно на числе, как toString в примере выше, то нам надо поставить две точки .. после числа. Если мы поставим одну точку: 123456.toString(36), тогда это будет ошибкой, поскольку синтаксис JavaScript предполагает, что после первой точки начинается десятичная часть. Своя функция: `function convertDecToBinary(num) { const binary = []; while (num >= 1) { binary.unshift(num % 2); // получаем остаток от деления на 2 (1 или 0) и пушим в начало массива num = Math.floor(num / 2); // меняем num - делим пополам и округляем к меньшему } return +binary.join(""); // массив переводим в строку и делаем числом }` **Из двоичного в десятеричное:** ES6 поддерживает двоичные числовые литералы для целых чисел, поэтому, если двоичная строка неизменяема, можно просто ввести ее как есть с префиксом 0b или 0B: `const binary = 0b1101000; // code for 104 console.log(binary); // prints 104` Функция parseInt(value,2) принимает строку и систему исчисления числа строки в качестве аргумента и возвращает целое число в десятичной системе исчисления. `parseInt("1111", 2); //15`
116
Префиксное дерево (Структуры данных)
Prefix tree или trie Префиксное дерево (также бор, луч, нагруженное или суффиксное дерево) в информатике - упорядоченная древовидная структура данных, которая используется для хранения динамических множеств или ассоциативных массивов, где ключом обычно выступают строки. Дерево называется префиксным, потому что поиск осуществляется по префиксам. В отличие от бинарного дерева, узлы не содержат ключи, соответствующие узлу. Представляет собой корневое дерево, каждое ребро которого помечено каким-то символом так, что для любого узла все рёбра, соединяющие этот узел с его сыновьями, помечены разными символами. Некоторые узлы префиксного дерева выделены (на рисунке они подписаны цифрами) и считается, что префиксное дерево содержит данную строку-ключ тогда и только тогда, когда эту строку можно прочитать на пути из корня до некоторого выделенного узла. Таким образом, в отличие от бинарных деревьев поиска, ключ, идентифицирующий конкретный узел дерева, не явно хранится в данном узле, а неявно задаётся положением данного узла в дереве. Получить ключ можно выписыванием подряд символов, помечающих рёбра на пути от корня до узла. Ключ корня дерева — пустая строка. Часто в выделенных узлах хранят дополнительную информацию, связанную с ключом, и обычно выделенными являются только листья и, возможно, некоторые внутренние узлы. Используется, к примеру, для автозаполнения.
117
Префиксный, инфиксный, постфиксный оператор
Prefix, infix, postfix operator Префиксный +5 Инфиксный 3 + 5 Постфиксный 5 +
118
Принцип DRY
don’t repeat yourself / не повторяйте себя Если код не дублируется, то для изменения логики достаточно внесения исправлений всего в одном месте и проще тестировать одну (пусть и более сложную) функцию, а не набор из десятков однотипных. Следование принципу DRY всегда приводит к декомпозиции сложных алгоритмов на простые функции. А декомпозиция сложных операций на более простые (и повторно используемые) значительно упрощает понимание программного кода. Повторное использование функций, вынесенных из сложных алгоритмов, позволяет сократить время разработки и тестирования новой функциональности. Следование принципу DRY приводит к модульной архитектуре приложения и к чёткому разделению ответственности за бизнес-логику между программными классами. А это — залог сопровождаемой архитектуры. Хотя чаще не DRY приводит к модульности, а уже модульность, в свою очередь, обеспечивает принципиальную возможность соблюдения этого принципа в больших проектах. В рамках одного программного класса (или модуля) следовать DRY и не повторяться обычно достаточно просто. Также не требует титанических усилий делать это в рамках небольших проектов, где все разработчики «владеют» всем кодом системы. А вот в больших проектах ситуация с DRY несколько сложнее — повторы чаще всего появляются из-за отсутствия у разработчиков целостной картины или несогласованности действий в рамках команды. Следовать принципу «don’t repeat yourself» в рамках больших проектов не так просто, как это может показаться на первый взгляд. От разработчиков требуется тщательное планирование архитектуры, а от архитектора или тимлида требуется наличие видения системы в целом и чёткая постановка задач разработчикам. В проектировании DRY тоже имеет место — доступ к конкретному функционалу должен быть доступен в одном месте, унифицирован и сгруппирован по какому-либо принципу, а не «разбросан» по системе в произвольных вариациях. Этот подход пересекается с принципом единственной ответственности из пяти принципов SOLID, сформулированных Робертом Мартином.
119
Принцип KISS
Keep It Short and Simple В проектировании следование принципу KISS выражается в том, что: - не имеет смысла реализовывать дополнительные функции, которые не будут использоваться вовсе или их использование крайне маловероятно, как правило, большинству пользователей достаточно базового функционала, а усложнение только вредит удобству приложения; - не стоит перегружать интерфейс теми опциями, которые не будут нужны большинству пользователей, гораздо проще предусмотреть для них отдельный «расширенный» интерфейс (или вовсе отказаться от данного функционала); - бессмысленно делать реализацию сложной бизнес-логики, которая учитывает абсолютно все возможные варианты поведения системы, пользователя и окружающей среды, — во-первых, это просто невозможно, а во-вторых, такая фанатичность заставляет собирать «звездолёт», что чаще всего иррационально с коммерческой точки зрения. В программировании следование принципу KISS можно описать так: - не имеет смысла беспредельно увеличивать уровень абстракции, надо уметь вовремя остановиться; - бессмысленно закладывать в проект избыточные функции «про запас», которые может быть когда-нибудь кому-либо понадобятся (тут скорее правильнее подход согласно принципу YAGNI); - не стоит подключать огромную библиотеку, если вам от неё нужна лишь пара функций; - декомпозиция чего-то сложного на простые составляющие — это архитектурно верный подход (тут KISS перекликается с DRY); абсолютная математическая точность или предельная детализация нужны не всегда — большинство систем создаются не для запуска космических шаттлов, данные можно и нужно обрабатывать с той точностью, которая достаточна для качественного решения задачи, а детализацию выдавать в нужном пользователю объёме, а не в максимально возможном объёме. Также KISS имеет много общего c принципом разделения интерфейса из пяти принципов SOLID, сформулированных Робертом Мартином.
120
Принцип YAGNI
«Вам это не понадобится» Если упрощенно, то следование данному принципу заключается в том, что возможности, которые не описаны в требованиях к системе, просто не должны реализовываться. Это позволяет вести разработку, руководствуясь экономическими критериями — Заказчик не должен оплачивать ненужные ему функции, а разработчики не должны тратить своё оплачиваемое время на реализацию того, что не требуется. Основная проблема, которую решает принцип YAGNI — это устранение тяги программистов к излишней абстракции, к экспериментам «из интереса» и к реализации функционала, который сейчас не нужен, но, по мнению разработчика, может либо вскоре понадобиться, либо просто будет полезен, хотя в реальности такого очень часто не происходит.
121
Принцип SOLID
SOLID — это аббревиатура пяти основных принципов проектирования в объектно-ориентированном программировании — Single responsibility — принцип единственной ответственности Open-closed — принцип открытости / закрытости Liskov substitution — принцип подстановки Барбары Лисков Interface segregation — принцип разделения интерфейса Dependency inversion — принцип инверсии зависимостей Принцип единственной обязанности / ответственности (single responsibility principle / SRP) обозначает, что каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс. Все его сервисы должны быть направлены исключительно на обеспечение этой обязанности. Подробнее про SRP... Принцип открытости / закрытости (open-closed principle / OCP) декларирует, что программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения. Это означает, что эти сущности могут менять свое поведение без изменения их исходного кода. Подробнее про OCP... Принцип подстановки Барбары Лисков (Liskov substitution principle / LSP) в формулировке Роберта Мартина: «функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа не зная об этом». Подробнее про LSP... Принцип разделения интерфейса (interface segregation principle / ISP) в формулировке Роберта Мартина: «клиенты не должны зависеть от методов, которые они не используют». Принцип разделения интерфейсов говорит о том, что слишком «толстые» интерфейсы необходимо разделять на более маленькие и специфические, чтобы клиенты маленьких интерфейсов знали только о методах, которые необходимы им в работе. В итоге, при изменении метода интерфейса не должны меняться клиенты, которые этот метод не используют. Подробнее про ISP... Принцип инверсии зависимостей (dependency inversion principle / DIP) — модули верхних уровней не должны зависеть от модулей нижних уровней, а оба типа модулей должны зависеть от абстракций; сами абстракции не должны зависеть от деталей, а вот детали должны зависеть от абстракций. Подробнее про DIP...
122
Пройти циклом по ключам объекта
1. Использование цикла `for..in`: `const obj = {a: 1, b: 2, c: 3}; for (let key in obj) { console.log(key); // Выводит каждый ключ объекта console.log(obj[key]); // Выводит значение для каждого ключа }` 2. Использование метода `Object.keys()`: `const obj = {a: 1, b: 2, c: 3}; Object.keys(obj).forEach(key => { console.log(key); // Выводит каждый ключ объекта console.log(obj[key]); // Выводит значение для каждого ключа });` 3. Использование метода `Object.getOwnPropertyNames()`: `const obj = {a: 1, b: 2, c: 3}; Object.getOwnPropertyNames(obj).forEach(key => { console.log(key); // Выводит каждый ключ объекта console.log(obj[key]); // Выводит значение для каждого ключа });` 4. Использование метода `Object.entries()` вместе с циклом `for..of`: `const obj = {a: 1, b: 2, c: 3}; for (let [key, value] of Object.entries(obj)) { console.log(key); // Выводит каждый ключ объекта console.log(value); // Выводит значение для каждого ключа }`
123
Что такое промис?
Promise (обещание) (resolve, reject) Промис (Promise) — специальный объект JavaScript, который используется для написания и обработки асинхронного кода. Асинхронные функции возвращают объект Promise в качестве значения. Промисы были придуманы для решения проблемы так называемого «ада функций обратного вызова». Промис может находиться в одном из трёх состояний: 1. pending (в ожидании) — стартовое состояние, операция стартовала; 2. - fulfilled (выполнено) — получен результат; или - rejected (отклонено) — ошибка. Поменять состояние можно только один раз: перейти из pending либо в fulfilled, либо в rejected. В качестве параметров конструктор промиса принимает resolve и reject. В resolve записывается результат выполнения операции, в - reject — причина невыполнения операции. Результат может быть обработан в методе .then, ошибка — в методе .catch. Метод then() используют, чтобы выполнить код после успешного выполнения асинхронной операции. Метод .then также возвращает промис, поэтому мы можем использовать цепочку, состоящую из нескольких .then. Метод catch() используют, чтобы выполнить код в случае ошибки при выполнении асинхронной операции. Метод finally() используют, чтобы выполнить код при завершении асинхронной операции. Он будет выполнен вне зависимости от того, была ли операция успешной или завершилась ошибкой. Синтаксис создания Promise: var promise = new Promise(function(resolve, reject) { // Эта функция будет вызвана автоматически // В ней можно делать любые асинхронные операции, // А когда они завершатся — нужно вызвать одно из: // resolve(результат) при успешном выполнении // reject(ошибка) при ошибке }) Универсальный метод для навешивания обработчиков: promise.then(onFulfilled, onRejected) onFulfilled – функция, которая будет вызвана с результатом при resolve. onRejected – функция, которая будет вызвана с ошибкой при reject. С помощью методов then(), catch() и finally() мы можем реагировать на изменение состояния промиса и выполнять код. `// Создаётся объект promise let promise = new Promise((resolve, reject) => { setTimeout(() => { // переведёт промис в состояние fulfilled с результатом "result" resolve("result"); }, 1000); }); // // promise.then навешивает обработчики на успешный результат или ошибку promise.then( (result) => { // первая функция-обработчик - запустится при вызове resolve alert("Fulfilled: " + result); // result - аргумент resolve }, (error) => { // вторая функция - запустится при вызове reject alert("Rejected: " + error); // error - аргумент reject } );` Promise.all — это промис, который принимает массив промисов в качестве входных данных (итерируемый), и он разрешается, когда все промисы разрешаются или любое из них отклоняется. Например, синтаксис метода promise.all приведен ниже: Promise.all([Promise1, Promise2, Promise3]) .then(result) => { console.log(result) }) .catch(error => console.log(`Error in promises ${error}`))
124
Псевдомассивы
Array-likes Псевдомассивы - это объекты, у которых есть индексы и length.
125
Разница между slice и splice
Array.splice («splice» — «наращивать, сращивать») принимает начальный (позицию) и необязательный конечный (количество элементов) аргументы и изменяет содержимое массива, удаляя существующие элементы и/или добавляя новые. Возвращается массив, содержащий удалённые элементы. Если будет удалён только один элемент, вернётся массив из одного элемента. Если никакие элементы не будут удалены, вернётся пустой массив. Используется для вставки или удаления элементов в/из массива. `var x = [14, 3, 77]; var y = x.splice(1, 2); console.log(x); // [14] console.log(y); // [3, 77]` Array.slice (срез) принимает два аргумента, начальный и необязательный конечный. Далее он возвращает новый массив, содержащий элементы начиная с указанного начального индекса вплоть до элемента, расположенного сразу перед конечным индексом. Если вы опустите второй аргумент, он будет выбран до конца. Он не изменяет исходный массив. Используется для выбора элементов из массива `var x = [14, 3, 77]; var y = x.slice(1, 2); console.log(x); // [14, 3, 77] console.log(y); // [3]` Общий пример: `[14, 3, 77].slice(1, 2) // [3] [14, 3, 77].splice(1, 2) // [3, 77]`
126
Разница между scope и context
Область видимости относится к видимости переменных, а контекст относится к объекту, в котором выполняется функция. Область действия : в JavaScript область действия достигается за счет использования функций. Когда вы используете ключевое слово «var» внутри функции, инициализируемая вами переменная является частной и не может быть видна за пределами этой функции. Но если внутри этой функции есть функции, то эти «внутренние» функции могут «видеть» эту переменную; говорят, что эта переменная находится в области видимости. Функции могут «видеть» переменные, объявленные внутри них. Они также могут «видеть» все, что объявлено вне их, но никогда не могут видеть те, которые объявлены внутри функций, вложенных в эту функцию. Это область видимости в JavaScript. Контекст : относится к объекту, в котором выполняется функция. Когда вы используете ключевое слово JavaScript «this», это слово относится к объекту, в котором выполняется функция.
127
Разница между window и document
`window` и `document` - это два разных объекта, которые управляются браузером и предоставляют различные функциональности при работе с HTML-страницей. `window` объект представляет текущее окно браузера и предоставляет доступ к различным свойствам и методам, таким как открытие и закрытие окон, установка таймеров, управление размерами и положением окна, обращение к URL-адресу текущей страницы и многое другое. Он также является глобальным объектом JavaScript в браузере, что означает, что его свойства и методы могут быть доступны из любого места в JavaScript-коде. `document` объект представляет текущий HTML-документ, загруженный в окне браузера, и предоставляет доступ к различным методам и свойствам, связанным с документом. Например, через `document` вы можете получить доступ и изменять содержимое документа, создавать новые элементы, добавлять обработчики событий, изменять стили элементов и многое другое. Он также является частью DOM (Document Object Model), который представляет структуру и содержимое HTML-документа в виде дерева объектов, к которым можно обращаться и изменять. Вкратце говоря, `window` отвечает за окно браузера и его функциональности, в то время как `document` относится к HTML-документу, его структуре и возможности работы с ним.
128
Разница между функцией и методом
Разница между функцией и методом заключается в их контексте и способе вызова. **Функция** - это совокупность инструкций, которая выполняет определенную операцию или вычисление. Функции могут быть определены независимо от объектов и вызываться в любом месте кода. Они могут принимать аргументы и возвращать значения. Пример: `function greet(name) { console.log(`Hello, ${name}!`); } greet('John'); // Output: Hello, John!` **Метод** - это функция, которая принадлежит определенному объекту или классу. Методы могут выполнять различные операции с данными объекта и имеют доступ к его свойствам. Они вызываются через объект с использованием точечной нотации. Пример: `const person = { name: 'John', greet: function() { console.log(`Hello, ${this.name}!`); } }; person.greet(); // Output: Hello, John!`
129
Разница циклов for of и for in
Оператор for...of выполняет цикл обхода итерируемых объектов (включая Array, Map, Set, объект аргументов и подобных), вызывая на каждом шаге итерации операторы для каждого значения из различных свойств объекта for ... of цикл работает только с повторяемыми объектами. В JavaScript итерируемые объекты - это объекты, которые можно перебирать. String, Array, TypedArray, Map и Set - все это встроенные итерируемые объекты, поскольку каждый из их объектов-прототипов реализует метод @@iterator. Цикл for...in проходит через перечисляемые свойства объекта. Он пройдёт по каждому отдельному элементу. for ... in работает с теми свойствами, у которых enumerable flag установлен в true: Перечислимый флаг для свойств, созданных с помощью простого присваивания или инициализатора свойств, по умолчанию равен true. Перечислимый флаг для свойств, созданных с помощью Object.defineProperty, по умолчанию равен false. Также работает со строками и массивами, поскольку перечислимый флаг для свойств строки и массива также по умолчанию имеет значение true. Итого: for...of - Используется для перебора строк и массивов. for...in - Используйте для перебора объектов. От себя добавлю, что for...of поставляется с ES2015 и в настоящее время является лучшей практикой (по моему опыту) потому что: Более читаемый, Без обратных вызовов, Быстрее (если верить тестам), Вы также можете использовать .entries() и деструктурирование.
130
Распространение событий
Когда мы нажимаем на какой-либо элемент на станице и генерируется событие нажатия, то это событие может распространяться от элемента к элементу. Например, если мы нажимаем на блок div, то также мы нажимаем и на элемент body, в котором блок div находится. То есть происходит распространение события. Есть несколько форм распространения событий: Восходящие: событие распространяется вверх по дереву DOM от дочерних узлов к родительским. Если такое поведение является нежелаемым, мы можем остановить распространение событие с помощью метода stopPropagation() объекта Event: var redRect = document.getElementById("redRect"); redRect.addEventListener("click", function(e){ console.log("Событие на redRect"); e.stopPropagation(); }); внутренний элемент (кликнутый) - > его родитель -> родитель родителя ... -> body Нисходящие: событие распространяется вниз по дереву DOM от родительских узлов к дочерним, пока не достигнет того элемента, на котором это событие и возникло. Для их использования в метод addEventListener() в качестве третьего необязательного параметра передается логическое значение true или false, которое указывает, будет ли событие нисходящим. По умолчанию все события восходящие. body -> родитель элемента -> внутренний элемент (кликнутый)
131
Рекурсия
Recursion Рекурсия - функция, которая вызывает сама себя. Она включает повторяющиеся операции и базовый случай (условие прекращения рекурсии) Как только выполнение доходит до базового случая, оно останавливается `function factorial(n) { // Если мы пытаемся найти факториал 1, возвращаем 1 — это базовый случай. if (n <= 1) { return 1 } // В остальных случаях возвращаем произведение n на факториал предыдущего числа — таким образом мы от n дойдём до 1, перебрав каждое число. return n * factorial(n - 1) } console.log(factorial(5)) // 120` Рекурсивные структуры данных: Дерево — это структура, в которой у каждого узла может быть несколько дочерних подузлов — «детей». Когда использовать рекурсию - Если вы работаете с рекурсивной структурой данных, лучше использовать рекурсию — это будет сильно проще. - Если промежуточный результат выполнения функции можно закэшировать, то стоит подумать об использовании рекурсии. Когда использовать цикл - Если рекурсивную функцию сложно читать или отлаживать, можно превратить её в цикл. Код станет менее лаконичным, но сил на отладку будет уходить меньше. - Если вам жизненно необходимо оптимизировать работу программы, рекурсию можно переписать на цикл. Это почти всегда работает быстрее, хотя код и становится менее читаемым.
132
Сustom sorting for Array
Метод sort() сортирует элементы массива на месте и возвращает ссылку на тот же массив, теперь отсортированный. Порядок сортировки по умолчанию — восходящий, основанный на преобразовании элементов в строки и последующем сравнении их последовательностей значений кодовых единиц UTF-16. `sort((a, b) => { /* … */ } )` `// Compare function sort(compareFn) // Inline compare function sort(function compareFn(a, b) { /* … */ })` compareFn Задает функцию, определяющую порядок сортировки. Если он опущен, элементы массива преобразуются в строки, а затем сортируются в соответствии со значением кодовой точки Unicode для каждого символа. Метод `sort()` возвращает ссылку на исходный массив, поэтому изменение возвращаемого массива также приведет к изменению исходного массива.
133
Самовызывающаяся функция
IIFE - Immediately Invoked Function Expression (немедленно вызываемое функциональное выражение). Этот подход позволяет: - обеспечить коду собственный блок видимости, то-есть контекст выполнения - избежать загрязнения глобальной области видимости глобальными переменными - избежать неумышленного переопределения уже существующих переменных в глобальной области видимости 1. Стандартный способ с использованием обычных скобок: `(function() { // код IIFE })();` 2. С использованием группирующих операторов: `( function() { // код IIFE }());` 3. С использованием оператора вспомогательного знака: `!function() { // код IIFE }();` 4. С использованием оператора минуса: `-var myNamespace = function() { // код IIFE }();` 5. С использованием оператора побитового НЕ: `~function() { // код IIFE }();`
134
Свойство Prototype
В JavaScript объекты имеют специальное скрытое свойство `[[Prototype]]` (так оно названо в спецификации), которое либо равно `null`, либо ссылается на другой объект. Этот объект называется «прототип». Когда мы хотим прочитать свойство из `object`, а оно отсутствует, JavaScript автоматически берёт его из прототипа. В программировании такой механизм называется «прототипным наследованием». Свойство `[[Prototype]]` является внутренним и скрытым, но есть много способов задать его. 1. Через `proto` (плохой вариант): `rabbit.__proto__ = animal;` Есть только два ограничения: - Ссылки не могут идти по кругу. JavaScript выдаст ошибку, если мы попытаемся назначить `proto` по кругу. - Значение `proto` может быть объектом или `null`. Другие типы игнорируются. - Может быть только один `[[Prototype]]`. Объект не может наследоваться от двух других объектов. 2. Современные методы: - `Object.create(proto, [descriptors])` – создаёт пустой объект со свойством `[[Prototype]]`, указанным как `proto`, и необязательными дескрипторами свойств `descriptors`. - `Object.getPrototypeOf(obj)` – возвращает свойство `[[Prototype]]` объекта `obj`. - `Object.setPrototypeOf(obj, proto)` – устанавливает свойство `[[Prototype]]` объекта `obj` как `proto`. let animal = { eats: true }; // создаём новый объект с прототипом animal let rabbit = Object.create(animal); alert(rabbit.eats); // true // получаем прототип объекта rabbit alert(Object.getPrototypeOf(rabbit) === animal); // заменяем прототип объекта rabbit на {} Object.setPrototypeOf(rabbit, {}); В JavaScript все объекты имеют скрытое свойство `[[Prototype]]`, которое является либо другим объектом, либо `null`. Мы можем использовать `obj.__proto__` для доступа к нему (исторически обусловленный геттер/сеттер, есть другие способы, которые скоро будут рассмотрены). Объект, на который ссылается `[[Prototype]]`, называется «прототипом». Если мы хотим прочитать свойство `obj` или вызвать метод, которого не существует у `obj`, тогда JavaScript попытается найти его в прототипе. Операции записи/удаления работают непосредственно с объектом, они не используют прототип (если это обычное свойство, а не сеттер). Если мы вызываем `obj.method()`, а метод при этом взят из прототипа, то `this` всё равно ссылается на `obj`. Таким образом, методы всегда работают с текущим объектом, даже если они наследуются. Цикл `for..in` перебирает как свои, так и унаследованные свойства. Остальные методы получения ключей/значений работают только с собственными свойствами объекта. Все встроенные объекты следуют одному шаблону: Методы хранятся в прототипах (`Array.prototype`, `Object.prototype`, `Date.prototype` и т.д.). Сами объекты хранят только данные (элементы массивов, свойства объектов, даты). Примитивы также хранят свои методы в прототипах объектов-обёрток: `Number.prototype`, `String.prototype`, `Boolean.prototype`. Только у значений `undefined` и `null` нет объектов-обёрток. Встроенные прототипы могут быть изменены или дополнены новыми методами. Но не рекомендуется менять их. Единственная допустимая причина – это добавление нового метода из стандарта, который ещё не поддерживается движком JavaScript. Мы можем создавать объекты без прототипов с помощью `Object.create(null)`. Такие объекты можно использовать как "чистые словари", у них нет проблем с использованием строки "__proto__" в качестве ключа. Ещё методы: - `Object.keys(obj) / Object.values(obj) / Object.entries(obj)` – возвращают массив всех перечисляемых собственных строковых ключей/значений/пар ключ-значение. - `Object.getOwnPropertySymbols(obj)` – возвращает массив всех собственных символьных ключей. - `Object.getOwnPropertyNames(obj)` – возвращает массив всех собственных строковых ключей. - `Reflect.ownKeys(obj)` – возвращает массив всех собственных ключей. - `obj.hasOwnProperty(key)` – возвращает `true`, если у `obj` есть собственное (не унаследованное) свойство с именем `key`.
135
Свойство объекта
Object property Свойство объекта - это пара ключ-значение, где ключ - это строка (имя свойства), а значение может быть чем угодно.
136
Событийный цикл
Поток выполнения в браузере, равно как и в Node.js, основан на событийном цикле. Понимание работы событийного цикла важно для оптимизаций, иногда для правильной архитектуры. В этой главе мы сначала разберём теорию, а затем рассмотрим её практическое применение. Идея событийного цикла очень проста. Есть бесконечный цикл, в котором движок JavaScript ожидает задачи, исполняет их и снова ожидает появления новых. Общий алгоритм движка: 1. Пока есть задачи: - Выполнить их, начиная с самой старой 2. Бездействовать до появления новой задачи, а затем перейти к пункту 1 Это формализация того, что мы наблюдаем, просматривая веб-страницу. Движок JavaScript большую часть времени ничего не делает и работает, только если требуется исполнить скрипт/обработчик или обработать событие. Примеры задач: Когда загружается внешний скрипт ` При каждом изменении файла вам нужно будет обновлять параметр, например, изменяя значение `v=12345` на новое уникальное значение, чтобы убедиться, что файл будет загружаться снова. 2. Отключение кэширования в dev-режиме: Некоторые инструменты разработки или фреймворки предоставляют встроенную поддержку для отключения кэширования во время фронтенд-разработки. Например, в Next.js вы можете задать параметр `cache` в значении `false` в файле конфигурации `next.config.js`: `module.exports = { // ...другая конфигурация... cache: false, };` Это заставит браузер игнорировать кэшированные файлы Javascript и CSS при разработке. 3. Использование инкрементальной обновляемой ссылки: Вы можете использовать стратегию инкрементально обновляемой ссылки для обмана браузера и заставления его считать файлы всегда новыми. Например, вы можете добавить случайное число или случайную строку к URL-адресу файла при каждом обновлении страницы: `const randomString = Math.random().toString(36).substr(2, 10); const script = document.createElement('script'); script.src = `script.js?${randomString}`; document.head.appendChild(script);` Это генерирует новый URL-адрес скрипта каждый раз при обновлении страницы, заставляя браузер загружать новый файл. Обратите внимание, что эти методы полезны только во время разработки и не должны использоваться на продакшене. Убедитесь, что вы удалите эти изменения перед развертыванием приложения в живую среду.
233
Что такое requestAnimationFrame?
requestAnimationFrame - это метод JavaScript, который позволяет выполнять анимацию и другие действия с высокой производительностью, синхронизированно с обновлением экрана браузера. Он позволяет браузеру самостоятельно выбирать оптимальное время для выполнения анимации, чтобы достичь плавного и эффективного воспроизведения. Синтаксис метода requestAnimationFrame выглядит следующим образом: `requestAnimationFrame(callback);` Он принимает один обязательный параметр - функцию обратного вызова (callback), которая будет выполняться перед следующим обновлением кадра экрана. Функция обратного вызова принимает время прошедшее с момента запуска страницы (DOMHighResTimeStamp) в качестве параметра. Преимущества использования requestAnimationFrame включают: - Автоматическая оптимизация производительности, так как браузер самостоятельно выбирает оптимальное время для выполнения анимации. - Снижение нагрузки на процессор и увеличение срока службы батареи устройств. - Предотвращение синхронизационных проблем, например, "дрожание" элементов, вызванных несовместимостью с обновлениями экрана. - Автоматическое приостановление анимации, когда пользователь не находится на активной вкладке или когда браузер находится в фоновом режиме. Использование requestAnimationFrame стало предпочтительным способом выполнения анимации вместо использования setInterval или setTimeout, так как этот метод предоставляет лучшую производительность и более точную синхронизацию с обновлениями экрана.
234
Что такое Shadow DOM?
Shadow DOM (Document Object Model) - это функциональность, предоставляемая браузерами, которая позволяет создавать компоненты с ограниченной областью видимости для стилей и функциональности. Когда вы создаете и используете Shadow DOM, вы создаете отдельное дерево DOM со своими собственными узлами и стилями, которые изолируются от внешнего дерева DOM страницы. Это позволяет создавать компоненты, которые не могут быть стилизованы или влиять на компоненты вне Shadow DOM. Основные преимущества использования Shadow DOM: 1. Изоляция стилей: Стили, определенные внутри Shadow DOM, применяются только к элементам внутри этого дерева DOM. Это предотвращает конфликты стилей между компонентами и внешней страницей. 2. Изоляция функциональности: JavaScript-код, выполняемый внутри Shadow DOM, имеет доступ только к элементам внутри этого дерева DOM. Это помогает избежать конфликтов и несанкционированного доступа к DOM-элементам извне компонента. 3. Переиспользование компонентов: Shadow DOM позволяет создавать компоненты с собственными стилями, разметкой и функциональностью, которые могут быть повторно использованы в разных частях веб-приложения или в разных проектах. 4. Устойчивость к внешним стилям: Стили внутри Shadow DOM не подвержены изменениям внешних стилей, поэтому компоненты остаются неизменными, независимо от того, какие стили применяются к внешней странице. Shadow DOM поддерживается веб-браузерами с помощью JavaScript API, такого как Element.attachShadow и ShadowRoot. Он находит применение в различных технологиях веб-разработки, таких как веб-компоненты, пользовательские элементы и некоторые фреймворки, которые стремятся создать изолированные компоненты.
235
Сравните nextElementSibling и nextSibling.
nextElementSibling и nextSibling - это свойства DOM-узлов, которые предоставляют доступ к следующему соседнему элементу, расположенному на одном уровне иерархии DOM. Однако, есть некоторые различия между ними: 1. nextSibling: Свойство nextSibling возвращает следующий соседний узел, независимо от того, является ли он элементом или текстовым узлом. Это означает, что nextSibling может возвращать текстовые узлы, комментарии или другие типы узлов, расположенные на том же уровне иерархии. 2. nextElementSibling: Свойство nextElementSibling возвращает следующий соседний элемент-узел на том же уровне иерархии DOM. Оно игнорирует текстовые узлы, комментарии и другие типы узлов, возвращает только элементы. Если следующий соседний узел не является элементом, то возвращается значение null. Пример использования: `const element = document.getElementById('myElement'); console.log(element.nextElementSibling); // Возвращает следующий элемент-узел console.log(element.nextSibling); // Возвращает следующий узел (может быть не элементом)`
236
Расскажи о GraphQL.
GraphQL - это язык запросов для API и среда выполнения запросов с открытым исходным кодом, разработанная и открытая для публичного использования командой Facebook. Он предлагает более эффективный и гибкий подход к коммуникации между клиентскими приложениями и серверами, позволяя клиентам запрашивать только требуемые данные и форматировать результаты в соответствии с их потребностями. Вот несколько ключевых принципов и функций GraphQL: 1. Определение схемы: GraphQL использует схему для описания типов данных, доступных в API. Вы определяете типы объектов и их отношения, определяете точку входа запроса (корневые поля) и указываете, какие запросы и мутации могут быть выполнены. Схема GraphQL обычно задается с использованием языка схемы GraphQL (GraphQL Schema Language) или с помощью программатического API. 2. Гибкие запросы и типизация: Вместо множества фиксированных конечных точек, как в REST, клиенты могут отправлять запросы GraphQL, содержащие только те данные, которые им нужны. Запросы GraphQL явно определяют требуемую структуру ответа и позволяют указывать вложенность и отношения между объектами. Также GraphQL предлагает строгую типизацию данных, поэтому клиенты заранее знают, какие данные ожидать от сервера. 3. Единственный точка входа: В GraphQL существует только одна конечная точка, по которой клиенты отправляют все свои запросы. Это упрощает установку и поддержку API сервера. 4. Резольверы и граф запросов: Резолверы работают как функции, которые извлекают запрошенные данные из различных источников или выполнения бизнес-логики, чтобы удовлетворить запросы клиента. Граф запросов GraphQL представляет соединения между резолверами и объектами в схеме для построения запрошенных данных. 5. Поддержка мутаций: GraphQL позволяет клиентам не только запрашивать данные, но и отправлять мутации для изменения данных на сервере. Мутации позволяют выполнять операции создания, обновления и удаления данных. 6. Интуитивный инструментарий разработчика: GraphQL обладает богатой экосистемой инструментов разработчика, включая клиентские библиотеки, серверные библиотеки, инструменты для разработки схем и просмотра схемы. GraphQL выделяется своей эффективностью, гибкостью и возможностью быстрого разработки и поддержки клиент-серверных приложений. Он становится все более популярным в современном веб-разработке и широко применяется многими крупными компаниями и командами разработчиков.
237
Какие знаете метрики веб-сайта?
еб-аналитика предоставляет различные метрики и показатели для измерения эффективности и производительности веб-сайта. Вот некоторые из наиболее распространенных метрик, которые используются для анализа веб-сайтов: 1. Посещения (Visits): Число уникальных пользователей, которые посетили веб-сайт за определенный период времени. 2. Сеансы (Sessions): Количество периодов активности пользователей на веб-сайте. Сессия начинается, когда пользователь заходит на сайт и заканчивается, когда он уходит или остается неактивным в течение определенного времени (обычно 30 минут). 3. Просмотры страниц (Pageviews): Количество просмотров конкретных страниц веб-сайта. Один пользователь может просматривать несколько страниц в рамках одной сессии. 4. Продолжительность сессии (Session Duration): Среднее время, которое пользователь проводит на вашем веб-сайте в рамках одной сессии. 5. Скорость загрузки страницы (Page Load Time): Время, необходимое для полной загрузки страницы. Быстрая скорость загрузки важна для улучшения пользовательского опыта. 6. Возвраты (Bounce Rate): Процент пользователей, которые покинули сайт после просмотра только одной страницы. Высокий показатель отказов может указывать на проблемы с контентом или пользовательским опытом. 7. Конверсии (Conversions): Достижение желаемой цели веб-сайта, такой как заполнение формы, совершение покупки и других действий, которые приводят к конверсии посетителей в клиентов.
238
Что такое git flow
Git Flow - это набор правил и методология работы с Git, разработанная Vincent Driessen, которая помогает разработчикам эффективно управлять разработкой проектов с использованием Git. Вот краткое описание основных идей и принципов Git Flow: 1. Основные ветки: Git Flow определяет две основные ветки в Git: - master: ветка master представляет стабильную версию кода. Код в этой ветке всегда должен быть готовым к развертыванию. - develop: ветка develop является главной веткой разработки. В нее вливаются все новые фичи и другие ветки перед их публикацией. 2. Вспомогательные ветки: Git Flow определяет три вида вспомогательных веток: - feature: ветки feature используются для добавления новой функциональности в проект. Они создаются от develop и вливаются обратно в develop после завершения работы. - release: ветки release используются для подготовки новых релизов проекта. Они создаются от develop и вливаются как в master, так и в develop после завершения тестирования и подготовки к релизу. - hotfix: ветки hotfix используются для быстрого исправления ошибок в производственной версии кода. Они создаются от master и вливаются как в master, так и в develop после завершения исправления. 3. Работа с ветками: Все описанные ветки создаются и сливаются в соответствии с определенными правилами и процессом Git Flow. Например, новые фичи разрабатываются в отдельных ветках feature, затем вливаются в develop. Релизы создаются из develop в ветках release и сливаются обратно как в master, так и в develop. Багфиксы создаются в "горячих" ветках hotfix и сливаются как в master, так и в develop. Git Flow имеет определенные преимущества, такие как четкая организация работы с ветками, возможность параллельной разработки множества фич и эффективное управление релизами. Однако он также может быть сложным для более небольших проектов или команд с небольшим числом разработчиков. Важно отметить, что Git Flow - это методология разработки, и каждая команда или проект может адаптировать ее под свои потребности и процессы.
239
Что означает требование делать squash commits во время rebase?
Требование делать squash commits во время rebase означает объединение нескольких коммитов в один, чтобы облегчить чтение истории коммитов и сделать ее более понятной и логичной. При выполнении операции rebase, где вы переносите коммиты из одной ветки на другую или преобразуете историю коммитов в некоторый другой способ, вы можете столкнуться с запросом на объединение (squash) одного или нескольких коммитов. В результате объединения коммиты будут объединены в один коммит с комментариями объединенных коммитов. Сквош коммиты могут использоваться для создания более логичной и чистой истории коммитов, чтобы облегчитьам поиск изменений, отслеживания прогресса и упрощения слияний. Это может быть особенно полезно, когда вы работаете с веткой функции, где вы стремитесь объединить все связанные коммиты в один перед выполнением слияния с основной веткой. Важно заметить, что squash commits может быть выполнен только перед публикацией кода в общедоступную ветку, так как это изменяет историю коммитов и может повлечь за собой изменение идентификационных хэш-сумм коммитов. Поэтому требуется осторожность при использовании squash commits и коммуникация с другими разработчиками, особенно если они работают с вашим репозиторием.
240
Какие конвенции знаете и используете для git?
Существует множество конвенций и рекомендаций для использования Git, которые помогают организовать и стандартизировать работу с системой контроля версий. Вот некоторые из них: 1. Организация репозитория: - Используйте дескриптивные и понятные имена для репозиториев. - Разделяйте проекты на отдельные репозитории согласно их функциональности. - Сохраняйте репозиторий чистым и избегайте добавления ненужных файлов или файлов, не относящихся к коду. 2. Наименование веток: - Дайте веткам понятные и описательные имена. - Используйте осмысленные префиксы (например, `feature/`, `bugfix/`) для указания типа задачи. 3. Коммиты: - Используйте осмысленные сообщения коммитов, описывающие выполняемые изменения. - Соблюдайте единообразие в форматировании сообщений коммитов (например, "[тип задачи] Описание коммита"). 4. Слияния (мерджи): - Избегайте сознательных изменений в слияниях, если только это не является частью задания слияния или исправления конфликтов. 5. Игнорирование файлов: - Используйте файл `.gitignore`, чтобы указать, какие файлы или директории должны быть игнорированы Git.
241
Для чего нужен package-lock.json?
Файл `package-lock.json` является частью экосистемы Node.js и используется для сохранения точной исходной составляющей зависимостей проекта. Вот несколько причин, почему `package-lock.json` важен: 1. Фиксация версий зависимостей: `package-lock.json` содержит полный список всех зависимостей проекта, включая их точные версии. Это позволяет обеспечить консистентность версий зависимостей между разными средами разработки и развёртывания, а также между разными разработчиками. Это особенно полезно при коллективной работе над проектом. 2. Более надёжная установка пакетов: `package-lock.json` содержит информацию о дереве зависимостей, включая вложенные зависимости и дистрибутивные файлы для каждого пакета. Это гарантирует, что при установке зависимостей будут загружены и установлены конкретные версии пакетов из `package-lock.json`, а не самые последние версии из веб-репозитория. Таким образом, это обеспечивает более надежный процесс установки и предотвращает неожиданные изменения в зависимостях. 3. Ускорение процесса установки: `package-lock.json` содержит кэшированные и предкомпилированные файлы для каждого пакета. Это позволяет ускорить процесс установки зависимостей, т.к. не требуется повторная загрузка исходных файлов пакетов для каждой установки.
242
В чем разница между npm install и npm ci?
`npm install` и `npm ci` - это две различные команды в NPM (Node Package Manager) для установки зависимостей проекта, но у них есть несколько ключевых различий: 1. Целевая аудитория: - `npm install` рекомендуется для ежедневной разработки и позволяет выполнять различные операции, такие как установка, обновление и удаление пакетов, а также управление зависимостями проекта. - `npm ci` предназначен для использования в среде непрерывной интеграции и непрерывной доставки (CI/CD) и предоставляет строгую и непрерывную установку зависимостей в соответствии с `package-lock.json` или `npm-shrinkwrap.json`. 2. Файлы блокировки зависимостей: - `npm install` устанавливает зависимости, учитывая `package.json` и `package-lock.json` (или `npm-shrinkwrap.json`) для обеспечения консистентности версий и фиксирования зависимостей проекта. - `npm ci` пропускает `package.json` и полностью доверяет `package-lock.json` (или `npm-shrinkwrap.json`), чтобы установить зависимости без проверки и обновления файлов блокировки зависимостей. 3. Быстродействие: - `npm install` может быть медленной в процессе установки, особенно при наличии большого числа зависимостей, поскольку он также выполняет проверку изменений в блокировочных файлах, обновление пакетов и другие операции. - `npm ci` оптимизирована для быстрого времени установки и не выполняет дополнительные проверки или обновления, обеспечивая более быструю и потенциально предсказуемую установку зависимостей.
243
Для чего нужны бандлеры?
Бандлеры - это инструменты, которые объединяют и упаковывают различные модули, файлы и зависимости вашего проекта в единый файл (бандл), который может быть выполнен браузером. Они играют важную роль в разработке веб-приложений и предоставляют несколько преимуществ: 1. Управление зависимостями: Бандлеры позволяют импортировать, управлять и использовать зависимости из разных модулей и файлов в вашем проекте. Они обеспечивают разрешение конфликтов зависимостей и автоматически импортируют необходимые ресурсы, что упрощает и улучшает организацию кода. 2. Модульность: Бандлеры поддерживают модульную структуру кода, что позволяет разделить код на независимые модули с четкими интерфейсами и зависимостями. Это улучшает читаемость и поддерживаемость кода, а также позволяет повторно использовать модули в других проектах. 3. Обработка различных типов файлов: Бандлеры предоставляют возможность работать с различными типами файлов, такими как JavaScript, CSS, HTML, изображения и другие ресурсы. Они могут выполнять обработку исходного кода, включая транспиляцию, компиляцию, минификацию, оптимизацию и другие преобразования, чтобы оптимизировать работу и размер итогового бандла. 4. Ленивая загрузка: Бандлеры поддерживают загрузку только тех модулей и ресурсов, которые необходимы для текущей страницы или функциональности. Это позволяет ускорить время загрузки и уменьшить начальные нагрузки, особенно для крупных проектов с множеством кода и ресурсов. Некоторые популярные бандлеры веб-приложений включают Webpack, Parcel и Rollup. Они предоставляют мощные возможности для создания эффективных, модульных и оптимизированных бандлов для вашего веб-приложения.
244
Чем различаются git merge и git rebase?
1. `git merge`: Команда `git merge` объединяет изменения из одной ветки в текущую ветку. При выполнении слияния Git создает новый коммит с объединенными изменениями, сохраняя исходную историю коммитов обеих веток. Это создает линейную историю слияний и позволяет в будущем отслеживать исходную историю каждой ветки. `git merge` является более простым и предпочтительным способом объединения изменений, особенно в случае, когда история коммитов не критически важна. 2. `git rebase`: Команда `git rebase` переносит изменения из одной ветки на другую, перебазируя историю коммитов на новую базовую ветку. Вместо создания нового коммита с объединенными изменениями, Git переписывает историю коммитов путем применения каждого коммита из исходной ветки к конечной ветке. Это позволяет создать более линейную историю коммитов без дополнительных комитов слияния. Однако, переписывание истории коммитов может создавать проблемы, если изменения уже были опубликованы или используются другими разработчиками.
245
Что такое staging area в git?
Staging area, также известная как индекс, это промежуточная область в Git, где находятся изменения, которые вы хотите включить в следующий коммит. Это пространство, где вы подготавливаете изменения, выбирая файлы для включения в следующую версию сохранения, или коммита. Они фактически являются предварительным коммитом. Когда вы работаете с Git, ваш рабочий каталог содержит набор файлов и каталогов, а также индекс, или staging area. Индекс отслеживает изменения в ваших файлах и подготавливает их для коммита. Вы можете добавлять, удалать или изменять изменения в staging area перед фиксацией их в коммите. Затем, когда вы выполняете команду `git commit`, Git записывает перечисленные изменения в новый коммит. Использование staging area позволяет гибко работать с коммитами в Git, так как вы можете подготовить определенные файлы или части файлов для фиксации и оставить другие изменения для следующего коммита. Это также позволяет вам проверить свои изменения перед фиксацией и вносить корректировки, если это необходимо, прежде чем закреплять их в рамках коммита.
246
Опишите процесс code review. Назовите основные правила, способы разрешения конфликтов и споров во время его проведения.
Code review (обзор кода) - это процесс проверки кода, который выполняется другим разработчиком или командой разработчиков для оценки качества, безопасности и соответствия лучшим практикам разработки. Он является важной практикой в современной разработке программного обеспечения и может быть полезным для повышения качества кода и улучшения коммуникации в команде. Основные правила code review включают: 1. Установление стандартов: Убедитесь, что команда имеет четкую и согласованную документацию о требованиях и стилях кодирования, чтобы каждый знал, какие правила нужно соблюдать и что ожидается от проверки кода. 2. Регулярность и своевременность: Code review следует проводить регулярно и в назначенные сроки, чтобы предотвратить задержки в процессе разработки и обеспечить непрерывное повышение качества. 3. Культура конструктивной обратной связи: Коммуникация при проведении code review должна быть конструктивной, уважительной и объективной. Используйте ясные и специфичные комментарии, чтобы помочь автору улучшить код. 4. Проверка правильности решения задачи: Убедитесь, что код решает поставленную задачу и соответствует бизнес-требованиям и функциональным спецификациям. Способы разрешения конфликтов и споров во время проведения code review: 1. Диалог и обсуждение: Откройте диалог с автором кода и обсудите возможные проблемы и предложения по улучшению. Дайте возможность автору объяснить свое решение и выслушайте его точку зрения. 2. Постепенные улучшения: Если обнаружены проблемы, предложите постепенные улучшения, чтобы не перегружать автора большим количеством изменений, которые могут быть сложны для восприятия. 3. Постепенное внедрение изменений: Если разработчики не согласны на какую-то часть кода, могут быть предложены компромиссы или постепенное внедрение изменений для устранения споров и поиск консенсуса. 4. Обоснование: Если возникла необходимость отклонить предложение или изменения автора, обоснуйте ваше решение и приведите аргументы, чтобы помочь автору понять причины. Важно помнить, что code review - это коллективная ответственность команды, и все ее члены должны использовать этот процесс для повышения качества кода, обмена опытом и непрерывного самоусовершенствования.
247
Что такое CI/CD
CI/CD (Continuous Integration/Continuous Delivery) - это практика и набор методов и инструментов, обеспечивающих автоматизацию и непрерывность внедрения и доставки приложений. Continuous Integration (непрерывная интеграция) - это процесс объединения кода и его автоматической сборки и проверки на соответствие при каждом изменении. Целью CI является ускорение и облегчение процесса разработки, обеспечение быстрой обратной связи и выявление ошибок на раннем этапе. Continuous Delivery (непрерывная доставка) - это практика автоматической подготовки приложения к выпуску на каждом этапе разработки, начиная от размещения в системе контроля версий до окончательной доставки в производственную среду. Цель CD состоит в минимизации ручной работы, сокращении времени до выпуска и максимизации надежности и гибкости процесса доставки. СI/CD дает возможность быстро и надежно поставлять новый функционал и исправления ошибок пользователю, а также автоматически тестировать, собирать, развертывать и проверять работоспособность приложения. Он способствует улучшению качества, сокращению времени разработки и доставки, а также упрощению процессов и стандартизации в разработке программного обеспечения.
248
Какие знаете коды ответа (состояния) HTTP?
1XX (Информационные): - 100 Continue - 101 Switching Protocols - 102 Processing 2XX (Успешные): - 200 OK - 201 Created - 202 Accepted - 204 No Content - 206 Partial Content 3XX (Перенаправления): - 300 Multiple Choices - 301 Moved Permanently - 302 Found - 304 Not Modified - 307 Temporary Redirect - 308 Permanent Redirect 4XX (Ошибки клиента): - 400 Bad Request - 401 Unauthorized - 403 Forbidden - 404 Not Found - 405 Method Not Allowed - 409 Conflict - 410 Gone - 415 Unsupported Media Type - 418 I'm a teapot 5XX (Ошибки сервера): - 500 Internal Server Error - 501 Not Implemented - 502 Bad Gateway - 503 Service Unavailable - 504 Gateway Timeout - 505 HTTP Version Not Supported
249
Что такое Cross-Origin Resource Sharing? Как устранить проблемы с CORS?
Cross-Origin Resource Sharing (CORS) - это механизм, предоставляемый браузером, который позволяет веб-страницам запрашивать ресурсы с другого домена (origin). Эта политика безопасности браузера предотвращает выполнение запросов на разные домены, если сервер не выдает соответствующие заголовки разрешения. Для устранения проблем с CORS можно применить следующие подходы: 1. Добавление заголовков CORS на сервере: Сервер должен включать заголовки CORS в ответе на запросы с других доменов. Наиболее часто используемый заголовок - "Access-Control-Allow-Origin", который указывает, какие домены могут получать доступ к ресурсам. Если вы хотите разрешить доступ со всех доменов, вы можете указать значение "*", но это может представлять угрозу безопасности и рекомендуется указать только разрешенные домены. 2. Использование прокси-сервера: Если вы не имеете контроля над сервером, с которого запрашиваются ресурсы, вы можете настроить прокси-сервер, который будет получать запрошенные ресурсы с другого домена и передавать их клиенту на вашем домене. Таким образом, запросы выполняются с того же домена, и проблема с CORS не возникает. 3. Использование JSONP (JSON with Padding): JSONP - это способ обойти политику безопасности браузера и получать данные с других доменов. Он использует тег скрипта (script tag), чтобы загрузить скрипт с другого домена, и вызывает функцию обратного вызова, чтобы передать данные на ваш домен. Однако JSONP имеет определенные ограничения и может быть уязвимым для атак. 4. Использование CORS-прокси: Может быть использован CORS-прокси, который подставляется между клиентом и сервером и добавляет необходимые заголовки CORS в ответы, полученные от сервера. Это может быть полезным, когда сервер не поддерживает или не настроен для обработки CORS.
250
Какой максимальный размер cookie?
Максимальный размер cookie может варьироваться в зависимости от браузера и операционной системы. Обычно, максимальный размер cookie составляет около 4 килобайт. Однако, существуют некоторые ограничения: 1. В некоторых браузерах максимальный размер cookie может быть ограничен до 4096 байт. 2. Веб-серверы могут устанавливать дополнительные ограничения на размер cookie. 3. Некоторые операционные системы или браузеры могут иметь ограничение на общее количество cookie для одного домена. Поэтому, рекомендуется следовать стандарту и ограничивать размер cookie до примерно 4 килобайт, чтобы обеспечить совместимость с большинством браузеров и операционных систем.
251
Что такое статическая и динамическая типизации?
Статическая и динамическая типизация - это два подхода к управлению типами данных в программировании. Статическая типизация означает, что типы данных проверяются во время компиляции программы. То есть, переменные должны быть объявлены с определенным типом, и этот тип должен соответствовать ожидаемому типу данных во время компиляции. Например, если вы объявляете переменную как целое число, то она не может содержать строки или булевые значения. Это позволяет выявлять ошибки типов на ранних стадиях разработки и повышает надежность программы. С другой стороны, динамическая типизация позволяет переменным изменять свой тип во время выполнения программы. То есть, переменная может быть использована для хранения любого типа данных. Это означает, что типы данных определяются во время выполнения, а не во время компиляции. Преимущество динамической типизации заключается в гибкости и удобстве использования, так как переменные могут быть переиспользованы для разных типов данных. Однако это также может повлечь за собой ошибки типов, которые могут быть обнаружены только во время выполнения программы. Итак, статическая типизация обеспечивает большую надежность и безопасность на этапе компиляции, в то время как динамическая типизация обеспечивает большую гибкость и удобство использования во время выполнения программы. Оба подхода имеют свои преимущества и недостатки, и выбор между ними зависит от требований конкретного проекта и предпочтений разработчика.
252
Как клиент взаимодействует с сервером?
Взаимодействие клиента и сервера основано на модели клиент-сервер, где клиент и сервер являются двумя отдельными компьютерами или программными компонентами, которые обмениваются данными и выполняют определенные функции в рамках сети. Вот общий процесс взаимодействия клиента и сервера: 1. Клиент отправляет запрос серверу. Запрос включает в себя информацию о том, какие данные или услуги клиент запрашивает, например, URL веб-страницы, метод API или другие параметры. 2. Сервер получает запрос от клиента. Он анализирует запрос, определяет, какие данные или услуги требуются, и подготавливает ответ. 3. Сервер обрабатывает запрос и выполняет соответствующие действия, например, извлекает данные из базы данных или вычисляет результат. 4. Сервер формирует ответ, содержащий запрошенные данные или результат выполнения операции. 5. Сервер отправляет ответ клиенту, используя протокол связи, такой как HTTP или TCP/IP. Ответ может включать данные, ошибки или статус операции. 6. Клиент получает ответ от сервера и обрабатывает его. Он может отображать данные на экране пользователя, использовать их для дальнейших вычислений или выполнить другие операции в зависимости от приложения или услуги, которые он предоставляет клиенту. 7. Взаимодействие между клиентом и сервером может продолжаться путем отправки дополнительных запросов и получения ответов, если это требуется для работы приложения или сервиса. Важно отметить, что этот процесс взаимодействия может быть реализован с использованием различных технологий, протоколов и платформ, в зависимости от контекста и требований приложения или услуги.
253
Как проверить, является ли число конечным в JS?
В JavaScript можно использовать функцию `isFinite()` для проверки, является ли число конечным. Функция `isFinite()` возвращает значение `true`, если число является конечным, и `false`, если число является бесконечным или NaN (Not a Number). Вот пример использования функции `isFinite()`: `let number1 = 10; let number2 = Infinity; let number3 = NaN; console.log(isFinite(number1)); // true console.log(isFinite(number2)); // false console.log(isFinite(number3)); // false` В этом примере `number1` является конечным числом, поэтому функция `isFinite()` возвращает `true`. `number2` является бесконечностью, поэтому функция возвращает `false`. `number3` - это NaN, поэтому функция также возвращает `false`.
254
Чем отличается поведение isNaN() и Number.isNaN()?
Функции `isNaN()` и `Number.isNaN()` предназначены для проверки, является ли значение не числом (NaN - Not-A-Number). Однако, они имеют некоторые отличия в своем поведении: - `isNaN()` является глобальной функцией JavaScript и выполняет преобразование аргумента в число при его необходимости. Если аргумент не может быть преобразован в число или является NaN, то функция возвращает `true`. Однако, `isNaN()` имеет некоторые особенности: она преобразует аргумент в число перед проверкой, что может привести к неожиданным результатам и ложным срабатываниям. Например, `isNaN("123")` будет `false`, поскольку строка "123" может быть преобразована в число. - `Number.isNaN()` является статическим методом объекта Number и не выполняет преобразование типов. Он строго проверяет, является ли аргумент NaN, и возвращает `true` только в случае, если аргумент является NaN. Например, `Number.isNaN("123")` будет `false`, поскольку строка "123" не является NaN. Резюмируя, `isNaN()` преобразует аргумент в число перед проверкой, а `Number.isNaN()` выполняет строгое сравнение с NaN. Если вам требуется точная проверка на NaN, рекомендуется использовать `Number.isNaN()`, чтобы избежать непредвиденного преобразования типов.
255
Сравните подходы работы с асинхронным кодом: сallbacks vs promises vs async / await.
Callback-функции, промисы и ключевые слова async/await - это три различных подхода к работе с асинхронным кодом в JavaScript. Они предлагают разные методы управления асинхронным выполнением и обработкой результатов. Вот краткое сравнение этих подходов: 1. Callback-функции: - Callback-функции - это стандартный метод управления асинхронным кодом в JavaScript. - Функция передается в качестве аргумента в другую функцию и вызывается после завершения асинхронной операции. - Может возникнуть проблема "callback hell" при обработке нескольких асинхронных операций последовательно. - Обработка ошибок может быть сложной и требует каждый раз проверять наличие ошибок. 2. Промисы: - Промисы - это объекты, представляющие результат или отсутствие результата асинхронной операции. - Позволяют обрабатывать успешное выполнение и обработку ошибок с помощью методов `then()` и `catch()`. - Чейнинг методов `then()` позволяет выполнять последовательные операции над результатами нескольких асинхронных вызовов. - Промисы могут быть объединены при помощи метода `Promise.all()` для ожидания выполнения нескольких асинхронных операций. 3. Async/await: - Async/await - это синтаксический сахар над промисами, который позволяет писать асинхронный код в более "синхронном" стиле. - Ключевое слово `async` объявляет функцию, которая выполняется асинхронно. - Ключевое слово `await` может использоваться внутри `async` функции для ожидания завершения промиса. - Ошибки могут быть обработаны с помощью конструкции try/catch вокруг `await` оператора. Каждый из этих подходов имеет свои преимущества и недостатки, и выбор подхода зависит от требований проекта и предпочтений разработчика. Async/await является более новым и предпочтительным подходом к работе с асинхронным кодом, так как предоставляет чистый и понятный синтаксис. Однако, промисы и callback-функции могут быть более удобными в некоторых случаях или существующем коде.
256
Можно ли записывать новые свойства / функции в прототипы стандартных классов (Array, Object и т. д.)? Почему нет? В каких случаях это делать можно? Как обезопасить себя, если нужно расширить прототип?
Да, технически вы можете записывать новые свойства и методы в прототипы стандартных классов, таких как Array, Object и другие. Это может быть полезным, когда вы хотите расширить функциональность этих классов для своих нужд. Однако, есть определенные рекомендации и ограничения, которые следует учитывать: 1. Изменение прототипов стандартных классов может иметь глобальный эффект на все экземпляры этих классов в вашем приложении. Это может привести к потенциальным конфликтам и сложностям в сопровождении кода. 2. Изменение прототипов стандартных классов может противоречить принципам модульности и инкапсуляции, особенно в случае коллаборативной разработки или больших проектов. 3. Модификация прототипов стандартных классов может вызвать несовместимость или проблемы совместимости с будущими версиями языка JavaScript или другими библиотеками. Если вы все же решаете расширить прототип стандартного класса, рекомендуется следовать следующим рекомендациям: 1. Будьте осторожны и избирательны в выборе методов и свойств для добавления в прототипы стандартных классов. Убедитесь, что ваше расширение не конфликтует с уже существующими методами или свойствами. 2. Документируйте изменения, чтобы другие разработчики знали, что прототип был изменен, и понимали, как использовать новые функции или свойства. 3. Рассмотрите использование других паттернов, таких как миксины или создание собственных классов, вместо прямой модификации прототипов стандартных классов. Обезопасить себя от нежелательных эффектов и конфликтов при расширении прототипов можно, следуя лучшим практикам: - Используйте уникальные имена для своих методов и свойств в прототипе, чтобы избежать возможных конфликтов с будущими версиями языка или другими библиотеками. - Перебирайте свойства только тогда, когда они действительно отсутствуют, чтобы не перезаписывать существующие свойства или методы. - При добавлении новых методов или свойств рекомендуется использовать Object.defineProperty(), чтобы явно указать атрибуты свойства, такие как `enumerable`, `writable` и `configurable`, для большей контроля и безопасности. Учитывайте, что расширение прототипов стандартных классов следует осуществлять осторожно и внимательно оценивать потенциальные плюсы и минусы подобного подхода в вашем конкретном случае.
257
Какие методы перебора массива знаете? В чем их отличие?
В JavaScript для перебора элементов массива существует несколько методов, включая `forEach()`, `map()`, `filter()`, `reduce()`, `some()`, `every()` и `find()`. Ниже я приведу краткое описание каждого метода и их отличия: 1. `forEach()`: Метод `forEach()` вызывает переданную функцию обратного вызова для каждого элемента массива. Он не возвращает новый массив и не изменяет исходный. 2. `map()`: Метод `map()` создает новый массив, в котором каждый элемент является результатом вызова переданной функции обратного вызова для каждого элемента исходного массива. 3. `filter()`: Метод `filter()` создает новый массив, содержащий только те элементы исходного массива, для которых функция обратного вызова возвращает `true`. 4. `reduce()`: Метод `reduce()` применяет функцию обратного вызова к аккумулятору и каждому элементу массива (слева направо) с целью свести массив к одному значению. Он возвращает это окончательное значение аккумулятора. 5. `some()`: Метод `some()` проверяет, удовлетворяет ли хотя бы один элемент массива условию, заданному в функции обратного вызова. Возвращает `true`, если хотя бы один элемент удовлетворяет условию, и `false` в противном случае. 6. `every()`: Метод `every()` проверяет, удовлетворяют ли все элементы массива указанному условию в функции обратного вызова. Возвращает `true`, если все элементы удовлетворяют условию, и `false` в противном случае. 7. `find()`: Метод `find()` возвращает первый элемент массива, для которого функция обратного вызова возвращает `true`. Если такой элемент не найден, возвращается `undefined`. Каждый метод имеет свою специфическую функциональность, поэтому выбор соответствующего метода зависит от требований конкретной задачи.
258
Какая разница между декларацией функции (function declaration) и функциональным выражением (function expression) и стрелочной функцией?
1. Декларация функции (Function Declaration): `function add(x, y) { return x + y; }` - Декларация функции создает и инициализирует переменную в текущей области видимости. - Функция может быть вызвана до самого определения, называется "hoisting" (поднятие). - Имя функции является видимым во всей области видимости. - Используется для определения именованных функций. 2. Функциональное выражение (Function Expression): `const add = function(x, y) { return x + y; };` - Функция присваивается переменной и трактуется как выражение. - Выражение функции не поднимается (не может быть вызвано до определения переменной). - Имя функции является видимым только внутри функции. - Переменная может быть использована для передачи функции в качестве аргумента или присвоения новому имени. 3. Стрелочные функции (Arrow Functions): `const add = (x, y) => x + y;` - Стрелочные функции представляют сокращенный синтаксис для определения функций. - Они всегда анонимные и не могут быть назначены переменным с тем же именем. - Не имеют собственного контекста выполнения (this) и наследуют его от окружающего контекста. - Обычно используются для простых функций без необходимости использования ключевого слова `this`. Разница между ними зависит от использования и контекста. Вообще: - Декларации функций и стрелочные функции являются более подходящими для объявления функций, которые могут быть вызваны из любого места в коде. - Функциональные выражения предпочтительны, когда функция используется в качестве значения, передается аргументом или присваивается переменной. - Функция, объявленная как Function Declaration, создается интерпретатором до выполнения кода. Функцию объявленную как Function Expression можно создать и присвоить переменной как обычное значение. Выбор между ними обычно зависит от предпочтений и требований конкретной задачи в вашем коде.
259
Сравните атрибуты подключения скрипта async и defer в HTML-документе.
Атрибуты `async` и `defer` используются для асинхронной загрузки скриптов в HTML-документе, но есть некоторые различия в их поведении: Атрибут `async`: - Скрипт, помеченный атрибутом `async`, загружается параллельно с парсингом HTML-документа. - Когда скрипт загружен, он будет выполнен сразу же, без ожидания завершения парсинга HTML-документа и других скриптов. - Порядок выполнения скриптов с атрибутом `async` не гарантирован. Если важен последовательный порядок выполнения скриптов, то `async` не подходит. - Событие `DOMContentLoaded` может быть запущено до или после того, как асинхронный скрипт выполнится, в зависимости от времени его загрузки и выполнения. Атрибут `defer`: - Скрипт, помеченный атрибутом `defer`, также загружается параллельно с парсингом HTML-документа. - Однако, выполнение отложено до тех пор, пока весь HTML-документ не будет полностью загружен. - Скрипты с атрибутом `defer` будут выполняться в порядке их появления в HTML-документе. - Событие `DOMContentLoaded` будет запущено после загрузки и перед выполнением всех скриптов с атрибутом `defer`. Вывод: - Если скрипты независимы друг от друга и очень важно, чтобы они были выполнены как можно быстрее, используйте `async`. - Если скрипты зависят друг от друга или требуют доступа к DOM-элементам, используйте `defer`, чтобы гарантировать правильный порядок выполнения и доступ к контенту после его загрузки.
260
Вы (20:16:21): Какая разница между свойствами HTML-элементов innerHTML и innerText?
- `innerHTML`: Свойство `innerHTML` предоставляет доступ к HTML-содержимому элемента в виде строки. Оно позволяет как получать содержимое элемента в виде HTML, так и изменять его. Это означает, что вы можете внедрять HTML-код в элемент с помощью `innerHTML`, включая HTML-теги, атрибуты, стили и другие элементы. `const element = document.getElementById("myElement"); console.log(element.innerHTML); // получаем HTML-содержимое элемента element.innerHTML = " Hello "; // изменяем HTML-содержимое элемента` - `innerText`: Свойство `innerText` предоставляет доступ к текстовому содержимому элемента в виде строки. Оно возвращает только видимый текст, игнорируя все HTML-теги или элементы. При изменении содержимого с использованием `innerText` все HTML-теги будут интерпретироваться как обычный текст. `const element = document.getElementById("myElement"); console.log(element.innerText); // получаем текстовое содержимое элемента element.innerText = "Hello"; // изменяем текстовое содержимое элемента` Таким образом, `innerHTML` позволяет работать с HTML-кодом элемента, включая его структуру и стили, в то время как `innerText` работает только с текстовым содержимым, игнорируя HTML-структуру. Выбор между ними зависит от конкретной задачи и требований.
261
Опишите процесс всплытия (bubbling) событий в DOM.
Процесс всплытия событий в DOM означает, что когда событие происходит на элементе вложенном в другие элементы, оно сначала обрабатывается на вложенном элементе, а затем восходящим образом передается и обрабатывается на его родительских элементах. Такое поведение называется всплытием событий. Во время всплытия, каждый родительский элемент обрабатывает событие, если у него есть соответствующий обработчик событий для него. Процесс всплытия продолжается до самого верхнего родительского элемента (до объекта window), если на пути события не будет остановлено с помощью метода `event.stopPropagation()`. Всплывшие события позволяют делегировать обработку событий на родительских элементах, что облегчает и упрощает работу с динамически создаваемыми или изменяемыми элементами в DOM.
262
Как остановить дефолтную обработку события?
Для остановки дефолтной обработки события в JavaScript можно использовать методы `preventDefault()`. Этот метод вызывается на объекте события и предотвращает выполнение стандартного действия, связанного с событием. Для примера, предположим, у вас есть ссылка и вы хотите предотвратить переход по этой ссылке при клике на нее. Вы можете использовать `preventDefault()` в обработчике события клика для отмены дефолтного поведения: `const link = document.getElementById('myLink'); link.addEventListener('click', function(event) { event.preventDefault(); // предотвращаем переход по ссылке });` Таким образом, при клике на ссылку будет вызван обработчик события, и `preventDefault()` остановит выполнение стандартного действия - перехода по ссылке. Также стоит отметить, что некоторые события могут иметь разные дефолтные действия, и в зависимости от контекста может потребовать дополнительных действий для полной остановки дефолтной обработки. Например, для полной остановки отправки формы при отправке данных через кнопку "submit" может потребоваться использование дополнительных методов, таких как `stopPropagation()`, чтобы предотвратить и другие действия связанные с обработкой события.
263
Как остановить всплытие (bubbling) события?
Чтобы остановить всплытие (bubbling) события в JavaScript, вы можете использовать метод `event.stopPropagation()`. Этот метод вызывается в обработчике событий и предотвращает дальнейшее всплытие события на родительские элементы. Пример использования метода `stopPropagation()`: `element.addEventListener('click', function(event) { event.stopPropagation(); // остальной код обработки события });` В этом примере метод `stopPropagation()` вызывается в обработчике события `click` элемента, чтобы предотвратить всплытие события на родительские элементы. Важно отметить, что метод `stopPropagation()` должен вызываться только в тех случаях, когда действительно требуется полностью остановить всплытие события. В противном случае, это может привести к проблемам с обработкой других событий на родительских элементах.
264
Как получить высоту блока? Его положение относительно границ документа?
В JavaScript для получения высоты блока и его положения относительно границ документа вы можете использовать свойства `offsetHeight` и `offsetTop` элемента. Вот как это сделать: 1. Получение высоты блока: `let block = document.getElementById("blockId"); // Замените "blockId" на идентификатор вашего блока let blockHeight = block.offsetHeight; console.log(blockHeight);` В этом примере мы используем `offsetHeight`, чтобы получить высоту блока. 2. Получение положения блока относительно границ документа: `let blockTop = block.offsetTop; console.log(blockTop);` `offsetTop` вернет значение положения блока по вертикали от верхней границы документа.
265
Чему равен this в обработчике событий (event handler)?
Значение `this` в обработчике событий (event handler) в JavaScript зависит от того, как обработчик событий был задан и вызван. В целом, значение `this` в обработчике событий указывает на элемент, к которому привязан обработчик. Если обработчик событий был добавлен с помощью метода `addEventListener()`, то значение `this` в обработчике будет равно элементу, к которому был привязан обработчик: Однако, если обработчик событий задан как атрибут HTML-элемента (например, `onclick="..."`), то значение `this` будет равно элементу, на котором произошло событие
266
Чем отличается dev-сборка от prod?
Dev-сборка (разработческая сборка) и Prod-сборка (продуктовая/продакшн сборка) - это две различные конфигурации сборки проекта, которые используются в процессе разработки и в боевой среде соответственно. Вот основные отличия между ними: 1. Режим отладки и оптимизация: - Dev-сборка предназначена для удобства разработчика. Она обычно включает режим отладки, расширенную информацию об ошибках и дополнительные инструменты разработчика. Производительность может быть ниже, так как приоритет удобства и быстрой обратной связи разработчика. - Prod-сборка оптимизирована для достижения максимальной производительности, надежности и минимизации размера проекта. Она может включать оптимизацию кода, минификацию и сжатие файлов, удаление неиспользуемого кода и другие оптимизации для повышения скорости и эффективности проекта. 2. Расширение и просмотр ошибок: - Dev-сборка часто предоставляет более подробные сообщения об ошибках, расширенные трассировки стека и другие инструменты для обнаружения и отладки проблем во время разработки. Это помогает разработчикам быстро находить и исправлять проблемы. - Prod-сборка может скрывать некоторую информацию об ошибках и предоставлять более общие сообщения об ошибках, чтобы не раскрывать конфиденциальную информацию или детали внутренних механизмов проекта. 3. Подключение сторонних пакетов и зависимостей: - Dev-сборка может включать отладочные или неоптимизированные версии сторонних пакетов, чтобы упростить разработку и облегчить отладку проблем, связанных с зависимостями. - Prod-сборка обычно использует оптимизированные и минифицированные версии сторонних пакетов и зависимостей, чтобы уменьшить размер финального бандла и улучшить производительность проекта. В целом, dev-сборка и prod-сборка служат различным целям: первая - для разработки и отладки, а вторая - для оптимизации и предоставления рабочей версии проекта. Выбор между ними зависит от конкретных требований и условий проекта.
267
Стайлгайд работы с гит
1. Создание веток: Рекомендуется использовать отдельные ветки для разных функциональных изменений или исправлений ошибок. Это помогает изолировать исходные изменения и обеспечивает возможность легкого переключения между различными задачами. 2. Частые коммиты: Различные внутренние изменения в Git лучше делать в виде небольших, смысловых коммитов. Это помогает в понимании последующих изменений и обеспечивает прозрачность истории изменений. 3. Использование комментариев коммитов: Рекомендуется добавлять информативные комментарии к коммитам, чтобы получить понимание внесенных изменений без необходимости анализировать код. 4. Удаление неиспользуемых веток: Необходимо периодически удалять ветки, которые больше не используются, чтобы уменьшить путаницу и ненужное загромождение репозитория Git. 5. Использование .gitignore: Создание и обновление файла .gitignore, чтобы исключить ненужные файлы и папки из контроля версий. 6. Разбиение больших изменений на мелкие: Большие изменения лучше разбивать на более мелкие и легче управляемые коммиты.
268
Что делает команда git fetch?
Команда `git fetch` используется для получения всех новых изменений из удаленного репозитория без автоматического слияния или модификации вашей рабочей директории или текущей ветки. Она обновляет информацию о ветках и коммитах в вашем локальном репозитории, синхронизируя их с удаленным репозиторием. Вот что делает команда `git fetch`: 1. Получает информацию о доступных ветках и коммитах из удаленного репозитория. 2. Обновляет локальные указатели на ветки (создает или обновляет ссылки на ветки) на основе удаленного репозитория. 3. Загружает все новые коммиты, которых у вас еще нет в локальной копии репозитория. Однако, важно отметить, что команда `git fetch` не автоматически обновляет ваши локальные ветки или рабочую директорию. Чтобы применить полученные изменения и объединить их с вашим текущим состоянием, вам нужно выполнить команду `git merge` или `git rebase` после `git fetch`. Команда `git fetch` полезна, когда вы хотите получить обновления из удаленного репозитория, но хотите вручную решить, какие из этих обновлений применить в вашем локальном репозитории.
269
Как можно получить символ строки по указанному индексу.
1. `str[0]`: Вы можете использовать квадратные скобки и индекс для получения символа по определенному индексу в строке. Например, `str[0]` вернет символ в строке `str` по индексу 0. Этот способ доступа похож на доступ к элементам в массиве. 2. `str.charAt(0)`: Метод `charAt()` позволяет получить символ в строке по указанному индексу. Вы передаете индекс в качестве аргумента метода. Например, `str.charAt(0)` вернет символ в строке `str` по индексу 0. 3. `str.at(0)`: Метод `at()` был предложен, но на самом деле не существует в стандарте JavaScript для работы со строками. Метод `at()` не является встроенным методом строки. Вместо этого, используйте `charAt()` для получения символа по определенному индексу в строке.
270
Как объединить строки?
Через конкатенацию, бэктиками или через `concat(str1, str2, ...)` - объединяет строки и возвращает новую строку.
271
Как получить индекс первого вхождения подстроки в строке и последнего вхождения (с конца строки)?
`indexOf(substring, startIndex)` - возвращает индекс первого вхождения подстроки, начиная с указанного индекса. `const str = 'Hello World'; console.log(str.indexOf('o')); // 4 console.log(str.indexOf('o', 5)); // 7` String.prototype.lastIndexOf(): Метод lastIndexOf() возвращает индекс последнего вхождения указанного значения в строку. Если значение не найдено, метод вернет -1.
272
Как получить часть строки, зная индекс?
`substring(startIndex, endIndex)` - возвращает часть строки от начального индекса до конечного индекса (не включая). `const str = 'Hello World'; console.log(str.substring(6, 11)); // 'World'` `slice(startIndex, endIndex)` - возвращает часть строки от начального индекса до конечного индекса (не включая). Работает аналогично методу `substring()`. `const str = 'Hello World'; console.log(str.slice(6, 11)); // 'World'`
273
Как получить числовое значение Юникода для символа в строке по указанному индексу?
Метод charCodeAt() возвращает числовое значение Юникода для символа в строке по указанному индексу. Индексация начинается с 0. Если индекс находится за пределами длины строки, метод вернет NaN. `const str = "Hello World"; console.log(str.charCodeAt(0)); // 72 console.log(str.charCodeAt(6)); // 87`
274
Как проверить заканчивается ли строка указанным символом или начинается указанным символом?
String.prototype.endsWith(): Метод endsWith() проверяет, заканчивается ли строка указанным значением. Возвращает true, если строка заканчивается указанным значением, иначе возвращает false. `const str = "Hello World"; console.log(str.endsWith("World")); // true console.log(str.endsWith("Hello")); // false` String.prototype.startsWith(): Метод startsWith() проверяет, начинается ли строка с указанного значения. Возвращает true, если строка начинается с указанного значения, иначе возвращает false. `const str = "Hello World"; console.log(str.startsWith("Hello")); // true console.log(str.startsWith("Foo")); // false`
275
Проверить, что строка включает подстроку? А как проверить, сопоставление строки с регулярным выражением?
String.prototype.includes(): Метод includes() проверяет, содержит ли строка указанное значение. Возвращает true, если строка содержит указанное значение, иначе возвращает false. `const str = "Hello World"; console.log(str.includes("Hello")); // true console.log(str.includes("Foo")); // false` String.prototype.match(): Метод match() ищет сопоставление строки с регулярным выражением. Возвращает массив с найденными совпадениями или null. `const str = "Hello World"; console.log(str.match(/Hello/)); // ["Hello"] console.log(str.match(/Foo/)); // null` String.prototype.matchAll(): Метод matchAll() возвращает итератор, который содержит все совпадения строки с регулярным выражением. `const str = "Hello World"; const matches = str.matchAll(/o/g); for (const match of matches) { console.log(match[0]); // "o", "o" }` String.prototype.search(): Метод search() выполняет поиск указанного значения в строке с использованием регулярного выражения. Возвращает индекс первого совпадения или -1, если совпадение не найдено. `const str = "Hello World"; console.log(str.search(/World/)); // 6 console.log(str.search(/Foo/)); // -1`
276
Как сравнить две строки лексикографически?
String.prototype.localeCompare(): Метод localeCompare() сравнивает две строки лексикографически с учетом языковых настроек текущей локали. Возвращает отрицательное число, если первая строка меньше, положительное число, если первая строка больше, и 0, если строки равны. `const str1 = "Hello"; const str2 = "World"; console.log(str1.localeCompare(str2)); // -1 console.log(str2.localeCompare(str1)); // 1 console.log(str1.localeCompare(str1)); // 0`
277
Как удалить пробелы в начале и конце строки?
String.prototype.trim(): Метод trim() удаляет пробельные символы с начала и конца строки и возвращает новую строку. `const str = " Hello World "; console.log(str.trim()); // "Hello World"` String.prototype.trimEnd(): Метод trimEnd() удаляет пробельные символы с конца строки и возвращает новую строку. String.prototype.trimStart(): Метод trimStart() удаляет пробельные символы с начала строки и возвращает новую строку.
278
Как изменить сделать повтор строки, дополнить строку знаками до определенной длины?
String.prototype.repeat(): Метод repeat() создает и возвращает новую строку, состоящую из указанного числа копий исходной строки. `const str = "abc"; console.log(str.repeat(3)); // "abcabcabc" console.log(str.repeat(0)); // "" console.log(str.repeat(1)); // "abc"` String.prototype.padEnd(): Метод padEnd() добавляет указанный символ в конец строки так, чтобы строка имела заданную длину. Если длина строки уже достигнута или превышена, метод не изменяет строку. `const str = "Hello"; console.log(str.padEnd(10, "!")); // "Hello!!!!" console.log(str.padEnd(5)); // "Hello"` String.prototype.padStart(): Метод padStart() добавляет указанный символ в начало строки так, чтобы строка имела заданную длину. Если длина строки уже достигнута или превышена, метод не изменяет строку. `const str = "Hello"; console.log(str.padStart(10, "!")); // "!!!!Hello" console.log(str.padStart(5)); // "Hello"`
279
Как заменить часть строки другой подстрокой?
String.prototype.replace(): Метод replace() заменяет часть строки на другую строку или регулярное выражение, и возвращает новую строку. String.prototype.replaceAll(): Метод replaceAll() заменяет все вхождения подстроки или регулярного выражения на заданную строку, и возвращает новую строку.
280
GraphQL и REST API какая разница? Когда что использовать?
REST — самоописательный архитектурный стиль API, определяемый набором архитектурных ограничений и предназначенный для широкого внедрения многими потребителями API. Наиболее распространенный сегодня стиль API был впервые описан в 2000 году Роем Филдингом в его докторской диссертации. REST делает доступными данные на стороне сервера, представляя их в простых форматах — чаще всего это JSON и XML. Как работает REST REST определен не так строго, как SOAP. RESTful-архитектура должна соответствовать шести архитектурным ограничениям: - единый интерфейс, позволяющий единообразное взаимодействие с сервером вне зависимости от типа устройства или приложения; - отсутствие состояния: необходимое состояние обработки запроса содержится в самом запросе без того, чтобы сервер хранил какие-либо относящиеся к сессии данные; кэширование; - клиент-серверная архитектура: позволяет независимое развитие двух сторон; - многоуровневая система приложения; - возможность для сервера обеспечивать исполняемый код на клиенте. Плюсы REST Разделение клиента и сервера. По возможности разделяя клиент и сервер, REST обеспечивает лучшую абстракцию по сравнению с RPC. Система с уровнями абстракции способна инкапсулировать свои детали, чтобы лучше идентифицировать и поддерживать свои свойства. Поэтому REST API достаточно гибок, чтобы развиваться с течением времени и при этом оставаться стабильной системой. Открытость. Все описывается связью между клиентом и сервером, так что для понимания, как взаимодействовать с REST API, не требуется никакая внешняя документация. Дружественность к кэшу. REST — единственный стиль, который позволяет кэшировать данные на уровне HTTP благодаря повторному использованию множества HTTP-инструментов. В то же время, реализация кэширования в любом другом API потребует настройки дополнительного модуля кэширования. Поддержка нескольких форматов. Возможность поддержки нескольких форматов хранения и обмена данными — одна из причин, по которым REST в настоящее время преобладает в сфере создания общедоступных API. Минусы REST Нет единой структуры REST. Нет точного правильного способа создания REST API. Как моделировать ресурсы и какие ресурсы моделировать, будет зависеть от каждого сценария. Это делает REST простым в теории, но трудным на практике. Большие полезные нагрузки. REST возвращает много богатых метаданных, так что клиент может понять все необходимое о состоянии приложения только из его ответов. И эта болтливость не имеет большого значения для большой сетевой трубы с большой пропускной способностью. Но это не всегда так. Это стало ключевым движущим фактором для Facebook, придумавшего описание стиля GraphQL в 2012 году. Проблемы чрезмерных или недостаточных данных. Ответы REST зачастую содержат либо слишком много данных, либо их недостаточное количество, и создают необходимость в дополнительном запросе. Сценарии использования REST API-интерфейсы управления. API, ориентированные на управление объектами в системе и предназначенные для многих потребителей, распространены шире всего. REST помогает таким API, обеспечивая сильную обнаруживаемость и хорошую документацию, и тем самым вписывается в эту объектную модель. Простые приложения, управляемые ресурсами. REST — это валидный подход для подключения приложений, управляемых ресурсами, которые не нуждаются в гибкости запросов. GraphQL: запрос только необходимых данных Для того, чтобы вернуть что-то нужное, требуется многократно вызывать REST API. И вот, чтобы изменить правила игры, был изобретен GraphQL. GraphQL — это синтаксис, который описывает, как сделать точный запрос данных. Реализация GraphQL стоит того, если задействована модель данных приложения с большим количеством сложных сущностей, ссылающихся друг на друга. Как работает GraphQL GraphQL начинается с построения схемы, которая представляет собой описание всех запросов, которые возможно сделать в API GraphQL, и всех типов данных, которые они возвращают. Строить схему сложно, поскольку она требует строгой типизации на языке определения схемы (Schema Definition Language, SDL). Клиент, который располагает схемой перед отправкой запроса, может проверить свой запрос и убедиться, что сервер сможет ответить на него. Добравшись до бэкэнда, операция GraphQL интерпретируется по всей схеме и разрешается с помощью данных для фронтэнда. Отправив один массивный запрос на сервер, API возвращает ответ в формате JSON с точно теми данными, которые мы запросили. В дополнение к CRUD-операциям RESTful GraphQL содержит также подписки, которые позволяют в режиме реального времени получать уведомления от сервера. Плюсы GraphQL Типизированная схема. GraphQL заранее публикует то, что он может осуществить, что улучшает обнаруживаемость. Указывая клиенту на API GraphQL, мы можем узнать, какие запросы доступны. Хорошо подходит для графических данных. Данные, которые находятся в глубоких отношениях связанности, но не годятся для плоских данных. Никаких версий. Лучшая практика управления версиями — это вообще не версионировать API. В то время как REST предлагает несколько версий API, GraphQL использует единую развивающуюся версию, которая обеспечивает непрерывный доступ к новым функциям и способствует более чистому и удобному в обслуживании серверному коду. Подробные сообщения об ошибках. Подобно SOAP, GraphQL предоставляет подробную информацию о возникших ошибках. Сообщение об ошибке включает в себя все резольверы и ссылается на конкретную часть запроса, на которой лежит ответственность. Гибкие разрешения. GraphQL позволяет выборочно раскрывать определенные функции, сохраняя при этом личную информацию. Между тем архитектура REST не раскрывает данные порциями: либо все, либо ничего. Минусы GraphQL Проблемы производительности. GraphQL выторговывает сложность в обмен на действенность. Слишком большое количество вложенных полей в одном запросе может привести к перегрузке системы. Таким образом, для сложных запросов лучшим вариантом остается REST. Сложность кэширования. Поскольку GraphQL не переиспользует семантику кэширования HTTP, кэширование требует приложения дополнительных усилий. Много самообразования перед разработкой. Когда у них недостаточно времени, чтобы разобраться в нишевых операциях GraphQL и SDL, многие проекты решают пойти по известному пути REST. Сценарии использования GraphQL Мобильный API. В этом случае важна производительность сети и оптимизация полезной нагрузки единичного сообщения. И GraphQL предлагает более эффективную загрузку данных для мобильных устройств. Сложные системы и микросервисы. GraphQL способен скрыть сложность нескольких интегрированных систем, лежащих в основе его API. Агрегируя данные из нескольких мест, он объединяет их в одну глобальную схему. Это особенно актуально для устаревших инфраструктур или сторонних API, которые расширились со временем.
281
Модули в JS - что такое CJS?
CJS (CommonJS) — стандартные модули в NodeJS. Вот как это выглядит: //importing const doSomething = require(‘./doSomething.js’); //exporting module.exports = function doSomething(n) {...} CJS синхронен и хорош для серверной части.
282
Модули в JS - что такое AMD?
AMD (Asynchronous module definition) — что дословно переводится как «Асинхронное определение модуля», используется с RequireJS. Вот пример кода: define([‘dep1’, ‘dep2’], function (dep1, dep2) { //Define the module value by returning a value. return function () {}; }); или // “simplified CommonJS wrapping” https://requirejs.org/docs/whyamd.html define(function (require) { var dep1 = require(‘dep1’), dep2 = require(‘dep2’); return function () {}; }); AMD асинхронна и хороша для внешнего интерфейса.
283
Модули в JS - что такое UMD?
UMD (Universal Module Definition) — по сути своей это не модули, это паттерн для использования AMD и CJS в зависимости от энва. Стал популярен с приходом бандлеров, т.к. разные пакеты использовали разные системы модулей, а бандлерам нужно было все это дело как-то упаковывать. Вот как это может выглядеть ( источник ): (function (root, factory) { if (typeof define === “function” && define.amd) { define([“jquery”, “underscore”], factory); } else if (typeof exports === “object”) { module.exports = factory(require(“jquery”), require(“underscore”)); } else { root.Requester = factory(root.$, root._); } }(this, function ($, _) { // this is where I defined my module implementation var Requester = { // … }; return Requester; })); UMD работает везде и обычно используется как запасной вариант на случай, если ESM не работает.
284
Модули в JS - что такое UMD?
ESM (EcmaScript Module) — самый свежий и единственный (CJS — был стандартом в ноде, но не в самом JS) стандарт модулей в JS. Кардинально отличается от CJS тем — что модули статичны. Т.е. зависимости и модули определяются до того как ранается код, поэтому в ESM на уровне синтаксиса нельзя переопределять значение экспорта (я имею ввиду нельзя поменять ссылку на экспорт, но если экспортится объект, его менять конечно же можно), import _, {} from ‘’ и export const | export default могут быть только на верхнем уровне файла . Благодаря статичным модулям, бандлеры могут без проблем делать Tree Shaking(удалять неиспользованные экспорты): import React from ‘react’; import {foo, bar} from ‘./myLib’; export default function() { // your Function }; export const function1() {…}; export const function2() {…}; ESM — лучший формат модуля благодаря простому синтаксису, асинхронному характеру и возможности тряски дерева.
285
Определение переменной (объявление) и инициализация переменной
1. Определение переменной: Определение переменной означает указание ее имени и возможного типа данных, который может быть присвоен этой переменной. В JavaScript переменные определяются с помощью ключевых слов `var`, `let` или `const`. Например: `let city` 2. Инициализация переменной - Initializing variable: Инициализация переменной - это присваивание начального значения переменной при ее создании либо впервые после объявления. Например: `let city = "Lisboa"` 1. let city - объявление, декларирование переменной 2. = "Lisboa" - присвоение значения (литерала)
286
Что называют “объектами обёртками” ?
временные объекты, в которые преобразуются значения примитивного типа
287
Ассоциативность операторов это
свойство, определяющее порядок обработки операторов с одинаковым приоритетом. Например, оператор доступа к свойствам объекта ".", ассоциативен слева направо - Левая ассоциативность означает, что операторы выполняются слева направо. Если есть несколько операторов с одинаковым приоритетом, то первый оператор слева выполнится первым. Пример: `5 - 3 - 1` будет обработано как `(5 - 3) - 1`, поскольку вычитание является левоассоциативным оператором. - Правая ассоциативность означает, что операторы выполняются справа налево. Если есть несколько операторов с одинаковым приоритетом, то первый оператор справа выполнится первым. Пример: `2 ** 3 ** 2` будет обработано как `2 ** (3 ** 2)`, поскольку оператор возведения в степень (`**`) является правоассоциативным оператором.
288
Какой оператор выполняет проверку на принадлежность объекта к указанному классу ?
В JavaScript, оператор `instanceof` используется для проверки принадлежности объекта к определенному классу. Оператор `instanceof` возвращает `true`, если объект является экземпляром указанного класса, и `false` в противном случае. Вот пример использования оператора `instanceof`: class Person { constructor(name) { this.name = name; } } const john = new Person("John"); console.log(john instanceof Person); // true const str = "Hello"; console.log(str instanceof String); // false const num = 42; console.log(num instanceof Number); // false
289
Что такое итерация?
Каждое отдельное исполнение инструкций в теле цикла
290
Что такое Call stack?
список всех активных функций, которые вызывались до текущей точки выполнения кода. Event Loop имеет два основных компонента - Call Stack (стек вызовов) и Task Queue. Когда Call Stack пуст, он берет функцию из Task Queue и помещает ее в Call Stack для выполнения.
291
Что такое Task Queue?
список задач, готовых к выполнению. Task Queue (очередь задач) - это механизм в JavaScript, связанный с событийным циклом (Event Loop), который управляет асинхронными операциями и выполняет коллбэки функций в правильном порядке. Когда в JavaScript выполняется асинхронная операция или запускается таймер, связанный с таймаутом или интервалом, коллбэк функция, связанная с этой операцией, помещается в Task Queue. Task Queue работает на принципе "первым пришел - первым вышел" (FIFO), то есть функции выполняются в том порядке, в котором они были добавлены в очередь. Event Loop имеет два основных компонента - Call Stack (стек вызовов) и Task Queue. Когда Call Stack пуст, он берет функцию из Task Queue и помещает ее в Call Stack для выполнения. Этот механизм позволяет JavaScript поддерживать асинхронные операции и обрабатывать их в определенном порядке, в том числе обеспечивает обновление интерфейса в браузере, чтобы он оставался отзывчивым. Примеры задач, которые могут быть помещены в Task Queue, включают обработку коллбэков асинхронных функций, обработку событий, XMLHttpRequest, таймеры и другие асинхронные операции.
292
Какой оператор мы используем для того, чтобы установить тип переменной ?
оператор typeof var num = 42; console.log(typeof num); // "number" var str = "Hello"; console.log(typeof str); // "string" var bool = true; console.log(typeof bool); // "boolean" var arr = [1, 2, 3]; console.log(typeof arr); // "object" var obj = { name: "John", age: 30 }; console.log(typeof obj); // "object" var func = function() { }; console.log(typeof func); // "function" var undef; console.log(typeof undef); // "undefined" var n = null; console.log(typeof n); // "object"
293
Для чего нужен BOM?
Открывает доступ к окну браузера, предоставляя возможность для манипуляций с ним. BOM (Browser Object Model) — это часть веб-браузера, предоставляющая программный интерфейс для взаимодействия с окном браузера и другими элементами пользовательского интерфейса. BOM предоставляет различные объекты и методы для доступа и управления окном браузера, историей переходов, URL-адресами, cookie, таймерами, управления всплывающими окнами и другими функциями, которые связаны с окном и браузером. Некоторые основные компоненты BOM включают: - Объект window: представляет окно браузера и предоставляет доступ к его свойствам и методам. Например, можно использовать объект window для получения размеров и координат окна, установки таймеров, открытия и закрытия всплывающих окон и т.д. - Объект navigator: предоставляет информацию о браузере, который запустил текущую страницу. Например, объект navigator может быть использован для определения пользовательского агента (User Agent), доступности функций браузера и других характеристик. - Объект location: предоставляет информацию о текущем URL-адресе страницы и позволяет изменять URL-адресы, загружать новые страницы, перенаправлять пользователя и т.д. - Объект history: предоставляет доступ к истории переходов пользователя между страницами, позволяет переходить вперед и назад по истории, управлять записями истории и т.д.
294
Опишите, как работает прототипно-ориентированная модель наследования в JS.
В плане наследования JavaScript работает лишь с одной сущностью: объектами. Каждый объект имеет внутреннюю ссылку на другой объект, называемый его прототипом. У объекта-прототипа также есть свой собственный прототип и так далее до тех пор, пока цепочка не завершится объектом, у которого свойство prototype равно null. По определению, null не имеет прототипа и является завершающим звеном в цепочке прототипов. При попытке получить доступ к какому-либо свойству объекта, свойство вначале ищется в самом объекте, затем в цепочке прототипов. Поиск ведется до тех пор, пока не найдено свойство с совпадающим именем или не достигнут конец цепочки прототипов.
295
В чем заключается различие между host objects и native objects ?
Native objects — объекты определенные спецификацией ECMAScript, например, Object (constructor), Date, Math. Host objects— объекты, чья роль заключается в создании исполнительного окружения для ECMAScript, например, window, document, location, history.
296
За что отвечают и в чем заключается различие между feature detection, feature inference и User Agent String ?
Feature detection, feature inference и User Agent String — это практики определения, существует ли определенная функция веб-технологии в браузере. Feature detection — это способ определить, существует ли функция в определенных браузерах. Feature inference — предположение: если одна функция присутствует (или нет), соответственно другая тоже будет присутствовать (или нет). User Agent String — это текстовая строка, которую отправляет каждый браузер и к которой можно получить доступ через navigator.userAgent. Эта строка содержит в себе информацию о исполнительном окружении.
297
Какое минимальное время можно задать в setTimeout и setInterval?
В каждом браузере есть свой минимум, если вы указываете меньше него, то все равно задержка будет не меньше минимума. Иногда даже и больше указанного времени, так как задача попадает в очередь и время складывается из заданного плюс затраты в на выполнение на задач в очереди перед ней. Однако, в большинстве современных браузеров и сред выполнения JavaScript, минимальное значение для времени составляет около 4 миллисекунд. Это связано с тем, что внутренняя реализация таких методов может иметь свои ограничения и не может обрабатывать задержки меньше этого значения с высокой точностью.
298
for (var i = 0; i < 10; i++) { setTimeout(function () { console.log(i); }, 100); } Возвращает 10 10 10 10 10 10 10 10 10 10 Придумай как можно больше вариантов, чтобы код отработал правильно
1. Анонимная самовызывающаяся функция (IIFE, Immediately Invoked Function Expression): for (var i = 0; i < 10; i++) { (function (i) { setTimeout(function () { console.log(i); }, 100); })(i); } В этом варианте обертка в виде анонимной самовызывающейся функции используется для создания новой области видимости, чтобы сохранить значение переменной `i` для каждой отдельной задержки `setTimeout`. Это позволяет корректно вывести значения `i`. 2. Использование метода `bind()`: for (var i = 0; i < 10; i++) { setTimeout(function (i) { console.log(i); }.bind(this, i), 100); } В этом варианте метод `bind()` используется для создания новой функции, связанной с контекстом и передачей значения `i` как аргумента. Это позволяет сохранить значение `i` для каждой вызываемой функции `setTimeout`. 3. Передача аргумента в `setTimeout`: for (var i = 0; i < 10; i++) { setTimeout(function (i) { console.log(i); }, 100, i); } В этом варианте третьим аргументом `setTimeout` явно передается значение `i`. Это позволяет сохранить значение `i` для каждой задержки `setTimeout`. 4. Использование блока `let`: for (let i = 0; i < 10; i++) { setTimeout(function () { console.log(i); }, 100); } В этом варианте используется блок `let` вместо `var` для объявления переменной `i` внутри цикла. Поскольку блок `let` создает новую переменную `i` для каждой итерации цикла, значение `i` сохраняется корректно для каждой задержки `setTimeout`.
299
Какие преимущества и недостатки в использовании Ajax
Ajax (Asynchronous JavaScript and XML) позволяет веб-страницам обмениваться данными асинхронно с сервером без перезагрузки всей страницы. Это имеет несколько преимуществ и недостатков, которые стоит учитывать: Преимущества использования Ajax: 1. Асинхронность: Одним из ключевых преимуществ Ajax является возможность асинхронной обработки запросов к серверу без блокировки пользовательского интерфейса. Это позволяет создавать более отзывчивые и динамические веб-приложения. 2. Обновление частей страницы: Ajax позволяет обновлять только части веб-страницы без необходимости перезагрузки всей страницы. Это помогает создавать более плавные и быстрые пользовательские интерфейсы. 3. Улучшенная производительность: Использование Ajax позволяет обмениваться данными с сервером в фоновом режиме, что может улучшить производительность и снизить нагрузку на сеть и сервер. 4. Лучший пользовательский опыт: Благодаря возможности обновления частей страницы без перезагрузки, Ajax помогает создавать более плавные и интерактивные пользовательские интерфейсы, улучшая общий опыт пользователя. Недостатки использования Ajax: 1. Сложность: Использование Ajax может быть более сложным, чем простая синхронная передача данных. Это связано с управлением асинхронными запросами, обработкой ошибок и синхронизацией состояний. 2. Загрузка дополнительного кода: Для работы с Ajax необходимо использовать JavaScript, что может привести к дополнительной загрузке кода на стороне клиента. Это может повысить потребление ресурсов и увеличить время загрузки страницы. 3. Проблемы с безопасностью: Использование Ajax может повлечь за собой уязвимости безопасности, такие как атаки типа Cross-Site Scripting (XSS) или Cross-Site Request Forgery (CSRF). Необходимо принять соответствующие меры для защиты от таких угроз.
300
Как можно оптимизировать загрузку внешних ресурсов на странице. Назовите 3 способа уменьшения времени загрузки страницы
Можно использовать кеширование, ленивую загрузку (lazy-loading), поддомены. Если используется HTTP/1.1, для HTTP/2 это неактуально. Можно оптимизировать сборку JS-кода, минифицировать, использовать CDN, gzip-сжатие, css- и svg-спрайты, настроить кеширование. 1. Минимизация количества запросов. 2. Lazy-loading. 3. Минификация CSS и JS.
301
Сколько ресурсов браузер может одновременно загружать с одного домена
Современные браузеры обычно поддерживают шесть одновременных соединений и параллельных загрузок.
302
Что такое REST и RESTful API?
REST (Representational State Transfer) — это архитектурный стиль, используемый при проектировании распределенных систем. Он был описан в диссертации Роя Филдинга в 2000 году и является основой для создания RESTful API. RESTful API — это веб-сервис, который использует протокол HTTP для обмена данными. Он предоставляет возможность получать, создавать, обновлять и удалять данные на удаленном сервере, используя стандартные HTTP-методы (GET, POST, PUT, DELETE и т. д.). RESTful API использует ресурсы (например, товары, пользователи, заказы) и URI (Uniform Resource Identifier) для доступа к этим ресурсам. Клиент отправляет запросы на сервер, указывая URI и метод HTTP, а сервер возвращает ответ, который может содержать данные в различных форматах (например, JSON или XML). Клиент-серверная архитектура: сервер и клиент независимы друг от друга, что позволяет развивать их независимо. Отсутствие состояния (stateless): каждый запрос клиента должен содержать всю необходимую информацию для его обработки, без сохранения состояния на сервере. Кэширование: клиенты могут кэшировать ответы сервера, чтобы уменьшить количество запросов. Единообразие интерфейса: единообразный интерфейс между клиентом и сервером упрощает взаимодействие и увеличивает его надежность. Слои: клиент не должен знать о слоях на сервере, которые обрабатывают запросы. RESTful API является широко используемым в веб-разработке и предоставляет удобный и гибкий способ обмена данными между сервером и клиентом.
303
Что такое «трёхстороннее рукопожатие»?
Трехстороннее рукопожатие (Triple Handshake) — это проблема безопасности в компьютерных сетях, которая возникает при использовании SSL/TLS-соединений. В процессе установки безопасного соединения SSL/TLS между клиентом и сервером происходит обмен сообщениями, который состоит из трех шагов (трехстороннее рукопожатие): Клиент отправляет серверу сообщение SYN с произвольным начальным номером (seq). Сервер отправляет клиенту сообщение SYN-ACK, подтверждающее получение сообщения SYN и содержащее свой собственный произвольный начальный номер (seq) и номер последовательности подтверждения (ack), который равен начальному номеру клиента +1. Клиент отправляет серверу сообщение ACK с номером подтверждения, который равен начальному номеру сервера +1. Проблема Triple Handshake возникает, когда злоумышленник нарушает правильный порядок шагов рукопожатия, вставляя свой сервер между клиентом и настоящим сервером. В этом случае злоумышленник может получить доступ к конфиденциальной информации, передаваемой между клиентом и сервером. Чтобы избежать проблемы Triple Handshake, необходимо использовать проверенные и безопасные протоколы SSL/TLS и устанавливать соединение только с доверенными серверами.
304
Как реализовать отложенную загрузку изображений?
Отложенная загрузка изображений — это способность страницы загружать изображения только тогда, когда они понадобятся пользователю. Это может ускорить время загрузки страницы и уменьшить использование данных. Есть несколько способов реализации отложенной загрузки изображений. Lazy Loading — техника, которая позволяет отложить загрузку изображений, находящихся за пределами видимой области. Для этого можно использовать библиотеки, такие как Lazysizes или Intersection Observer API. Атрибуты data- — Вы можете использовать атрибут data- вместе с атрибутом src, чтобы отложить загрузку изображения 1. Использование атрибута `loading`: - Добавьте атрибут `loading="lazy"` к ``. - Это позволит браузеру отложить загрузку изображения до тех пор, пока оно не будет видимым на экране пользователем при прокрутке страницы. - Например: `Image`. 2. Использование JavaScript: - Замените исходные значения атрибута `src` на `data-src` (например, `data-src="image.jpg"`). - Добавьте обработчик события для прокрутки страницы, который будет загружать изображения по мере их появления в области просмотра. - Пример кода на JavaScript: const lazyImages = Array.from(document.querySelectorAll('img[data-src]')); const lazyLoad = function() { lazyImages.forEach(img => { if (img.getBoundingClientRect().top <= window.innerHeight && img.getBoundingClientRect().bottom >= 0 && getComputedStyle(img).display !== 'none') { img.src = img.dataset.src; img.removeAttribute('data-src'); } }); lazyImages = lazyImages.filter(img => img.hasAttribute('data-src')); if (lazyImages.length === 0) { document.removeEventListener('scroll', lazyLoad); window.removeEventListener('resize', lazyLoad); } }; document.addEventListener('scroll', lazyLoad); window.addEventListener('resize', lazyLoad); window.addEventListener('orientationchange', lazyLoad); - В этом примере событие `scroll`, `resize` и `orientationchange` отслеживается, и когда изображение появляется в видимой области, его `data-src` заменяется на `src`.
305
Что такое XSS (Cross-Site Scripting)?
XSS (Cross-Site Scripting) - это тип атаки на веб-приложение, при которой злоумышленник внедряет вредоносный код (обычно JavaScript) в веб-страницу или приложение, чтобы выполнить его на компьютере пользователя или получить доступ к его персональным данным. Атаки XSS возникают, когда веб-приложение недостаточно санитизирует или неэкранирует введенные пользователем данные, что позволяет злоумышленнику выполнить свой код в браузере пользователя. Есть несколько типов атак XSS: 1. Stored XSS: Вредоносный код сохраняется на сервере и потом выводится на страницу при ее загрузке или при запросах пользователей. 2. Reflected XSS: Вредоносный код внедряется в URL-параметры, которые затем отображаются на странице или передаются пользователю в ответ на запрос. 3. DOM-Based XSS: Вредоносный код изменяет DOM (Document Object Model) страницы непосредственно в браузере пользователя, влияя на различные компоненты и функциональность веб-приложения. Атаки XSS могут приводить к различным проблемам, включая кражу данных пользователя, манипуляцию сессией, перенаправление пользователя на фальшивые сайты и т.д. Для предотвращения атак XSS в веб-приложениях рекомендуется применять следующие меры безопасности: 1. Экранирование вводимых данных: Проверяйте и экранируйте (экранирование HTML-символов) все входные данные от пользователя, перед тем как выводить их на веб-страницу. Это предотвратит выполнение вредоносного кода. 2. Валидация данных: Применяйте строгую валидацию пользовательского ввода на сервере и на клиентской стороне, чтобы проверить, что данные являются допустимыми и соответствуют ожидаемому формату. 3. Ограничение использования HTML и JavaScript: Следует ограничивать возможности пользователей использовать HTML и JavaScript на странице. Например, не разрешайте вводить HTML-теги или скрипты в формы и поля ввода. 4. Установка правильных заголовков Content Security Policy (CSP): CSP позволяет настроить политику безопасности для ограничения и контроля ресурсов, которые может загружать страница. Это может помочь предотвратить выполнение вредоносного кода. 5. Использование HTTPOnly флага для кук: Установка флага HTTPOnly для кук позволяет предотвратить доступ к ним из JavaScript и защищает от кражи сессионных данных.
306
Как подключить js, css? Плюсы, минусы разных способов?
Можно с помощью тегов прямо на странице или