Обучение
September 2, 2022

Курс по JavaScript

Все, что вы тут найдете, это ничто иное как один большой конспект курса по JavaScript от Стащук Богдана.

Когда начинал проходить курс, хотел каждую тему делать отдельной статьей, но потом понял, что это сильно затрудняет навигацию и поиск, поэтому сделал все в одной статье. Букв, конечно, пришлось написать изрядное количество🙄

Возможно кому-то конспект пригодится😊 Если найдете ошибки, а в нем они есть, я уверен, то пишите в комментариях - исправлю.

Курс есть в свободном доступе на ютубе.


Оглавление

1. Отзыв о курсе

2. Введение

3. Переменные

3.1. Имена переменных
3.2. Типы значений
3.3. Объявления переменных

4. Объекты

4.1. Действия со свойствами объекта
4.2. Вложенные свойства объектов
4.3. Использование переменных как значения свойств
4.4. Глобальные объекты
4.5. Методы объекта

5. JSON

6. Мутации

6.1. Способы избежать мутаций

7. Функции

7.1. Неявный возврат значения
7.2. Параметры функции
7.3. Мутации объектов через функции
7.4. Коллбэк функция
7.5. JSDOC-комментарии

8. Области видимости

9. Ложные значение

10. Операторы

11. Конкатенация строк

12. Обработка ошибок

13. Инструкции

14. Массивы

14.1. Чтение значений массива
14.2. Методы массивов

15. Деструктуризация

16. Условные инструкции

17. Тернарный оператор

18. Циклы

18.1. Цикл for
18.2. Метод массивов forEach
18.3. Циклы while и do while
18.4. Цикл for in для объектов
18.5. Метод forEach() для объектов
18.6. Цикл for of

19. Модули

20. Классы и прототипы

20.1. Статический метод
20.2. Расширение других классов
20.3. Примитивные значения

21. Промисы

22. Асинхронные функции

22.1. Переход с промисов на ASYNC/AWAIT


1. Отзыв о курсе

Я только начинаю изучение JS и курс Богдана показался мне самым понятным из тех что я нашел. Подкупили простые слайды и приятный голос, - поэтому я начал именно с его курса. И не ошибся.

Просмотр курса занял не слишком много времени, но мое понимание JS с полного нуля знатно так прокачалось. Делая змейку по гайду с ютуба я понял 90% написанного кода, даже не пройдя курс целиком.

Видео смотрел на скорости 2х и не было ни одного момента где было бы что-то не понятно в словах, дикция у автора отличная. Поэтому настоятельно рекомендую ускорять видео, если будете проходить этот курс, в оригинале речь показалось очень медленной.

Не понятно

Единственный непонятный раздел в курсе - это коллбэк функции, тут мне пришлось загуглить чтоб разобраться что к чему. Все остальное объясняется очень хорошо и можно обойтись тем, что есть в курсе.

Задания

На момент написания отзыва я прошел только 14 заданий из 80, но все задания проходил используя только информацию с курса, не гугля. Это сложно и иногда, чтоб найти тему задания приходилось проматывать много видео и искать где же Богдан это объяснял. Думаю стоило добавить в заданиях ссылки на разделы курса, в которых тема задания раскрывается.

Курс рекомендую👍

Сайт автора курса: stashchuk.com


2. Введение

Самое важно в JavaScript это Выражение, Функции и Объекты

Практически все сущности в JS это объекты или ведут себя как объекты

Объект — это набор свойств "имя: значение" или "ключ: значение"

Разбор выражения console.log, который выводит в консоль Hello World:

console.log('Hello World')

console       // Это объект у которого есть свойства (имя: значение)
.             // Это точечная запись. Оператор
log()         // Это метод (функция) одного из свойств объекта consol
()            // Это оператор вызова функции
'Hello World' // это значение типа String (Строка). 
              // Может быть как в двойных кавычках, так и в одинарных

Выражения всегда возвращают значения

Примеры выражений:

'abc'                 // Вернет значение 'abc'
10                    // Вернет 10
5 + 2                 // Вернет результат 7. Тут есть оператор +
с = 10                // = это оператор присвоения. Результатом будет 10
'Good ' + 'Morning'   // Вернет 'Good Morning'
a <= b || c !== d     // Вернет true или false. Логические операторы
                      // меньше или равно, 

Выражение с побочными действиями (SideEffects):

a = 15  // Это выражение присваивания, где переменной a присваивается
        // значение 10, также это выражение возвращает значение 15
a++     // увеличивает значение переменной на единицу и вернет значение
myFunction(c, d)  // внутри функции проиходят какие-то действия
Наверх


3. Переменные

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

Переменные дают возможность повторного доступа к значениям

3.1. Имена переменных

Не стоит называть переменные a b c и не стоит сокращать названия. Названия переменных должны быть понятны не только вам, но и другим разработчикам. Должны отражать суть и назначение переменных.

Есть 3 типа имен для переменных:

PascalCase - для типов и классы.

DB_USER - значения известны до запуска приложения и не меняются. Константы.

camelCase - для всех остальных переменных.

3.2. Типы значений

Тип переменной определяется типом значения, которое ей присвоено.

В JavaScript мы можем переопределять тип переменной сколько угодно раз так как JS это динамически типизируемый язык.

Существуют примитивные типы значений, это те значения которые хранятся непосредственно в переменных. К примитивным относятся:

  • number (число) — это любое числовое значение, в том числе и дробные (1, -2, 3, 5.6, 1.15).
  • string (строка) — это любой текст. Можно использовать как двойные, так и одинарные кавычки.
  • boolean (логический) — всего 2 значения true и false.
  • undefined (неопределенный) — только значение undefined. Значение не определено.
  • null (нулевой) — только значение null. Для указания отсутствия значения у переменной.
  • symbol (символ) — используется для создания уникальных значений (В этом курсе про этот тип переменных информации нет).

Помимо примитивного, есть ссылочный тип переменной. Такая переменная хранит в себе только ссылку на значение:

  • object (объект) — это ссылочный тип.

Так как переменная типа объект хранит только ссылку на объект, то изменение этого объекта коснутся всех переменных имеющих туже ссылку:

const objectA = {
  name: 'Alex',
  age: 40
}
const objectB = objectA
// Создаем еще один объект копирующий первый
objectB.name = 'Victor'
// Меняем значение свойства name в ObjectB
console.log(objectA.name)
// Получаем значение Victor, первый объект также изменился

3.3. Объявления переменных

Чтобы создать переменную и присвоить ей значение, можно использовать ключевые слова const, let или var:

const - не позволяет менять значение переменной. Объявление и присваивание обязательно прописывается одной строкой.

const myAge = 40
// объявили переменную myAge и сразу присвоили ей значение 40
myAge = 50
// попытались присвоить переменной myAge новое значение 50
// и получили ошибку!)

Стоит использовать const максимально, где это возможно. Т.к. он решает некоторые проблемы связанные с динамической типизацией языка JS.

let - для всех остальных переменных.

let myAge
// объявили переменную myAge
myAge = 50
// присвоили переменной myAge значение 50
let youHere = true
//объявили переменную youHere и сразу присвоили ей значение true
youHere = false
//присвоили переменной youHere новое значение false

var - устаревший тип и использовать его больше не имеет смысла.

Наверх

4. Объекты

Объект — это набор свойств.

Практически все сущности в JS это объекты или ведут себя как объекты. Например, массив и функция - это объекты, а число и строка ведут себя как объекты, хотя являются примитивными значениями.

Свойство — это пара имя: значение.

Значением также может быть другой объект (вложенный объект):

const objectA = {
height: 550,
weight: 440,
xyz: {
  x: -1,
  y: 2,
  z: 3.5
  },
title: 'cube'

После каждого свойства ставиться запятая, а после последнего свойства в объекте запятую можно опустить.

Порядок свойств в объекте не имеет значения

4.1. Действия со свойствами объекта

С помощью оператора точка . мы получаем доступ к свойствам объектов.

По аналогии с вызовом метода log объекта console используем точку после названия объекта для получения значения свойств объекта aboutMe:

const aboutMe = {
  city: 'Moscow',
  designer: true,
  country: 'Russia'
}
console.log(aboutMe.city)
// Получим 'Moscow'
console.log(aboutMe.designer)
// Получим true

Чтобы присвоить новое значение используем оператор =, а чтобы удалить свойство - ключевое слово delete. Если мы присвоим значение несуществующему свойству, то это свойство создастся автоматически:

const aboutMe = {
  city: 'Moscow',
  designer: true,
  country: 'Russia'
}
aboutMe.city = 'Perm'
// Переопределим значение свойства city
aboutMe.time = 3
// Добавим новое свойство time со значением 3 в объект
delete aboutMe.designer
// Удалим свойство designer из объекта
console.log(aboutMe)
// Получим объект {city: 'Perm', country: 'Russia', time: 3}

Кроме точки, доступ к свойствам объекта можно получить с помощью [ ] (bracket notation).

const myCity = {
  city: 'Kiev'
}
myCity['year'] = 2022
console.log(myCity.year)
// Получим 2022, мы добавили новое свойство year со значением 2022

Синтаксис с [ ] используется когда название свойства является значением той или иной переменной, например:

const myCity = {
  city: 'Kiev',
  year: 2022
}
const countryPropertyName = 'country'
// Создадим новую переменную со значение country
myCity[countryPropertyName] = 'Ukraine'
// Добавили свойство с именем взятым из значения переменной
// countryPropertyName, т.е. country
console.log(myCity)
// Получим объект {city: 'Kiev', year: 2022, country: 'Ukraine'}

В [ ] мы можем указать любое выражение, и результат этого выражения, станет названием свойства.

4.2. Вложенные свойства объектов

Оператор точка также позволяет получить доступ к вложенным объектам. Можно комбинировать с bracket notation:

const myCity = {
  city: 'Kiev',
  date: {
    month: 4,
    day: 22
  }
}
console.log(myCity.date.day)
// Получим 22
console.log(myCity.date['day'])
// Получим также 22

4.3. Использование переменных как значения свойств

Мы можем использовать имена переменных в качестве значений свойств. При создании объекта в него будут скопированы значения этих переменных:

const name = 'Alex'
const age = 40

const userProfile = {
  userName: name,
  userAge: age,
  userCity: 'Pskov'
}
console.log(userProfile)
// Получим объект {userName: 'Alex', userAge: 40, userCity: 'Pskov'}

Для удобства, можно сокращать написание свойства до названия переменной. Но тогда имя свойства будет такое же, как имя переменной:

const name = 'Alex'
const age = 40

const userProfile = {
  name,
  age,
  userCity: 'Pskov'
}
console.log(userProfile)
// Получим объект {name: 'Alex', age: 40, userCity: 'Pskov'}

4.4. Глобальные объекты

Глобальный объект - это встроенный объект с огромным количеством свойств и методов. Для браузеров это window или globalThis, для node.js это global или globalThis.

Мы можем использовать методы глобального объекта. Метод log() один из таких методов:

window.console.log('Hello World!')
// Объект console является одним из свойств глобального объекта window

4.5. Методы объекта

Метод — это свойство объекта, значение которого - функция

Мы можем вызвать метод объекта использую точечную запись . (оператор точка) и () круглые скобки:

myCity.city // Получаем доступ к значению свойства
myCity.helloMyCity() // Вызываем метод объекта

В объекте myCity city является обычным свойством, а helloMyCity() методом:

const myCity = {
  city: 'Pskov',
  helloMyCity: function () {
    console.log('Hello my City')
  }
}
myCity.helloMyCity()

Для удобства, можно опускать ключевое слово function и ставить круглые скобки сразу после названия свойства:

const myCity = {
  city: 'Pskov',
  helloMyCity() { // Сократили слово function
    console.log('Hello my City')
  }
}
myCity.helloMyCity()
Наверх


5. JavaScript Object Notation (JSON)

Формат обмена данными с серверами. Если добавить переносы, то формат записи JSON похож на формат записи JS:

{
  "id": 10,
  "title": "user",
  "status": false
}

Но передается JSON в виде строки (string):

'{"id":10,"title":"user","status":false}'

Чтобы конвертировать данные JSON в объект JS, существует метод parse() объекта JSON, а для конвертации из JS в JSON, есть метод stringify()

JSON.parse() // конвертирует строку JSON в JS
JSON.stringify() // конвертирует объект JS в JSON

После выполнения метода JSON.parse(), мы получим привычный JS набор свойств:

{
  id: 10,
  title: 'user',
  status: false
}

Пробуем конвертировать объект в JSON и обратно:

const normalJavaScriptObject = {
  id: 10,
  title: 'user',
  status: false
}
const jsonString = JSON.stringify(normalJavaScriptObject)
// Создаем новую переменную и присваиваем ей значение JSON
console.log(jsonString)
// Получим строку '{"id":10,"title":"user","status":false}'
const jsonParse = JSON.parse(jsonString)
// Создаем новую переменную и конвертируем строку JSON в объект
console.log(jsonParse)
// Получим объект {id: 10, title: 'user', status: false}
Наверх


6. Мутации

Значения переменных примитивного типа хранятся в самих переменных, поэтому при присвоении одной переменной значения другой переменной происходит копирование этого значения (copy by value):

const a = 10
let b = a
// Создаем переменную b и копируем в нее значение переменной a, равное 10
b = 30
// Меняем значение переменной b на 30
console.log(a)
// Видим что значение переменной a по прежнему 10
console.log(b)
// А значение переменной b теперь 30

Переменные ссылочного типа, содержат ссылку на объект, поэтому при присвоении одной переменной этой ссылки из другой переменной, переменные будут иметь одну и туже ссылку на один и тот же объект в памяти (copy by reference):

const user = {
  name: 'Sofia',
  age: 50
}
user.age = 22
// Присвоим значение 22 свойству age 
user.isAdult = true
// Создадим новое свойство isAdult со значением true
console.log(user.age)
console.log(user.isAdult)
// Убедимся, что значения age и isAdult переменной user равны 22 и true

const userCopy
// Создадим новую переменную и присвоим ей значение переменной user
userCopy.age = 44
userCopy.isAdult = false
// Изменим значения свойств для новой переменной userCopy

console.log(user.age)
console.log(user.isAdult)
// Значения свойств age и isAdult первой переменной user теперь
// равны 44 и false, т.к. мы изменили объект по ссылке
// через переменную userCopy

Мы изменили объект через копию переменной имеющую такую же ссылку на объект. Такое поведение называется мутированием.

6.1. Способы избежать мутаций

Метод Object.assign(). Чтобы избежать мутаций можно использовать метод assign() объекта Object. При создании копии объекта, этот метод скопирует все свойства первого объекта и создаст новый объект:

const user = {
  name: 'Sofia',
  age: 50
}
const userCopy = Object.assign({}, user)

Оператор три точки(...).Также можно воспользоваться оператором три точки (spread), он разделяет объект на свойства. И если мы применим такой оператор при создании нового объекта, то получим полностью новый объект, с новой ссылкой.

const user = {
  name: 'Sofia',
  age: 50
}
const userCopy = {...user}

Но такие методы не подойдут если у копируемого объекта есть вложенные свойства, тоже являющиеся объектами, так как ссылки на них сохранятся.

Методы JSON.parse() и JSON.stringify(). Чтобы избежать мутаций полностью, даже имея вложенные объекты, можно воспользоваться конвертацией объекта в JSON и обратно:

const user = {
  name: 'Sofia',
  age: 50
}
const userCopy = JSON.parse(JSON.stringify(user))
// Конвертируем объект user в JSON, а потом создаем новую переменную
// userCopy и присваеваем ей полученное значение JSON
// конвертируя его обратно в JS
Наверх


7. Функции

Функция - это блок кода, который можно выполнять многократно

Функция - это объект, и как любой объект она имеет свойства. Это можно увидеть применив метод метод console.dir() на функции:

function myFn() {
  console.log('Hello')
}
console.dir(myFn)
// Метод dir() покажет свойства функции как объекта

Функция может быть:

  • Именованной (Объявленная функция). Объявляем функцию сразу указывая ее будущее имя. Будет аналогична анонимной функции присвоенной переменной через ключевое слово let;
  • Анонимной. Анонимными являются функциональные выражения и стрелочные функции;
  • Присвоенная переменной. Если функциональное выражение и стрелочную функцию присвоить переменной, то будет функция присвоенная переменной;
  • Аргументом. Например, при вызове другой функции (callback function);
  • Методом, т.е. значением свойства в объекте.
function myFn() {
  console.log('Hello')
}
// Именованая функция

function () {
  console.log('Hello')
}
// Функциональное выражение (анонимная функция)

const myFn = function () {
  console.log('Hello')
}
// Функциональное выражение присвоеное переменной

const myFn = () => {
  console.log('Hello')
}
// Стрелочная функция присвоеная переменной

const myFn = () => console.log('Hello')
// Если у стрелочной функции всего одно выражение, то {} можно опустить
// Функция будет неявно возвращать значение этого выражения

setTimeout(myFn, 5000)
// Функция как аргумент другой функции

const myObject = {
  name = 'Alex'
  print: function
// Функция как значение одного из свойств объекта (метод)

7.1. Неявный возврат значения

Функция, как и любое выражение всегда возвращает значение. Если в функции явно (командой return) не указать какое именно значение она должна вернуть, то она всегда неявно (без команды return) возвращает undefined:

const myFn = () => {
  5 + 5
}
// Такая функция вернет undefined, т.к. мы не указали что ей возвращать

const myFn = () => {
  return 5 + 5
}
// Такая функция вернет сумму 10, т.к. мы ЯВНО указали
// командой ruturn, что ей нужно вернуть 

const myFn = () => return 5 + 5
// Такая функция также вернет сумму 10, но уже НЕЯВНО, без команды ruturn

7.2. Параметры функции

При объявлении функции опционально можно указать параметры, с которыми функция будет работать внутри себя:

function mySum(a, b) {
// Указываем параметры a и b, это переменные которые действуют 
// только внутри функции в момент вызова функции
  let sum
  // Объявляем переменную sum, которая будет  
  sum = a + b
  return sum
  // Ключевое слово return указывает то, что должна вернуть функция.
}

В момент вызова этой функции в круглых скобках указываются значения параметром функции - аргументы:

function mySum(a, b) {
  let sum
  sum = a + b
  return sum
}
myFn(10, 3)
// Аргументы присваиваются параметрам по порядку
// Число 10 будет присвоено параметру a, а число 3 - параметру b

Можно указать какие параметры функция будет использовать по умолчанию, если не передано другое значение:

function multByFactor(value, multiplier = 1) {
  return value * multiplier
}
// Аргумент multiplier будет равен 1 если не передано другое значение

multByFactor(10, 2)
// Вернет результат 20
multByFactor(5)
// Вернет результат 5, т.к. второй агрумент будет взят по умолчанию, это 1

В примере функция создает объект, добавляет к нему все свойства из объекта post с помощью оператора ... и добавляет новое свойство со значением текущей даты с помощью метода Date():

const newPost = (post, addedAt = Date()) => ({
  ...post,
  addedAt,
})
// Тело функции ({}) дополнительно обернуто круглыми скобками,
// это позволяет неявно вернуть объект, как результат функции

const firstPost = {
  id: 1,
  author: 'Pavel'
}
newPost(firstPost)
// Функция вернет новый объект с тремя свойствами

7.3. Мутации объектов через функции

Аргументом в вызове функции может быть объект и в этом случае параметру функции будет передана ссылка на этот объект и все дальнейшие действия с этим параметром, будут вызывать мутации внешнего объекта по ссылке:

const personOne = {
  name: 'Lara',
  age: 22
}

function increasePersonAge(person) {
  person.age += 1
  return person
}

increasePersonAge(personOne)
// Вызываем функцию с аргументом переменной personOne
console.log(personOne.age)
// Получим 23, т.к. внешний объект мутировал через изменение person

Внутри функции не рекомендуется мутировать внешние объекты.

Функция которая выполняет одну задачу не меняет внешних переменных называется чистой функцией (pure function).

Чтобы избежать мутаций в функции можно использовать метод assign(), три точки или JSON, также как мы использовали их раннее:

const personOne = {
  name: 'Lara',
  age: 22
}

function increasePersonAge(person) {
  const updatedPerson = Object.assign({}, person)
  // Создаем новый объект прямо внутри функции и начинаем работать с ним
  updatedPerson.age += 1
  return updatedPerson
}
const updatedPersonOne = increasePersonAge(personOne)
// Создаем новую переменную, вызываем функцию и
// присваиваем результат функции этой новой переменной

7.4. Коллбэк функция (callback function)

Коллбэк — это функция, которая должна быть выполнена после того, как другая функция завершит работу.

function printFinish() {
  console.log('Finish')
}
console.log('Start')
// Печатаем в консоль Start
setTimeout(printFinish, 5000)
// Выполняем функцию printFinish после выполнения функции setTimeout
// setTimeout это встроенная функция, устанавливает таймер выполнения

7.5. JSDOC-комментарии

Такие комментарии позволяют указать какие типы переменных должны использоваться внутри функции:

/**
*
* @param {number} a
* @param {number} b
* @returns {number} Разница чисел
*/
function myFn(a, b) {
  let c = a - b
  return c
}
Наверх


8. Области видимости

Глобальная область видимости - это наш файл в целом.

Локальная область видимости - это область внутри блока { }, между фигурными скобками.

Может существовать целая цепочка областей видимости и переменные объявленные в локальных областях видимости не будут учитываться в глобальной области видимости.

При этом мы можем обратиться из локальной области видимости к переменной из глобальной области видимости, если в локальной области видимости не создана переменная с таким же именем:

let a = 'New York'
let b = 'New York'
// Объявим две переменные и присвоим им значения New York

function myFn() {
  let b
  // Объявим переменную b внутри функции
  a = 'Las Vegas'
  b = 'Las Vegas'
  // Поменяем значения обеих переменных на Las Vegas
}

myFn()

console.log(a) // Получим 'Las Vegas'
console.log(b) // Получим 'New York'
Наверх


9. Ложные значение

Ложные значения, это те значения, которые дают false при приведении их к логическому типу boolean.

К таким значениям относятся:

false     // Ложь
0         // Ноль
''        // Пустая строка
undefined // Значение неопределено
null      // Значения нет

Привести значение к типу boolean можно с помощью функции Boolean():

const a = 'Alex'
const b = ''

console.log(Boolean(a)) // true
console.log(Boolean(b)) // false
Наверх


10. Операторы

Оператор — это встроенная в JS функция

=   // Оператор присваивания
,   // Позволяет объявить несколько переменных в одной строке
.   // Позволяет обращаться к свойствам объектов
()  // Оператор вызова функции
... // Оператор разделения объекта на свойства. Префиксный

Арифметические операторы

+  // Плюс. Используется для сложения. Конвертации строк в числа
-  // Минус
*  // Умножить
/  // Разделить
++ // Увеличивает значение переменной на 1
+= // Увеличение значения
-= // Уменьшение значения

Операторы сравнения

=== // Равно, оператор сравнения. Сравнивает как тип, так и значение
!== // Не равно
<=  // Меньше
>=  // Больше

Логические операторы

! // Не. Префиксный оператор. Возвращает тип boolean (true или false)
&& // И. Бинарный оператор. Возвращает значение одного из операндов
|| // Или. Бинарный оператор. Возвращает значение одного из операндов

Текстовые операторы

typeof // Проверяет тип значения. Префиксный
instanceof // Проверяет принадлежность объекта к классу. Бинарный
new // Создает новый экземпляр объекта. Префиксный
delete // С помощью него можно удалить свойство в объекте. Префиксный

Унарные и бинарные операторы

У унарных операторов всегда один операнд (аргумент). Они могут быть префиксные, когда оператор стоит перед оператором, и постфиксные, когда оператор идет после операнда.

+myName         // + перед операндом
delete myName.a // delete перед операндом
myName++        // ++ после операнда
myName()        // () после операнда

У бинарных операторов всегда два операнда, они имеют инфиксный формат, когда оператор находится между операндами.

a = 5
a + b
a += 5
a === b
a && b

Существует тернарный оператор у которого 3 операнда, подробнее о нем тут.

Оператор typyof

typeof 10 // 'number'
typeof 10 === 'number' // true
typeof 'Alex' // 'string'

let a
typeof a === 'undefined' // true

Оператор !

Такой оператор чаще всего используется в условных инструкциях.

!10  // false
!0   // true
!!10 // true
!!0  // false

Оператор && и ||

Операторы && и || (и и или) являются операторами короткого замыкания.

/*Выражение 1*/ && /*Выражение 2*/
  1. Если выражение 1 ложно, то выражение 2 игнорируется и возвращается результат выражения 1
  2. Если выражение 1 истина, то смотрим на выражение 2 и т.д.

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

/*Выражение 1*/ || /*Выражение 2*/
  1. Если выражение 1 истина, то выражение 2 игнорируется и возвращается результат выражения 1
  2. Если выражение 1 ложно, то смотрим на выражение 2 и т.д.

Оператор || (или) работает противоположно оператору && (и), ищет первое истинное значение и выдает его результат.

const a = 10
a && console.log('Hello')
// т.к. значение 10 истина, то мы получим Hello в консоли,
// это результат второго выражения
let b
b && console.log('Hello')
// второе выражение не будет выполнено, т.к. значение переменной b ложно

Это можно использовать для выполнения тех или иных действий, в зависимости от результата других выражений.

Оператор ...

!!!

const button = {
  width: 200,
  text: 'Buy'
}
// Создадим объект с двумя свойствами

const redButton = {
// Создадим еще один объект
  ...button,
  // Добавим в него свойства из объекта button
  color: 'Red'
  // Добавим третье свойство color
}
console.table(redButton)
// Метод table() выведет объект в консоль в виде таблицы
Наверх


11. Конкатенация (соединение) строк

Для соединения строк можно использовать оператор + , но нужно следить за наличием пробелов между строк и расставлять их при необходимости. Также с оператором + можно использовать но только строки, но и переменные:

console.log('Hello, ' + 'world')
// Получим Hello, world

const hello = 'Hello'
const world = 'world'
console.log(hello + ', ' + world')
// Также получим Hello, world

Обратные кавычки `` также позволяют внутри себя использовать любые выражения с помочью ${ } , это называется шаблонная строка (template string literals).

const name = 'Denis'
const city = 'Perm'
console.log(`My name is ${name} and I live in ${city}`)

При конкатенации строки с числом, JS превратит число в строку:

const string = 'Numder'
const number = 22
console.log(string + ' ' + number)
// Получим строку 'Number 22'
Наверх


12. Обработка ошибок

После получения ошибки в JavaScript выполнение кода останавливается

С помощью инструкции throw new Error() можно искусственно создать ошибку

const fnWithError = () => {
  throw new Error('Some error')
}
// Создаем функцию которая выдаст ошибку Some error

fnWithError()
// Вызываем функцию вызывающую ошибку и получаем непойманную ошибку

console.log('Continue...')
// Эта функция выполнена не будет

Непойманная ошибка (Uncaught Error) это та ошибка, которая остановит выполнение кода JS.

Решение try/catch. В блоке кода try мы указываем код, в котором может возникнуть ошибка, а в блоге catch с параметром (error), тот код, который будет выполнен в случае появления ошибки в блоке try:

const fnWithError = () => {
  throw new Error('Some error')
}
// Создаем функцию которая выдаст ошибку Some error
try {
  fnWithError()
} catch (error) {
    console.error(error)
    // Метод выведет всю информацию об ошибке
    console.log(error.message)
    // Выведет сообщение зашитое в ошибку, some error
}
console.log('Continue...')
// На этот раз выполнение кода продолжится и мы получим Continue...
Наверх


13. Инструкции

В JavaScript существуют:

  • Выражения. Они всегда возвращают значения.
  • Инструкции. Они выполняют действия (например, объявление переменной).
  • Выражения-инструкции. Выражения может быть инструкцией, но инструкция не может быть выражением (инструкций-выражений НЕТ)

Инструкции следует завершать ; (точкой с запятой), а также каждую инструкцию писать на отдельной строке кода.

После { } блока кода инструкций точка с запятой не требуется.

const a = 5;
// в конце инструкции стоит ;

if (a > b) {
  console.log('Hello');
}
// Блоки кода {} условных инструкций не требуют ;

for (let i = 0; i++; i <5) {
  console.log(i);
}
// Блоки кода {} циклов тоже не требуют ;

Точки с запятой в JavaScript можно опускать

Инструкции нельзя использовать как аргументы в функциях

Если в конце выражения поставить точку с запятой, то мы явно укажем, что это инструкция и JS перестанет считать его выражением, даже если это выражение-инструкция

function myFn(a) {
  console.log(a);
}
let c = 10
//
myFn(c = c + 1)
// Функция выдаст результат выражения, это 11
myFn(c = c + 1;)
// Поставив точку с запятой, мы указали что это инструкция,
// поэтому функция выполнена не будет
Наверх


14. Массивы

Массив — это объект с цифровыми именами свойств: 0, 1, 2, 3 и т.п.

Для создания массива используются квадратные скобки, в которых указываются будущие значения массива по порядку.

const myNumbers = ['one', 'two', 'three']
console.log(myNumbers)
// Получим массив ['one', 'two', 'three']

Также для создания массива можно использовать ключевое слово new для создание нового экземпляра класса Array(), в качестве аргументов указываются будущие значения массива по порядку.

const myNumbers = new Array ('one', 'two', 'three')
console.log(myNumbers)
// Получим массив ['one', 'two', 'three']

Свойства в массиве начинаются с нуля: [0: 'one', 1: 'two', 2: 'three']

14.1. Чтение значений массива

Вместо точечной записи, для обращения к цифровым свойствам массива используют [] квадратные скобки:

const myArray = [1, true, 'Petr']
console.log(myArray)
// Получим [1, true, 'Petr']
console.log(myArray[0])
// Получим 1
console.log(myArray[1])
// Получим true
console.log(myArray.length)
// Получим 3, это кол-во значений в массиве. Свойство length имеет
// любой массив по умолчанию и обновляется оно автоматически

Обновлять значения поля length вручную не рекомендуется

const myNumbers = new Array (1, 2, 3, 4)
// Создадим массив с 4-я значениями
myNumbers[1] = 'new'
console.log(myNumbers)
// Получим [1, 'new', 2, 3, 4] т.к мы присвоили свойству 1 значение 'new'
myNumbers[4] = 'four'
// Добавили новое значение в массив, т.к. свойства 4 не было

14.2. Методы массивов

Метод push() добавляет аргумент как элемент в конец массива:

const myNumbers = ['one', 'two', 'three']
// Создадим массив [0: 'one', 1: 'two', 2: 'three']
myNumbers.push('PUSH')
// Получим массив [0: 'one', 1: 'two', 2: 'three', 3: 'PUSH', ]

Метод pop() удаляет последний элемент массива и возвращает его значение:

const myNumbers = ['one', 'two', 'three']
// Создадим массив [0: 'one', 1: 'two', 2: 'three']
const myPop = myNumbers.pop()
// Получим массив [0: 'one', 1: 'two'] и переменную myPop
// со значением 'three', т.к. метод pop возвращает значение

Метод unshift() добавляет аргумент как элемент в начало массива. Все названия элементов сдвинуться на 1 вперед:

const myNumbers = ['one', 'two', 'three']
// Создадим массив [0: 'one', 1: 'two', 2: 'three']
myNumbers.unshift('UNSHIFT')
// Получим массив [0: 'UNSHIFT', 1: 'one', 2: 'two', 3: 'three']

Метод shift() удаляет первый элемент массива и возвращает его значение:

const myNumbers = ['one', 'two', 'three']
// Создадим массив [0: 'one', 1: 'two', 2: 'three']
const myShift = myNumbers.shift()
// Получим массив [0: 'two', 1: 'three'] и переменную myShift
// со значением 'one', т.к. метод shift возвращает значение

Методы push(), pop(), unshift() и shift() мутируют изначальный массив!

Метод forEach() перебирает все элементы массива и применяет к ним функцию, которую мы прописываем как аргумент метода. Возвращает значение undefined.

const myNumber = [1, 2, 3, 4]
myNumber.forEach(element => console.log(element * 2))
// Метод forEach() переберет все элементы масива, умножит их на 2
// и выведет их в консоль

Метод map() перебирает все элементы массива и применяет к ним функцию также как forEach, но возвращает новый массив.

const myNumber = [1, 2, 3, 4]
const newMyNumber = myNumber.map(element => element * 2)
// Получим новый массив со значениями [2, 4, 6, 8]
Наверх


15. Деструктуризация

Позволяет, используя {} фигурные скобки, создавать переменные на остнове свойств объектов:

const myName = {
  name: 'James',
  surname: 'Crock',
  age: 35,
}

const {name, surname} = myName
// Будет создана переменная name со значением 'James'
// и переменная surname со значением 'Crock'

Похожим образом можно создавать переменные на основе свойств массивов. В фигурных скобках мы указываем названия будущих переменных, а значения для них берутся из массива по порядку:

const fruits = ['apple', 'banana']
const [fruitOne, fruitTwo] = fruits
// Получим переменную fruitOne = 'apple' и fruitTwo = 'banana'

В функциях также можно использовать деструктуризацию для передачи нужных свойств объекта как аргументов

const userProfile = {
  name: 'Pavel',
  commentsQty: 23,
  hasSignedAgreement: false
}
const userInfo = ({name, commentsQty}) => {
// В переданом объекте ищем свойства name и commentsQty
  if (!commentsQty) {
  // Если в объекте нет свойства commentsQty, то мы возвращаем:
    return `User ${name} has no comments`
  }
  return `User ${name} has ${commentsQty} comments`
}
userInfo(userProfile)
// Получим 'User Pavel has 23 comments'
Наверх

16. Условные инструкции

Инструкция if. Блок кода в {} выполняется однократно если условие правдиво, если ложно - игнорируется:

let val = 10

if (val > 5) {
// Если val больше 5, то мы выполняем блок кода в {}
  val += 20
}

console.log(val)
// Получим 30
const person = {
  age: 20
}

if (!person.name) {
// Если у объекта НЕТ свойства name, то мы выполняем блок кода в {}
  console.log('Имя не указано')
}

Инструкция if else. В этой инструкции можно использовать 2 блока кода. Первый блок кода { } выполняется если условие правдиво, а второй { } - если ложно:

const person = {
  age: 20,
  name:'Diana'
}

if (person.name) {
// Если у объекта ЕСТЬ свойство name, то мы выполняем блок кода в {}
  console.log(`Имя: ${person.name}`)
} else {
  // Если свойства name НЕТ, то выполняем блок кода else {}
  console.log('Имя не указано')
}

Можно комбинировать инструкции if else:

if (/*Условие1*/) {
 // Блок кода, выполняемый однократно, если Условие1 правдиво
} else if (/*Условие2*/) {
 // Блок кода, выполняемый однократно, если Условие2 правдиво
} else {
 // Блок кода, выполняемый однократно, если предыдущие условия ложны
}

Применение инструкции if в функциях:

const sumPositiveNumbers = (a, b) => {
  if (typeof a !== 'number' || typeof b !== 'number') {
  // Если a или b не являются числами, то возвращаем
    return 'One of the arguments is not a number'
  }
  if (a <= 0 || b <= 0) {
  // Если a или b меньше или равны нулю, то возвращаем
    return 'Numbers are not positive'
  }
  return a + b
  // Если передыдущие условия ложны, то выполняется сложение
}

Инструкции if и if else выполняются однократно

Инструкция switch. Действие выполняется если выражение в условии равно case (кейс/случай). Если выражение равно нескольким кейсам, то выполнятся оба кейса.

Если в кейсе стоит ключевое слово brake, то после удачного выполнения этого кейса выполнение инструкции switch прекратиться, даже если какие-то кейсы еще подходят под условия:

switch (Выражение) {
  case A:
    // Действие, если Выражение === A
    break
    // brake это инструкция для прикращения инструкции после 
    // удачного выполнения действия в кейсе
  case B:
    // Действие, если Выражение === B
    break
  default:
    // Действие по умолчанию
}
const winterMonth = 2

switch (winterMonth) {
  case 12:
    console.log('December')
    break
  case 1:
    console.log('January')
    break
  case 2:
    console.log('February')
    break
  default:
    console.log('Its not a winter month')
}
Наверх


17. Тернарный оператор

Конструкция с тернарным оператором является выражением

У тернарного оператора 3 операнда, его операнды пишутся между ? и : Конструкция с тернарным оператором является выражением и возвращает результат одного и операндов:

Условие (Условие) // Условием может быть любое выражение, в том числе
                      // и с другими операторами
  ? Операнд Один      // Выражение, выполняется если условие ПРАВДИВО
  : Операнд Два      // Выражение, выполняется если условие ЛОЖНО
const value1 = 11
const value2 = 25
value1 && value2
  ? myFunction(value1, value2)
  // Если value1 и value2 правдивы, то вызываем функцию используя их
  // как аргументы
  : myFunction()
  // Если значения ложны, то вызываем функцию без аргументов
let value = 11
console.log(value >= 0 ? value : -value)
// Если value больше или равно нулю, то возвращаем результат value
// Получим 11

value = -5
const result = value >= 0 ? value : -value
// Присваиваем переменной результат работы тернарного оператора,
// это возможно т.к. это выражение
console.log(result)
// значение result будет -value, а --5 это 5
Наверх


18. Циклы

Циклы — это инструкции, они не возвращают значение, а только совершают действие

18.1. Цикл for

for (Начальная инструкция; Условие; Итерационное действие) {
  // Блок кода, выполняемый на КАЖДОЙ итерации
}

Итерационное действие повторяет выполняется до тех пор, пока условие верно.

for (let i = 0; i < 5; i++) {
  console.log(i)
}

Циклы можно использовать для массивов, хотя это не рекомендуется. Для массивов есть методы forEach, map, reduce

const myNumbers = ['one', 'two', 'three']

for (let i = 0; i < myNumbers.length; i++) {
  console.log(myNumbers[i])
}
// Получим в консоли:
// one
// two
// three

18.2. Метод массивов forEach

Метод forEach вызывается столько раз, сколько элементов в массиве

const myNumbers = ['one', 'two', 'three']

myNumbers.forEach((element, index) => {
  console.log(element, index)
})
// Получим в консоли:
// one 0
// two 1
// three 2

18.3. Циклы while и do while

Цикл while выполняет инструкцию пока условие правдиво (true)

while (Условие) {
  // Блок кода, выполняемый при каждой итерации
}

Мы должны сами контролировать изменение изначального условия внутри инструкции:

let myNumber = 0

while (myNumber < 5) {
  console.log(myNumber)
  myNumber++
  // Меняем переменную, которое влияет на условие
  // позволяя выйти из цикла
}

// Получим в консоли:
//   0
//   1
//   2
//   3
//   4
// Когда myNumber станет равна 5 цикл закончится

Цикл do while, в отличии от цикла while всегда выполняется хотя бы 1 раз, т.к. инструкция выполняется до просмотра условия

do {
  // Блок кода, выполняемый при каждой итерации
} while (Условие)

Мы также должны сами контролировать изменение изначального условия внутри инструкции:

let myNumber = 0

do {
console.log(myNumber)
myNumber++
} while (myNumber < 5)

Если условие изначально ложно (false), то условие НЕ выполнится ни разу

Если условие будет бесконечно правдиво (true), то цикл будет выполнятся бесконечно

18.4. Цикл for in для объектов

Блок инструкций for in выполняется для каждого свойства объекта или элемента массива

for (key in Object) {
  // Действия с каждым свойством объекта
  // Значение свойства - Object[key]
}

Key будет представлять название свойства в объекте, а Object это переменная указывающая на объект. Используя такой цикл можно перебрать все свойства объекта:

const myObject = {
  x: 20,
  y: true,
  z: 'text'
}

for (const key in myObject) {
  console.log(key, myObject[key])
  // key указывает на название свойства
  // myObject[key] указывает на значение свойств
}

// Получим в консоли:
//   x 20
//   y true
//   z text

Точно также цикл for in применяется к массивам:

const myArray = [true, 20, 'text', null]

for (const key in myArray) {
  console.log(key, myArray[key])
}

// Получим в консоли:
//   true
//   20
//   text
//   null

Для массивов рекомендуется применять метод forEach()

Цикл for in повторяется столько раз, сколько свойств в объекте или элементов в массиве

18.5. Метод forEach() для объектов

Можно применить метод массивов forEach() для объектов, предварительно получив все имена свойств (ключи) этого объекта в вид массива с помощью метода keys() глобального объекта Object:

const myObject = {
  x: 20,
  y: true,
  z: 'text'
}

Object.keys(myObject).forEach(key => {
  console.log(key, myObject[key])
})

// Получим в консоли:
//   x 20
//   y true
//   z text

Аналогично, но используя метод values() можно перебирать значения свойств:

const myObject = {
  x: 20,
  y: true,
  z: 'text'
}

Object.values(myObject).forEach(value => {
  console.log(value)
})

// Получим в консоли:
//   20
//   true
//   text

18.6. Цикл for of

for (Element of Iterable) {
  // Действие с определенным элементом
}

Iterable это любое значение по которому можно итерироваться (элементы которого можно перебирать):

const myString = 'Hello'

for (const letter of myString) {
  console.log(letter)
}
// Получим в консоли:
//   H
//   e
//   l
//   l
//   o

Цикл for of также подходит для массивов:

const myArray = [true, 20, 'text', null]

for (const element of myArray) {
  console.log(element)
}
// Получим в консоли:
//   true
//   20
//   text
//   null

Для массивов рекомендуется применять метод forEach()

for of для объектов НЕ подходит, т.к. объекты это НЕ итерируемые элементы

Наверх


19. Модули

Чтобы код был более читаемым используют модули. Один модуль это отдельный файл JS с частью кода и решающий какую-то одну задачу.

Модули должны быть одноцелевыми

В JS есть возможность экспорта и импорта переменных из модуля в модуль, для этого используется ключевые слова export default, export и import

Все экспортируемые переменные рекомендуется всегда писать в самом конце файла, а импортируемые в самом начале

Export default

Если нужно экспортировать из файла file1.js только одну переменную, лучше использовать export default:

// file1.js

const myName = () => {
  console.log('Bob')
}
  
export default myName

В файле file2.js делаем импорт. Для этого используем ключевое слово import и указываем путь к файлу file1.js с помощью from:

// file2.js

import printMyName from './file1.js'
// Укажем переменной новое имя printMyName

printMyName()
// Получим 'Bob'

При импорте переменной, экспортируемой с помощью export default, можно указать ей любое новое имя

Export

При экспорте нескольких переменных из файла file1.js используем ключевое слово export:

const sum = (a, b) => a + b
const mult = (a, b) => a * b

export {sum, mult}

В файле file2.js делаем импорт. Для этого используем ключевое слово import и {} фигурные скобки, в которых перечисляем имена импортируемых переменных:

import {sum, mult} from './file1.js'

console.log(sum(10, 2))
console.log(mult(10, 2))

При импорте нескольких переменных с помощью export, нужно указывать те же самые имена переменных, что были при экспорте.

Но есть возможность переименовать переменную после экспорта:

import {sum as sumRenamed, mult} from './file1.mjs'
// Указываем переменной sum новое имя sumRenamed
Наверх


20. Классы и прототипы

Классы позволяют создавать прототипы для объектов

На основании прототипов создаются экземпляры

Экземпляры наследуют свойства и методы прототипов

Для создания класса используется ключевое слово class. Название нового класса пишется с использованием CamalCase нотации (с большой буквы)

class Comment {
// Пишем Comment с большой буквы т.к. это название класса
  constructor(a) {
  // Метод constructor вызывается только когда создается новый экземпляр
  // Его параметры, это аргументы заданые при создании экземпляра
    this.text = a
    this.votesQty = 0
    // Слово this указывает на будущий экземпляр класса.
    // То есть оно как бы замениться именем экземпляра,
    // который мы будем создавать 
  }
  upvote() {
  // Метод upvote будет доступен всем экземплярам нового класса Comment
  // Это называется наследованием
    this.votesQty += 1
  }
}
const firstComment = new Comment('First Comment')
// Оператор new вызывает функцию constructor с аргументом 'First Comment'
console.log(firstComment)
// Получим объект firstComment {text: 'First Comment', votesQty: 0}
// Ему также доступны наследованные методы constructor и upvote

Наследование происходит по цепочке:

  • Глобальный класс Object содержит методы доступные всем объектам в JavaScript.
  • Созданный нами класс Comment содержит созданные нами методы и наследует все методы глобального класса Object.
  • Экземпляр класса firstComment наследует все методы обоих родительских классов Comment и Object.

Чтобы проверить принадлежность объекта к тому или иному классу можно использовать оператор instanceof:

firstComment instanceof Comment
// Получим true т.к. firstComment экземпляр класса Comment
firstComment instanceof Object
// Получим true т.к. firstComment также является экземпляром
// глобального класса Object

Чтобы проверить принадлежность свойства к тому или иному объекту можно использовать метод hasOwnProperty():

const firstComment = new Comment('First Comment')

firstComment.hasOwnProperty('text')
// Проверяем есть ли у объекта firstComment собственное свойство text
// Получаем true, такое свойство есть
firstComment.hasOwnProperty('votesQty')
// Получаем true, такое свойство тоже есть
firstComment.hasOwnProperty('upvote')
// Получаем false, такого свойства нет, это свойство родительского
// класса Comment
firstComment.hasOwnProperty('hasOwnProperty')
// Получаем false, такого свойства тоже нет, это свойство родительского
// глабального класса Object

20.1 Статический метод

С помощью ключевого слова static можно создавать методы, которые НЕ будут наследоваться экземплярами классов:

class Comment {
  constructor(text) {
    this.text = text
    this.votesQty = 0
  }
  upvote() {
    this.votesQty += 1
  }
  static mergeComments(first, second) {
    return `${second} ${second}`
}

Comment.mergeComments('First Comment', 'Second Comment')
// Метод будет доступен как свойство объекта Comment

20.2 Расширение других классов

С помощью ключевого слова extends можно расширить существующий класс. Создастся новый класс с использованием конструктора из расширяемого класса, в данном случае класса массивов Array:

class NumbersArray extends Array {
  sum() {
    return this.reduce((el, acc) => acc += el, 0)
  }
}

const myArray = new NumbersArray(2, 5, 7)
//
console.log(myArray)
myArray.sum()

Наследование происходит по цепочке:

myArray => NumbersArray => Array => Object

20.3 Примитивные значения

Примитивные значения ведут себя как объекты и наследуют методы родительских классов:

const myName = 'Vladimir'
// Создадим переменную примитивного типа String
console.log(myName)
// Получим 'Vladimir'
console.log(myName.toUpperCase())
// Получим 'VLADIMIR'. Мы использовали метод класса String
const mySecondName = new String('Putin')
// Создадим новый экземпляр класса String
mySecondName
// Получим объект, экземпляр класса String:
// String {'Putin'}
//   0: "P"
//   1: "u"
//   2: "t"
//   3: "i"
//   4: "n"
//   length: 5
Наверх

21. Промисы

Промисы позволяют обрабатывать отложенные во времени события

Промисы нужны чтобы иметь возможность обрабатывать другие запросы пока ждем ответа на предыдущие. Это возможно благодаря асинхронным запросам, ответ на которые JavaScript может получать не сразу, а через какое-то время

Промис может вернуть ошибку, если результат предоставить невозможно

У промиса может быть 3 состояния:

  • Ожидание (pending), когда промис создан;
  • Исполнен (resolve), когда вернул результат;
  • Отклонен (reject), когда вернул ошибку.

Промис - это объект, глобальный класс в JS.

Создаются промисы путем создания экземпляра класса Promise:

const myPromise = new Promise((resolve, reject) => {
  // Выполнение асинхронных действий
  //
  // Внутри этой функции нужно в результате вызывать одну из функций
  // resolve или reject
})

Получение результата промиса

myPromise
  .then(value => {
    // Действия в случае успешного исполнения Промиса
    // Значение value - это значение, переданное в вызове функции
    // resove внутри промиса
  })
  .catch(error => {
    // Действия в случае отклонения Промиса
    // Значение error - это значение, переданное в вызове функции
    // reject внутри промиса
  })

Для тестирования отправки и получения запросов можно использовать сервис

jsonplaceholder.typicode.com

Когда мы отправляем fetch-запрос, он отправляет назад промис.

fetch('https://jsonplaceholder.typicode.com/todos')
//
  .then(response => response.json())
  // Получаем данные при исполнении промиса функцией fetch,
  // но т.к. там нет объектов и масивов JS мы присваиваем эти
  // данные переменной response и применяем к ней метод json()
  // Метод json() также возвращает промис
  .then(json => console.log(json))
  // При возвращении промиса от метода json(), нужен еще один метод
  // then, который выводит его в консоль
  .catch(error => console.error(error))
  // Если один из промисов отклонен, выводим ошибку в консоль

  
fetch('https://jsonplaceholder.typicode.com/todos')
  .then(response => {
    console.log(response)
    return response.json()
  }) 
// Такая функция позволит нам увидеть ответ от сервера в его исходном виде
  .then(json => console.log(json))
  .catch(error => console.error(error))

Чтобы избежать двойного использования .then мы можем создать собственный промис, который будет получать данные, обрабатывать через JSON и вызвращать результат в виде переменной getData. Такой вариант кода можно поместить в модуль и обращаться к нему по мере необходимости:

// Файл module1.js
const getData = (url) => 
  new Promise((resolve, reject) => 
    fetch(url)
      .then(response => response.json())
      .then(json => resolve(json))
      .catch(error => reject(error))
  )

// Файл module2.js
getData('https://jsonplaceholder.typicode.com/todos')
  .then(data => console.log(data))
  .catch(error => console.log(error.massage))
Наверх

22. Асинхронные функции

ASYNC/AWAIT - Специальный синтаксис для упрощения работы с промисами

Асинхронная функция - это функция которая всегда возвращает промис

Для создания асинхронной функции используется слово async:

async function asyncFn() {
  // Стандартный синтаксис функции
}
const asyncFn = async () => {
  // Стрелочная функция
}

!!!

const asyncFn = async () => {
  throw new Error('There was an error!')
}

asyncFn()
  .then(value => console.log(value))
  .catch(error => console.log(error.massage))

С помощью ключевого слова await можно внутри асинхронной функции ожидать результата другого промиса:

const timerPromise = () =>
  new Promise( (resolve, reject) => setTimeout(() => resolve(), 5000) )
  
const asyncFn = async () => {
  console.log('Start')
  const start = performance.now()
  await timerPromise()
  const end = performance.now()
  console.log('Finish', end - start)
}

asyncFn()

22.1. Переход с промисов на ASYNC/AWAIT

Возьмем предыдущий пример использования промисов:

// Первая часть
const getData = (url) => 
  new Promise((resolve, reject) => 
    fetch(url)
      .then(response => response.json())
      .then(json => resolve(json))
      .catch(error => reject(error))
  )

// Вторая часть
getData('https://jsonplaceholder.typicode.com/todos')
  .then(data => console.log(data))
  .catch(error => console.log(error.massage))

Заменим первую часть:

const getData = async (url) => {
  const res = await fetch(url)
  const json = await res.json()
  return json
}

Если на каком этапе асинхронной функции возникнет ошибка промис будет автоматически отклонен

Заменим вторую часть:

const url = 'https://jsonplaceholder.typicode.com/todos')
const data = await getData(url)

Ключевое слово await можно использовать в JS только внутри асинхронных функций, но браузеры позволяют обрабатывать такой код в консоли

Добавим во вторую часть обработчик ошибок с помощью try/catch блоков:

const url = 'https://jsonplaceholder.typicode.com/todos')

try {
  const data = await getData(url)
  console.log(data)
} catch (error) {
  console.log(error.message)
}

ASYNC/AWAIT - это синтаксическая надстройка над промисами

await синтаксис возможен только внутри async функций

async функция всегда возвращает Promise

async функция ожидает результата инструкции await и не выполняет последующие инструкции

Наверх